flatten 20260225

This commit is contained in:
Timothy Prepscius
2026-02-25 12:41:23 -05:00
commit d33ec72fa2
43 changed files with 3296 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
#pragma once
#include "ConcreteVariant.h"
namespace tjp {
namespace core {
namespace variant {
template<typename IO, typename T>
void io_bin(IO &io, ConcreteVariant<T> &v)
{
io.any((Variant &)v);
}
template<typename IO, typename T>
void io_json(IO &io, ConcreteVariant<T> &v)
{
io.any((Variant &)v);
}
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,13 @@
#pragma once
namespace tjp {
namespace core {
namespace variant {
template<typename T>
struct ConcreteVariant;
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,84 @@
#pragma once
#include "ConcreteVariant.h"
#include "Variant.h"
#include <tjp/core/system/Debug.h>
namespace tjp {
namespace core {
namespace variant {
#ifdef DEBUG
#define DEBUG_CONCRETE_VARIANT
#endif
template<typename T>
struct ConcreteVariant : Variant
{
typedef Variant Super;
#ifdef DEBUG_CONCRETE_VARIANT
T *debug_ = nullptr;
void execute_debug ()
{
debug_ = ptr_of(ptr());
}
#else
void execute_debug () {}
#endif
T *operator ->()
{
return &Super::template ref<T>();
}
T &operator *()
{
return Super::template ref<T>();
}
ConcreteVariant()
{
}
template<typename U>
ConcreteVariant(const U &rhs) :
Super(rhs)
{
execute_debug();
}
template<typename U>
ConcreteVariant &operator =(const U &rhs)
{
Super::template set<>(rhs);
execute_debug();
return *this;
}
auto ptr ()
{
return Super::template ptr<T>();
}
template<typename U>
auto ptr ()
{
return Super::template ptr<U>();
}
auto valid ()
{
return Super::template valid<T>();
}
auto &variant ()
{
return (Super &)*this;
}
} ;
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,21 @@
When I redo VType
I want to make IOType to be 3 unsigned integers.
type[0] is modifications, like, is it a vector
type[1] is category, like 0 is intrinsics, 1 is core, 2, network, 3, crown, etc.
type[2] is type, but with respect to category.
VType is u64;
u32
struct {
u8 nothing;
u24 keyType;
u24 valueType;
u8 meta;
} ;

View File

@@ -0,0 +1,115 @@
#pragma once
#include "VType.hpp"
#include "VType_of.hpp"
#include <tjp/core/types/Types.h>
#include <tjp/core/assert/debug_assert.h>
#include <tjp/core/ptr/Ptr.hpp>
#include <tjp/core/type_traits/always_false.hpp>
//#include <tjp/core/type_traits/is_mappish.hpp>
#include <vector>
#include <map>
#include <set>
#include <typeindex>
namespace tjp {
namespace core {
inline
bool operator <(const VType &lhs, const VType &rhs)
{
return lhs.id < rhs.id;
}
inline
bool operator ==(const VType &lhs, const VType &rhs)
{
return lhs.id == rhs.id;
}
inline
bool operator !=(const VType &lhs, const VType &rhs)
{
return lhs.id != rhs.id;
}
template<typename T>
constexpr
VType vtype_of_custom(const StrongPtr<T> *)
{
return vtype_of<T>();
}
template<typename T>
struct vtype_of_dispatch<StrongPtr<T>> {
static
constexpr
auto dispatch(const StrongPtr<T> *t) {
return vtype_of_custom(t);
}
} ;
template<typename T>
constexpr
VType VType::type_of()
{
return vtype_of<T>();
}
template<typename T>
constexpr
bool vtype_cast(const T &t, const VType &to)
{
return false;
}
template<typename T>
constexpr
bool vtype_cast(const StrongPtr<T> &t, const VType &to)
{
return VType::type_cast<T>(to);
}
template<typename T>
constexpr
bool vtype_cast_dispatch(const T *t, const VType &to)
{
return vtype_cast<T>(*t, to);
}
template<typename T>
constexpr
bool vtype_cast_dispatch(const VType &to)
{
return vtype_cast_dispatch<T>(nullptr, to);
}
template<>
constexpr
bool vtype_cast_dispatch<void>(const VType &to)
{
return false;
}
template<typename T>
constexpr
bool vtype_cast(const VType &to)
{
return vtype_cast_dispatch<T>(to);
}
template<typename T>
constexpr
bool VType::type_cast(const VType &to)
{
return vtype_cast_dispatch<T>(to);
}
} // namespace
} // namespace

View File

@@ -0,0 +1,13 @@
#include "VType+IO.h"
#include <iostream>
namespace tjp {
namespace core {
std::ostream &operator <<(std::ostream &o, const VType &v)
{
return o << v.id;
}
} // namespace
} // namespace

View File

@@ -0,0 +1,18 @@
#pragma once
#include "VType.hpp"
#include <iosfwd>
namespace tjp {
namespace core {
template<typename IO>
void io_(IO &io, VType &v)
{
io.any(v.id);
}
std::ostream &operator <<(std::ostream &o, const VType &v);
} // namespace
} // namespace

View File

@@ -0,0 +1,10 @@
#include "VType.hpp"
namespace tjp {
namespace core {
const VType VType::Null = {0};
} // namespace
} // namespace

48
tjp/core/variant/VType.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <tjp/core/types/Types.h>
#include <tjp/core/const_hash/Hash.hpp>
#include <tjp/core/string/StringView.hpp>
namespace tjp::core {
using VType_ID = u32;
using VType_Namespace = u8;
using VType_Class = u32;
constexpr
VType_ID vtype_make(const VType_Namespace, const VType_Class);
constexpr
VType_Namespace vtype_namespace(const VType_ID);
constexpr
VType_Class vtype_class(const VType_ID);
struct VType;
bool operator <(const VType &lhs, const VType &rhs);
bool operator ==(const VType &lhs, const VType &rhs);
bool operator !=(const VType &lhs, const VType &rhs);
template<typename T>
struct vtype_of_dispatch;
template<typename T>
constexpr VType vtype_of(const T &);
template<typename T>
constexpr
VType vtype_of();
template<typename T>
constexpr
VType_ID vtype_id();
template<typename T>
constexpr
bool vtype_cast(const T &t, const VType &to);
using Type_ = VType;
} // namespace

View File

@@ -0,0 +1,47 @@
#pragma once
#include "VType.h"
namespace tjp {
namespace core {
struct VType
{
using Namespace = VType_Namespace;
using Class = VType_Class;
using ID = VType_ID;
constexpr static Namespace primitive = 0;
const static VType Null;
ID id;
static
constexpr
VType make(Namespace, Class);
template<typename T>
static
constexpr
VType type_of();
template<typename T>
static
constexpr
bool type_cast(const VType &to);
constexpr
Namespace getNamespace() const {
return vtype_namespace(id);
}
constexpr
Class getClass() const {
return vtype_class(id);
}
};
} // namespace
} // namespace
#include "VType.inl"

View File

@@ -0,0 +1,39 @@
#include <tjp/core/debug/Debug.h>
namespace tjp {
namespace core {
constexpr VType_ID VType_Bits8 = 0xFF;
constexpr
VType_ID vtype_make(const VType_Namespace n, const VType_Class c)
{
auto r =
(VType_ID(c) << 8) |
(VType_ID(n));
return r;
}
constexpr
VType_Namespace vtype_namespace(const VType_ID t)
{
return VType_Namespace(t & VType_Bits8);
}
constexpr
VType_Class vtype_class(const VType_ID t)
{
return VType_Class((t >> 8));
}
inline
constexpr
VType VType::make(const Namespace n, const Class c)
{
return { .id = vtype_make(n, c) };
}
} // namespace
} // namespace

View File

@@ -0,0 +1,102 @@
#pragma once
#include "VType.hpp"
#include <tjp/core/rtti/TypeName.hpp>
#include <tjp/core/const_hash/Hash.hpp>
#include <tjp/core/type_traits/always_false.hpp>
#include <utility>
namespace tjp::core {
constexpr
auto vtype_id_class_using(const StringView &v)
{
return hash_compile<VType_ID>(v);
}
constexpr
auto vtype_id_using(const VType_Namespace n, const StringView &t)
{
return vtype_make(n, vtype_id_class_using(t));
}
template<typename T>
constexpr
auto vtype_id_from(const VType_Namespace n, const T *t)
{
return vtype_id_using(n, type_name<T>());
}
template<typename T>
constexpr
VType vtype_from(const VType_Namespace n, const T *t)
{
return { vtype_id_from(n, t) };
}
template<typename T>
constexpr
VType vtype_from(const T *t)
{
return vtype_from(0, t);
}
static_assert(hash_compile<VType_ID>("tjp::core::variant::Variant") == 2549889722U);
// ------------
template<typename T>
constexpr
VType vtype_of_custom(const T *t)
{
return VType { vtype_id_from(0, t) };
}
template<typename T>
struct vtype_of_dispatch {
static
constexpr
auto dispatch(const T *t) {
return vtype_of_custom(t);
}
} ;
template<typename T>
constexpr
VType vtype_of()
{
// somehow, if these are not isolated as a constexpr before returning
// the compiler does not discard the temporaries, and full type names
// will exist in the code
constexpr VType result =
vtype_of_dispatch<T>::dispatch(static_cast<T *>(nullptr));
return result;
}
template<>
constexpr
VType vtype_of<void>()
{
// somehow, if these are not isolated as a constexpr before returning
// the compiler does not discard the temporaries, and full type names
// will exist in the code
constexpr VType result =
{ vtype_make(0, vtype_id_class_using("void")) };
return result;
}
template<typename T>
constexpr
VType_ID vtype_id()
{
constexpr auto result = vtype_of<T>().id;
return result;
}
} // namespace

View File

@@ -0,0 +1,22 @@
#pragma once
#include "VType.h"
#include <tjp/core/containers/Vector.h>
namespace tjp {
namespace core {
template<typename T>
struct VVector : Vector<T>
{
using Super = Vector<T>;
VVector() {}
VVector(size_t size) : Super(size) {}
VVector(std::initializer_list<T> l) : Super(l) {}
VVector(Super &&rhs) : Super(std::move(rhs)) {}
VVector(const Super &rhs) : Super(rhs) {}
};
} // namespace
} // namespace

View File

@@ -0,0 +1,28 @@
#pragma once
#include "Variant+IO.hpp"
namespace tjp {
namespace core {
namespace variant {
template<typename D>
bool dispatch_type_builtin(int, D &d);
template<typename T, typename D, typename R>
struct dispatch_variant;
template<typename T, typename D, typename R>
struct dispatch_object;
template<typename T, typename D, typename R>
struct dispatch_array;
template<typename T, typename D, typename R>
struct dispatch_primitive;
} // namespace
} // namespace
} // namespace
#include "Variant+Dispatch.inl"

View File

@@ -0,0 +1,99 @@
#pragma once
#include "VType.hpp"
#include "VariantSerialization.h"
namespace tjp {
namespace core {
namespace variant {
template<typename D>
bool dispatch_type_builtin_value(VType type, D &d)
{
switch(type.id) {
case vtype_id<bool>(): d.template visit<bool>(); return true;
case vtype_id<u8>(): d.template visit<u8>(); return true;
case vtype_id<s8>(): d.template visit<s8>(); return true;
case vtype_id<u16>(): d.template visit<u16>(); return true;
case vtype_id<s16>(): d.template visit<s16>(); return true;
case vtype_id<u32>(): d.template visit<u32>(); return true;
case vtype_id<s32>(): d.template visit<s32>(); return true;
case vtype_id<u64>(): d.template visit<u64>(); return true;
case vtype_id<s64>(): d.template visit<s64>(); return true;
case vtype_id<r32>(): d.template visit<r32>(); return true;
case vtype_id<r64>(): d.template visit<r64>(); return true;
case vtype_id<std::string>(): d.template visit<std::string>(); return true;
case vtype_id<VariantSerialization>(): d.template visit<VariantSerialization>(); return true;
}
return false;
}
template<typename D, typename Type, typename T>
void dispatch_primitive_if(D &d, const Type &type, T &t)
{
d.dispatch_primitive(type, t);
}
template<typename T, typename D, typename R>
struct dispatch_primitive
{
typedef dispatch_primitive<T,D,R> Self;
T &variant;
D &d;
R r;
struct Value
{
Self &self;
template<typename V>
void visit()
{
self.r.template visit<V>(self.variant);
}
} ;
dispatch_primitive(T &variant_, D &d_, R &&r_) :
variant(variant_),
d(d_),
r(std::move(r_))
{
infer();
}
void infer ()
{
Value v{*this};
dispatch_primitive_if(d, variant.type, v);
}
} ;
template<typename T, typename D, typename R>
struct dispatch_variant
{
typedef dispatch_variant<T,D,R> Self;
T &variant;
D &d;
dispatch_variant(T &variant_, D &d_, R &&r) :
d(d_),
variant(variant_)
{
infer(std::move(r));
}
void infer (R &&r)
{
if (variant.type == T::Type::Null)
r.visit_void(variant);
else
dispatch_primitive<T,D,R>(variant, d, std::move(r));
}
} ;
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,27 @@
template<typename T>
Functions generateVariantFunctions ();
template<typename T>
Functions generateVariantFunctions ()
{
using Type = Type_;
return Functions {
.construct = variant_construct<T>,
.copy = variant_copy<T>,
.destruct = variant_destruct<T>,
.cast = Type::type_cast<T>
};
}
Functions generateVariantFunctionsVoid ();
template<>
inline
Functions generateVariantFunctions<void> ()
{
return generateVariantFunctionsVoid();
}

View File

@@ -0,0 +1,25 @@
#include "Variant+Functional.hpp"
namespace tjp {
namespace core {
namespace variant {
Variant::Type Variant::voidType = Variant::Type::template type_of<void>();
Functions generateVariantFunctionsVoid ()
{
using Type = Type_;
return Functions {
[](Value &) {},
[](Value &, const Value &) {},
[](Value &) {},
[](const Type &) { return false; }
};
}
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,605 @@
#pragma once
#include "Variant.h"
#include "VType.hpp"
#include "VType+Functional.hpp"
#include "VariantSerialization.h"
#include <tjp/core/types/Types.h>
#include <tjp/core/debug/Debug.h>
#include <tjp/core/assert/debug_assert.h>
#include <tjp/core/exception/Exception.hpp>
#include <tjp/core/ptr/Ptr.hpp>
#include <tjp/core/allocator/SFINAE.hpp>
#include <tjp/core/string/StringView.h>
#include <cstring>
#include <functional>
namespace tjp {
namespace core {
namespace variant {
#ifdef DEBUG
#define DEBUG_VARIANT
#else
// #define DEBUG_VARIANT
#endif
// types
struct Value_ {
char memory [sizeof(core::ptr::StrongPtr<int>)];
} __attribute__ ((aligned (8)));
typedef Value_ Value;
inline
Value emptyValue () {
Value v;
memset(&v, 0, sizeof(v));
return v;
}
template<typename T> struct is_intrinsic : std::false_type {};
template<> struct is_intrinsic<u8> : std::true_type {};
template<> struct is_intrinsic<s8> : std::true_type {};
template<> struct is_intrinsic<char> : std::true_type {};
template<> struct is_intrinsic<u16> : std::true_type {};
template<> struct is_intrinsic<s16> : std::true_type {};
template<> struct is_intrinsic<u32> : std::true_type {};
template<> struct is_intrinsic<s32> : std::true_type {};
template<> struct is_intrinsic<u64> : std::true_type {};
template<> struct is_intrinsic<s64> : std::true_type {};
template<> struct is_intrinsic<r32> : std::true_type {};
template<> struct is_intrinsic<r64> : std::true_type {};
template<typename T> struct is_intrinsic<StrongPtr<T>> : std::true_type {};
template<typename T> struct is_variant : std::false_type {};
template<> struct is_variant<Variant> : std::true_type {};
// ------ SFINAE
template<typename T>
struct variant_type_ptr {
template<typename U, typename std::enable_if<is_intrinsic<U>::value, U>::type* = nullptr>
static T *type_();
template<typename U, typename std::enable_if<!is_intrinsic<U>::value, U>::type* = nullptr>
static StrongPtr<T> type_();
typedef decltype(type_<T>()) type;
} ;
// ------ SFINAE
template<typename T>
using has_variant_allocator = allocators::has_strong_allocator<T>;
// -----
// variant
template<typename T>
T &deref (const Value &value) {
static_assert(sizeof(T) <= sizeof(Value));
auto *vp = &value;
auto *pp = const_cast<T *>(reinterpret_cast<const T *>(vp));
return *pp;
} ;
struct Functions {
using Type = Type_;
std::function<void(Value &)> construct;
std::function<void(Value &, const Value &)> copy;
std::function<void(Value &)> destruct;
std::function<bool(const Type &)> cast;
};
DECLARE_EXCEPTION(CastException);
template<typename T>
void variant_construct(Value &lhs) {
new(&deref<T>(lhs)) T();
}
template<typename T>
void variant_copy(Value &lhs, const Value &rhs) {
deref<T>(lhs) = deref<T>(rhs);
}
template<typename T>
void variant_destruct(Value &lhs) {
deref<T>(lhs).~T();
};
#include "Variant+Functional+Generate.hpp"
//#include "Variant+VariantFunctionalUsingSingleFunctionsObject.hpp"
#include "Variant+VariantFunctionalUsingSingleFunctionsObjectPointer.hpp"
struct DefaultAllocator
{
template<typename T, typename ... Args>
auto strong(Args && ...args)
{
return core::strong<T>(std::forward<Args>(args)...);
}
} ;
#ifdef DEBUG_VARIANT
#define CORE_VARIANT_INCLUDES_DEBUG_TYPE
#endif
struct Variant
{
using Type = Type_;
using Allocator_ = DefaultAllocator;
using Functional = VariantFunctional;
using Self = Variant;
using value_type = Value;
Value value = emptyValue();
mutable Functional functional;
static Type voidType;
Type type = voidType;
[[no_unique_address]]
Allocator_ defaultAllocator;
Variant()
{
auto &functions = functional.functionsFor(type);
functions.construct(value);
}
Variant(const Variant &rhs)
{
set(rhs);
}
template<typename U>
Variant(const Variant &rhs, U &allocator)
{
set(rhs, allocator);
}
template<
typename T,
typename std::enable_if<!is_variant<T>::value, T>::type* = nullptr
>
Variant(const T &t)
{
auto &functions = functional.functionsFor(type);
functions.construct(value);
set(t);
}
template<
typename T,
typename U,
typename std::enable_if<!is_variant<T>::value, T>::type* = nullptr
>
Variant(const T &t, U &allocator)
{
auto &functions = functional.functionsFor(type);
functions.construct(value);
set(t, allocator);
}
~Variant()
{
auto &functions = functional.functionsFor(type);
functions.destruct(value);
}
bool valid () const
{
return type != Type::template type_of<void>();
}
template<typename T>
bool valid () const
{
return is_<T>() && (bool)ptr<T>();
}
void clear ()
{
inferTypeName<void>();
if (type == voidType)
return;
auto &f0 = functional.functionsFor(type);
f0.destruct(value);
type = voidType;
auto &f1 = functional.ensureFunctions<void>();
f1.construct(value);
}
template<typename T>
T &ensure_()
{
inferTypeName<T>();
if (!(type == Type::template type_of<void>()))
{
if (!is_<T>())
{
debug_assert(false);
throw CastException{};
}
return deref<T>(value);
}
type = Type::template type_of<T>();
auto &functions = functional.template ensureFunctions<T>();
functions.construct(value);
return deref<T>(value);
}
template<typename T, typename P>
T &ensure_()
{
auto &r = ensure_<T>();
inferTypeName<P>();
return r;
}
template<typename T>
bool is_() const
{
auto otherType = Type::template type_of<T>();
if (!(type == otherType))
{
auto &functions = functional.functionsFor(type);
if (!functions.cast(otherType))
return false;
}
return true;
}
bool is_serialization () const
{
return type == Type::template type_of<VariantSerialization>();
}
template<typename T>
T &ref_() const
{
if (!is_<T>())
{
throw CastException{};
}
return deref<T>(value);
}
#ifdef CORE_VARIANT_INCLUDES_DEBUG_TYPE
// debug
mutable StringView typeName;
template<typename T>
void inferTypeName()
{
typeName = type_unique_label_stringview<T>();
}
template<typename T>
void copyTypeName(const T &rhs)
{
typeName = rhs.typeName;
}
StringView getTypeName() {
return typeName;
}
#else
template<typename T>
void inferTypeName () {}
template<typename T>
void copyTypeName(const T &rhs) {}
StringView getTypeName() {
return {};
}
#endif
// copy
template<
typename T,
typename std::enable_if<is_variant<T>::value, T>::type* = nullptr
>
void set(const T &rhs)
{
copyTypeName(rhs);
auto &functions = functional.functionsFor(type);
functions.destruct(value);
type = rhs.type;
auto &rhsFunctions = functional.ensureFunctions(type, rhs.functional.functionsFor(rhs.type));
rhsFunctions.construct(value);
rhsFunctions.copy(value, rhs.value);
}
template<
typename T,
typename U,
typename std::enable_if<is_variant<T>::value, T>::type* = nullptr
>
void set(const T &rhs, U &)
{
set(rhs);
}
// intrinsic
template<
typename T,
typename std::enable_if<is_intrinsic<T>::value, T>::type* = nullptr
>
T *ptr()
{
if (!is_<T>())
return nullptr;
return &ref<T>();
}
template<
typename T,
typename std::enable_if<is_intrinsic<T>::value, T>::type* = nullptr
>
T *ptr() const
{
if (!is_<T>())
return nullptr;
return &ref<T>();
}
template<
typename T,
typename std::enable_if<is_intrinsic<T>::value, T>::type* = nullptr
>
void set(const T &rhs)
{
ensure_<T>() = rhs;
}
template<
typename T,
typename U,
typename std::enable_if<is_intrinsic<T>::value, T>::type* = nullptr
>
void set(const T &rhs, U &allocator)
{
ensure_<T>() = rhs;
}
template<
typename T,
typename std::enable_if<is_intrinsic<T>::value, T>::type* = nullptr
>
T &ref()
{
return ref_<T>();
}
template<
typename T,
typename std::enable_if<is_intrinsic<T>::value, T>::type* = nullptr
>
T &ref() const
{
return ref_<T>();
}
template<
typename T,
typename U,
typename std::enable_if<is_intrinsic<T>::value, T>::type* = nullptr
>
T &load(U &)
{
return ensure_<T>();
}
template<
typename T,
typename std::enable_if<is_intrinsic<T>::value, T>::type* = nullptr
>
T &load()
{
return load<T>(defaultAllocator);
}
// pointer
template<typename TP, typename std::enable_if<std::is_pointer<TP>::value, TP>::type* = nullptr>
void set(const TP rhs)
{
typedef typename std::remove_pointer<TP>::type T;
typedef StrongPtr<T> P;
ensure_<P, T>() = rhs;
}
// complex
template<
typename T,
typename std::enable_if<!is_variant<T>::value, T>::type* = nullptr,
typename std::enable_if<!std::is_pointer<T>::value, T>::type* = nullptr,
typename std::enable_if<!is_intrinsic<T>::value, T>::type* = nullptr
>
StrongPtr<T> ptr()
{
typedef StrongPtr<T> T_;
if (!is_<T_>())
return StrongPtr<T>();
return deref<T_>(value);
}
template<
typename T,
typename std::enable_if<!is_variant<T>::value, T>::type* = nullptr,
typename std::enable_if<!std::is_pointer<T>::value, T>::type* = nullptr,
typename std::enable_if<!is_intrinsic<T>::value, T>::type* = nullptr
>
const StrongPtr<T> ptr() const
{
typedef StrongPtr<T> T_;
if (!is_<T_>())
return StrongPtr<T>();
return deref<T_>(value);
}
template<
typename T,
typename U,
typename std::enable_if<!is_variant<T>::value, T>::type* = nullptr,
typename std::enable_if<!std::is_pointer<T>::value, T>::type* = nullptr,
typename std::enable_if<!is_intrinsic<T>::value, T>::type* = nullptr
>
void set(const T &rhs, U &allocator)
{
typedef StrongPtr<T> T_;
auto &p = ensure_<T_, T>();
if (!p)
p = allocator.template strong<T>(rhs);
else
*p = rhs;
}
template<
typename T,
typename std::enable_if<!is_variant<T>::value, T>::type* = nullptr,
typename std::enable_if<!std::is_pointer<T>::value, T>::type* = nullptr,
typename std::enable_if<!is_intrinsic<T>::value, T>::type* = nullptr
>
void set(const T &rhs)
{
set(rhs, defaultAllocator);
}
template<
typename T,
typename std::enable_if<!is_variant<T>::value, T>::type* = nullptr,
typename std::enable_if<!std::is_pointer<T>::value, T>::type* = nullptr,
typename std::enable_if<!is_intrinsic<T>::value, T>::type* = nullptr
>
T &ref()
{
typedef StrongPtr<T> T_;
return *ref_<T_>();
}
template<
typename T,
typename std::enable_if<!is_variant<T>::value, T>::type* = nullptr,
typename std::enable_if<!std::is_pointer<T>::value, T>::type* = nullptr,
typename std::enable_if<!is_intrinsic<T>::value, T>::type* = nullptr
>
T &ref() const
{
typedef StrongPtr<T> T_;
return *ref_<T_>();
}
template <
typename T,
typename U,
typename std::enable_if<!std::is_void<T>::value, T>::type* = nullptr,
typename std::enable_if<!is_variant<T>::value, T>::type* = nullptr,
typename std::enable_if<!std::is_pointer<T>::value, T>::type* = nullptr,
typename std::enable_if<!is_intrinsic<T>::value, T>::type* = nullptr
>
T &load(U &allocator)
{
typedef StrongPtr<T> T_;
auto &ptr = ensure_<T_, T>();
if (ptr == nullptr)
ptr = allocator.template strong<T>();
return *ptr;
}
template<
typename T,
typename std::enable_if<!std::is_void<T>::value, T>::type* = nullptr,
typename std::enable_if<!is_variant<T>::value, T>::type* = nullptr,
typename std::enable_if<!std::is_pointer<T>::value, T>::type* = nullptr,
typename std::enable_if<!is_intrinsic<T>::value, T>::type* = nullptr
>
T &load()
{
return load<T>(defaultAllocator);
}
template <
typename T,
typename U,
typename std::enable_if<std::is_void<T>::value, T>::type* = nullptr
>
void load(U &allocator)
{
clear();
}
template<
typename T,
typename std::enable_if<std::is_void<T>::value, T>::type* = nullptr
>
void load()
{
return load<T>(defaultAllocator);
}
// -- operator =
template<typename T>
Variant &operator=(const T &rhs)
{
set(rhs);
return *this;
}
// even though this should be covered by the operator = above,
// somehow the compiler misses this occasionally and must be explicity specified
Variant &operator=(const Variant &rhs)
{
set(rhs);
return *this;
}
} ;
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,137 @@
#pragma once
#include "VariantIO.hpp"
#include "Variant.hpp"
#include "VariantSerialization.hpp"
#include "VariantSerialization+IO.hpp"
#include "VType.hpp"
#include "VType+IO.h"
#include <tjp/core/algorithm/remove_const_of_var.hpp>
namespace tjp {
namespace core {
namespace variant {
template<typename IO, typename T>
struct io_bin_variant {
IO &io;
template<typename V>
void visit(T &t)
{
auto &v = t.template as<V>(io);
io.any(v);
}
void visit_void(T &v) {}
} ;
template<typename IO>
void io_(IO &io, Variant &v)
{
VariantIO vio{ v.type, v };
io.object("type", vio.type, "value", io.custom(vio));
}
// -------------------------------------
#define IO_ENABLE_VARIANT_SERIALIZATION
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// --------------------------------------------------------------------
#ifdef IO_ENABLE_VARIANT_SERIALIZATION
// ----------------------
template<typename IO>
void deserialize_(IO &io, Variant &variant, const StrongPtr<VariantSerialization> &serialization)
{
auto io_ = io.partial(serialization->block);
variant = Variant();
io_->any(variant);
}
template<typename IO>
bool io_variant_deserialization_available(IO &io, Type_ type)
{
return io.variant_deserialization_available(type);
}
template<typename IO>
void deserialize_io(IO &io, Variant &variant)
{
using Type = Type_;
if (variant.type == Type::template type_of<VariantSerialization>())
{
auto serialization = variant.template ptr<VariantSerialization>();
if (io_variant_deserialization_available(io, serialization->type))
{
deserialize_(io, variant, serialization);
}
}
}
// ----------------------
template<typename IO>
void io_w_serialization(IO &io, const Variant &v)
{
VariantSerialization s(v, io);
Variant v_(s);
VariantIO vio{ v_.type, v_ };
io.object("type", vio.type, "value", io.custom(vio));
}
template<typename IO, typename T>
bool io_use_variant_serialization_for(IO &io, const T &t)
{
return io.use_variant_serialization_for(t);
}
template<typename IO>
void io_w(IO &io, const Variant &v)
{
typedef Variant V;
if (io_use_variant_serialization_for(io, v))
{
return io_w_serialization(io, v);
}
io_(io, const_cast<V&>(v));
}
template<typename IO>
void io_r(IO &io, Variant &v)
{
VariantIO vio{ v.type, v };
io.object("type", vio.type, "value", io.custom(vio));
deserialize_io(io, v);
}
// --------------------------------------------------------------------
// --------------------------------------------------------------------
#else
// --------------------------------------------------------------------
// --------------------------------------------------------------------
template<typename IO>
void io_bin(IO &io, Variant &v) { return io_(io, v); }
template<typename IO>
void io_json(IO &io, Variant &v) { return io_(io, v); }
#endif
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// --------------------------------------------------------------------
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,77 @@
// VariantFunctionalUsingMap implements the functional aspect of a variant,
// using both a Map to look up the functions object.
//
// I did this, because at the time of writing it was still unclear exactly what types would be needed
// and when they would be needed, and then I also didn't know the overhead of constructing the
// Function<> objects.
template<typename Type_>
struct VariantFunctionalUsingMap
{
typedef Type_ Type;
struct FunctionsMap {
SharedMutex mutex;
std::map<Type, Functions<Type>> values;
} ;
static FunctionsMap *functionsMap_;
static std::once_flag initializeFunctionMapFlag;
static void initializeFunctionMap ()
{
functionsMap_ = new FunctionsMap();
(*functionsMap_).values[Type::template type_of<void>()] =
generateVariantFunctionsVoid<Type>();
}
static FunctionsMap &functionsMap ()
{
std::call_once(initializeFunctionMapFlag, initializeFunctionMap);
return *functionsMap_;
}
template<typename T>
static Functions<Type> &ensureFunctions()
{
Type type = Type::template type_of<T>();
auto &functions = functionsMap();
auto r = shared_lock_of(functions.mutex);
auto i = functions.values.find(type);
if (i == functions.values.end())
{
r.unlock();
auto w = lock_of(functions.mutex);
auto variantFunctions = generateVariantFunctions<Type, T>();
auto emplaced = functions.values.insert({type, variantFunctions});
i = emplaced.first;
}
return i->second;
}
static const Functions<Type> &ensureFunctions(Type type_, const Functions<Type> &functions_)
{
return functions_;
}
static Functions<Type> &functionsFor(Type type)
{
auto &functions = functionsMap();
auto r = shared_lock_of(functions.mutex);
auto i = functions.values.find(type);
debug_assert(i != functions.values.end());
return i->second;
}
} ;
template<typename Type_>
typename VariantFunctionalUsingMap<Type_>::FunctionsMap *VariantFunctionalUsingMap<Type_>::functionsMap_ = nullptr;
template<typename Type_>
std::once_flag VariantFunctionalUsingMap<Type_>::initializeFunctionMapFlag;

View File

@@ -0,0 +1,122 @@
// VariantFunctionalUsingMapAndVectorCache implements the functional aspect of a variant,
// using both a Map lookup for complex types and a Vector cache for indexing into commonly used
// types. This is significantly faster.
template<typename Type_>
struct VariantFunctionalUsingMapAndVectorCache
{
typedef Type_ Type;
struct CacheItem {
bool cached = false;
Functions<Type> functions;
void set(Functions<Type> &&functions_)
{
functions = std::move(functions_);
cached = true;
}
} ;
struct FunctionsMap
{
typedef CacheItem Cache;
static constexpr size_t maxCache = 256;
Cache cache[maxCache];
SharedMutex mutex;
std::map<Type, Functions<Type>> values;
} ;
static FunctionsMap *functionsMap_;
static std::once_flag initializeFunctionMapFlag;
static void initializeFunctionMap ()
{
functionsMap_ = new FunctionsMap();
auto type = Type::template type_of<void>();
auto cacheIndex = type.id;
debug_assert(cacheIndex < functionsMap_->maxCache);
auto &item = functionsMap_->cache[cacheIndex];
item.set(generateVariantFunctionsVoid<Type>());
}
static FunctionsMap &functionsMap ()
{
std::call_once(initializeFunctionMapFlag, initializeFunctionMap);
return *functionsMap_;
}
template<typename T>
static Functions<Type> &ensureFunctions()
{
auto &functions = functionsMap();
Type type = Type::template type_of<T>();
auto cacheIndex = type.id;
if (cacheIndex < functions.maxCache)
{
auto &item = functions.cache[cacheIndex];
if (item.cached)
return item.functions;
auto w = lock_of(functions.mutex);
if (!item.cached)
item.set(generateVariantFunctions<Type, T>());
return item.functions;
}
auto r = shared_lock_of(functions.mutex);
auto i = functions.values.find(type);
if (i == functions.values.end())
{
r.unlock();
auto w = lock_of(functions.mutex);
auto variantFunctions = generateVariantFunctions<Type, T>();
auto emplaced = functions.values.insert({type, variantFunctions});
i = emplaced.first;
}
return i->second;
}
static const Functions<Type> &ensureFunctions(Type type_, const Functions<Type> &functions_)
{
return functions_;
}
static Functions<Type> &functionsFor(Type type)
{
auto &functions = functionsMap();
auto cacheIndex = type.id;
if (cacheIndex < functions.maxCache)
{
auto &item = functions.cache[cacheIndex];
debug_assert(item.cached);
return item.functions;
}
auto r = shared_lock_of(functions.mutex);
auto i = functions.values.find(type);
debug_assert(i != functions.values.end());
return i->second;
}
} ;
template<typename Type_>
typename VariantFunctionalUsingMapAndVectorCache<Type_>::FunctionsMap *VariantFunctionalUsingMapAndVectorCache<Type_>::functionsMap_ = nullptr;
template<typename Type_>
std::once_flag VariantFunctionalUsingMapAndVectorCache<Type_>::initializeFunctionMapFlag;

View File

@@ -0,0 +1,39 @@
// VariantFunctionalUsingSingleFunctionsObject implements the functional aspect of a variant
// without using any lookups or caching. If the Function<> objects don't use the heap, and
// are nearly instantaneous to construct, this is the best option.
//
// And it turns out that Function<> objects don't use the heap if they capture < threshold bytes
// and are in fact nearly instantaneous to construct
struct VariantFunctionalUsingSingleFunctionsObject
{
typedef Type_ Type;
Type type = Type::template type_of<void>();
Functions functions = generateVariantFunctions<void>();
template<typename T>
const Functions &ensureFunctions()
{
type = Type::template type_of<T>();
functions = generateVariantFunctions<T>();
return functions;
}
const Functions &ensureFunctions(Type type_, const Functions &functions_)
{
type = type_;
functions = functions_;
return functions;
}
const Functions &functionsFor(Type type) const
{
debug_assert(type == type);
return functions;
}
} ;
using VariantFunctional = VariantFunctionalUsingSingleFunctionsObject;

View File

@@ -0,0 +1,67 @@
// VariantFunctionalUsingSingleFunctionsObject implements the functional aspect of a variant
// without using any lookups or caching. If the Function<> objects don't use the heap, and
// are nearly instantaneous to construct, this is the best option.
//
// And it turns out that Function<> objects don't use the heap if they capture < threshold bytes
// and are in fact nearly instantaneous to construct
template<typename T>
struct SingleFunctionsObject {
static const Functions functions_;
static const Functions *functions() {
return &functions_;
}
} ;
template<typename T>
const Functions SingleFunctionsObject<T>::functions_ = generateVariantFunctions<T>();
#ifdef DEBUG_VARIANT
#define VARIANT_FUNCTIONS_INCLUDE_TYPE
#endif
struct VariantFunctionalUsingSingleFunctionsObjectPointer
{
typedef Type_ Type;
#ifdef VARIANT_FUNCTIONS_INCLUDE_TYPE
Type type = Type::template type_of<void>();
#endif
const Functions *functions = SingleFunctionsObject<void>::functions();
void setType(Type type_)
{
#ifdef VARIANT_FUNCTIONS_INCLUDE_TYPE
type = type_;
#endif
}
template<typename T>
const Functions &ensureFunctions()
{
setType(Type::template type_of<T>());
functions = SingleFunctionsObject<T>::functions();
return *functions;
}
const Functions &ensureFunctions(Type type_, const Functions &functions_)
{
setType(type_);
functions = &functions_;
return *functions;
}
const Functions &functionsFor(Type type) const
{
#ifdef VARIANT_FUNCTIONS_INCLUDE_TYPE
debug_assert(type == type);
#endif
return *functions;
}
} ;
using VariantFunctional = VariantFunctionalUsingSingleFunctionsObjectPointer;

View File

@@ -0,0 +1,23 @@
#pragma once
#include "VType.h"
namespace tjp {
namespace core {
namespace variant {
struct DefaultAllocator;
struct Variant;
struct VariantSerialization;
struct Functions;
struct CastException;
template<typename T>
Functions generateVariantFunctions ();
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,12 @@
#pragma once
#include "Variant+Functional.hpp"
namespace tjp {
namespace core {
namespace variant {
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,13 @@
#pragma once
#include "Variant.h"
namespace tjp {
namespace core {
namespace variant {
struct VariantIO;
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,41 @@
#pragma once
#include "VariantIO.h"
#include "Variant.hpp"
namespace tjp {
namespace core {
namespace variant {
// -------------------------------------
struct VariantIO {
typedef Type_ Type;
Type type;
Variant &variant;
template<
typename T,
typename IO,
typename std::enable_if<has_variant_allocator<IO>::value, IO>::type* = nullptr
>
auto &as(IO &io)
{
return variant.load<T>(io.allocator);
}
template<
typename T,
typename IO,
typename std::enable_if<!has_variant_allocator<IO>::value, IO>::type* = nullptr
>
auto &as(IO &io)
{
return variant.load<T>();
}
} ;
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,24 @@
#pragma once
namespace tjp {
namespace core {
namespace variant {
// -------------------------------------
template<typename IO>
void io_(IO &io, VariantSerialization &v)
{
io.object("type", v.type, "block", v.block);
}
template<typename IO>
void io_json(IO &io, VariantSerialization &v) { return io_(io, v); }
template<typename IO>
void io_bin(IO &io, VariantSerialization &v) { return io_(io, v); }
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,14 @@
#pragma once
#include "VType.hpp"
namespace tjp {
namespace core {
namespace variant {
struct VariantSerialization;
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,36 @@
#pragma once
#include "VariantIO.hpp"
namespace tjp {
namespace core {
namespace variant {
struct VariantSerialization
{
typedef std::vector<char> Block;
Type_ type;
Block block;
VariantSerialization() {}
template<typename IO>
VariantSerialization(const Variant &v, IO &io)
{
auto &v_ = const_cast<Variant &>(v);
auto io_ = io.partial();
VariantIO vio{ v_.type, v_ };
io_.object("type", v.type, "value", io_.custom(vio));
type = v.type;
block = io_.partial_serialization();
} ;
} ;
} // namespace
} // namespace
} // namespace

View File

@@ -0,0 +1,132 @@
// TJP COPYRIGHT HEADER
#include <tjp/core/variant/VType.hpp>
#include <tjp/core/variant/VType_of.hpp>
#include <tjp/core/variant/VType+IO.h>
#include <tjp/core/variant/VType+Functional.hpp>
#include <tjp/core/log/Log.h>
#include <tjp/core/log/LogOf.h>
#include <iostream>
#include <tjp/core/testing/catch.hpp>
namespace tjp::core {
namespace variant_testing {
struct X1 {};
constexpr
VType vtype_of_custom(const X1 *)
{
return vtype_from("X");
}
SCENARIO("core::variant::VType_of_strings")
{
GIVEN("compile vs runtime")
{
struct abcdefgh {};
// sLogTest("testing",
// logVar(type_name<abcdefgh>()));
constexpr auto x = vtype_of<abcdefgh>();
sLogTest("testing",
logVar(x));
sLogTest("testing",
logVar(vtype_of<abcdefgh>()));
}
}
SCENARIO("core::variant::VType_of")
{
GIVEN("compile vs runtime")
{
using A = VType;
sLogTest("testing",
logVar(type_name<VType>()) <<
logVar(type_name<A>()) <<
logVar(type_name<VType_ID>()) <<
logVar(type_name<std::iostream>()) <<
logVar(type_name<std::numeric_limits<int>>())
);
sLogTest("testing",
logVar(rtti::impl::format.leading_junk) <<
logVar(rtti::impl::format.trailing_junk)
);
sLogTest("testing",
"\n" << rtti::impl::RawTypeName<int>() <<
"\n" << type_name<int>() <<
"\n" << rtti::impl::RawTypeName<VType>() <<
"\n" << type_name<VType>() <<
"\n" << rtti::impl::RawTypeName<A>() <<
"\n" << type_name<A>() <<
"\n" << rtti::impl::RawTypeName<VType_ID>() <<
"\n" << type_name<VType_ID>() <<
"\n" << rtti::impl::RawTypeName<std::iostream>() <<
"\n" << type_name<std::iostream>() <<
"\n" << rtti::impl::RawTypeName<std::numeric_limits<int>>() <<
"\n" << type_name<std::numeric_limits<int>>()
);
sLogTest("testing",
logVar(hash_compile<u64>(type_name<VType>())) <<
logVar(hash_compile<u64>(type_name<VType_ID>())) <<
logVar(hash_runtime<u64>(type_name<VType>())) <<
logVar(hash_runtime<u64>(type_name<VType_ID>()))
);
constexpr VType v0 = vtype_of<VType>();
constexpr VType v1 = vtype_of<VType_ID>();
constexpr VType xt = vtype_of<X1>();
constexpr VType xt_ = core::vtype_from("X");
constexpr int xti = xt.id;
constexpr int xt_i = xt_.id;
static_assert(xti == xt_i);
REQUIRE(xt.id == xt_.id);
constexpr VType xtv = VType::type_of<X1>();
REQUIRE(xt == xtv);
sLogTest("testing",
logVar(v0) << logVar(v1));
}
}
} // namespace
namespace variant_testing_2 {
SCENARIO("core::variant::VType_of::other_namespace")
{
GIVEN("compile vs runtime")
{
constexpr VType xt = vtype_of<variant_testing::X1>();
constexpr VType xt_ = vtype_from("X");
REQUIRE(xt == xt_);
constexpr VType xtv = VType::type_of<variant_testing::X1>();
REQUIRE(xt == xtv);
}
}
} // namespace
} // namespace

View File

@@ -0,0 +1,379 @@
// TJP COPYRIGHT HEADER
#include <tjp/core/string/String.h>
#include <tjp/core/variant/Variant.hpp>
#include <tjp/core/variant/Variant+IO.hpp>
#include <tjp/core/variant/VType+IO.h>
#include <tjp/core/variant/Variant+Dispatch.h>
#include <tjp/core/io/bin/IO.h>
#include <tjp/core/ptr/Ptr+IO.h>
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/algorithm/unused.hpp>
#include <tjp/core/log/Log.h>
#include <tjp/core/log/LogOf.h>
// -------------------------------------------------------------------------------
// because the custom map type is actually in the std namespace
// we can either make it in the std namespace or put it in the variant namespace
namespace tjp::core::variant {
typedef std::map<std::string,Variant> CustomMapType;
constexpr
VType vtype_of_custom(const CustomMapType *) { return VType::make(30, 13); }
} // namespace
// -------------------------------------------------------------------------------
namespace tjp::core::variant::test_compile {
typedef std::map<std::string,Variant> CustomMapType;
constexpr
VType vtype_of_custom(const CustomMapType *) { return VType::make(30, 13); }
struct Custom
{
int i;
} ;
constexpr
VType vtype_of_custom(const Custom *) { return VType::make(64, 23); }
struct External
{
int x;
} ;
constexpr
VType vtype_of_custom(const External *) { return VType::make(65, 33); }
template<typename IO>
void io_bin(IO &io, Custom &a)
{
io.all(a.i);
}
template<typename IO>
void io_bin(IO &io, External &a)
{
io.all(a.x);
}
struct DispatchVariant
{
template<typename IO>
void dispatch_custom(IO &io, VariantIO &v)
{
core::variant::dispatch_variant(
v,
*this,
io_bin_variant<IO, VariantIO> { io }
);
}
template<typename D>
void dispatch_primitive(VType type, D &d)
{
if (core::variant::dispatch_type_builtin_value(type, d))
return;
switch(type.getNamespace())
{
case 30:
{
switch (type.getClass())
{
case 13: return d.template visit<CustomMapType>();
}
} break;
case 64:
{
switch (type.getClass())
{
case 23: return d.template visit<Custom>();
}
}
}
debug_assert(false);
}
template<typename V>
bool use_variant_serialization_for(const V &v)
{
auto primary = vtype_namespace(v.type.id);
return primary > 64;
}
bool variant_deserialization_available(VType type)
{
auto primary = vtype_namespace(type.id);
return primary <= 64;
}
} ;
struct DispatchVariantHandleExternal : DispatchVariant
{
template<typename IO>
void dispatch_custom(IO &io, VariantIO &v)
{
core::variant::dispatch_variant(
v,
*this,
io_bin_variant<IO, VariantIO> { io }
);
}
template<typename D>
void dispatch_primitive(VType type, D &d)
{
auto ns = type.getNamespace();
unused(ns);
if (core::variant::dispatch_type_builtin_value(type, d))
return;
switch(type.getNamespace())
{
case 30:
{
switch (type.getClass())
{
case 13: return d.template visit<CustomMapType>();
}
} break;
case 64:
{
switch (type.getClass())
{
case 23: return d.template visit<Custom>();
}
}
case 65:
{
switch (type.getClass())
{
case 33: return d.template visit<External>();
}
}
}
debug_assert(false);
}
template<typename V>
bool use_variant_serialization_for(const V &v)
{
return v.type.getNamespace() > 64;
}
bool variant_deserialization_available(VType type)
{
return true;
}
} ;
struct PoolAllocatorTest
{
typedef void is_strong_allocator;
template<typename T>
void allocate(T &t)
{
io_ptr_allocate_default(*this, t);
}
template<typename T, typename ... Args>
auto strong(Args && ...args)
{
return core::strong<T>(std::forward<Args>(args)...);
}
} ;
SCENARIO("core::variant")
{
GIVEN("nothing")
{
sLogTest("testing",
logVar(sizeof(Variant)) <<
logVar(sizeof(Variant::Functional)) <<
logVar(sizeof(Variant::value_type)) <<
logVar(sizeof(Variant::Type)) <<
logVar(sizeof(Variant::Allocator_))
);
}
GIVEN("a variant")
{
Variant v;
WHEN("variant is set to a unknown")
{
auto c = External { 5 };
v = c;
constexpr auto cv = vtype_of<External>();
constexpr auto cvn = cv.getNamespace();
static_assert(cvn == 65);
REQUIRE(cv.getNamespace() == 65);
auto vt = v.type;
REQUIRE(vt == cv);
THEN("unknown is set")
{
REQUIRE(v.ref<decltype(c)>().x == c.x);
}
WHEN("the variant with unknown is serialized from external -> internal -> internal -> external")
{
typedef io::bin::Writer<io::bin::memory_out, DispatchVariant> WriterInternal;
typedef io::bin::Reader<io::bin::memory_in, DispatchVariant> ReaderInternal;
typedef io::bin::Writer<io::bin::memory_out, DispatchVariantHandleExternal> WriterExternal;
typedef io::bin::Reader<io::bin::memory_in, DispatchVariantHandleExternal> ReaderExternal;
WriterExternal wE;
auto s1 = io::bin::serializeTo(wE, v);
ReaderInternal rI({ s1.data(), s1.size() });
auto v2 = io::bin::deserializeFrom<Variant>(rI);
WriterInternal wI;
auto s2 = io::bin::serializeTo(wI, v2);
ReaderExternal rE({ s2.data(), s2.size() });
auto v_ = io::bin::deserializeFrom<Variant>(rE);
THEN("unknown survives")
{
REQUIRE(v.ref<decltype(c)>().x == c.x);
}
}
}
WHEN("variant is set to a number")
{
auto c = 5;
v = c;
THEN("number is set")
{
REQUIRE(v.ref<decltype(c)>() == c);
}
}
WHEN("variant is set to a string")
{
auto c = std::string("hello");
v = c;
THEN("value is set")
{
REQUIRE(v.ref<decltype(c)>() == c);
}
}
WHEN("variant is set to a map")
{
float onePZero = 1.0;
double threeP5 = 3.5;
auto c = CustomMapType();
c["hi"] = onePZero;
c["there"] = Variant(threeP5);
c["what"] = Custom{ .i = 10 };
c["recursive"] = CustomMapType { { "a", onePZero }};
v = c;
THEN("hi value is set")
{
auto l = v.ref<decltype(c)>()["hi"].ref<decltype(onePZero)>();
auto r = c["hi"].ref<decltype(onePZero)>();
REQUIRE(l == r);
}
THEN("there value is set")
{
auto l = v.ref<decltype(c)>()["there"].ref<decltype(threeP5)>();
auto r = c["there"].ref<decltype(threeP5)>();
REQUIRE(l == r);
}
THEN("the value is serialized and deserialized")
{
typedef io::bin::Writer<io::bin::memory_out, DispatchVariant> Writer;
typedef io::bin::Reader<io::bin::memory_in, DispatchVariant> Reader;
io::bin::Serialization s1;
Writer w1;
s1 = io::bin::serializeTo(w1, v);
Reader reader({ s1.data(), s1.size() });
auto v2 = io::bin::deserializeFrom<Variant>(reader);
auto &m = v2.ref<CustomMapType>();
REQUIRE(m["hi"].ref<decltype(onePZero)>() == onePZero);
REQUIRE(m["there"].ref<decltype(threeP5)>() == threeP5);
REQUIRE(m["recursive"].ref<CustomMapType>()["a"].ref<decltype(onePZero)>() == onePZero);
}
THEN("the value is serialized deserialized and serialized again")
{
typedef io::bin::Writer<io::bin::memory_out, DispatchVariant> Writer;
typedef io::bin::Reader<io::bin::memory_in, DispatchVariant> Reader;
io::bin::Serialization s1, s2;
{
Writer w1;
s1 = io::bin::serializeTo(w1, v);
Reader reader({ s1.data(), s1.size() });
auto v2 = io::bin::deserializeFrom<Variant>(reader);
Writer w2;
s2 = io::bin::serializeTo(w2, v2);
}
REQUIRE(s1 == s2);
}
THEN("the value is serialized deserialized and serialized again with a pool allocator")
{
typedef io::bin::Writer<io::bin::memory_out, DispatchVariant, PoolAllocatorTest> Writer;
typedef io::bin::Reader<io::bin::memory_in, DispatchVariant, PoolAllocatorTest> Reader;
io::bin::Serialization s1, s2;
static_assert(allocators::has_allocator<Reader>::value);
static_assert(has_variant_allocator<Reader>::value);
static_assert(allocators::has_strong_allocator_flag<Reader::Allocator>::value);
{
Writer w1;
s1 = io::bin::serializeTo(w1, v);
Reader reader({ s1.data(), s1.size() });
auto v2 = io::bin::deserializeFrom<Variant>(reader);
Writer w2;
s2 = io::bin::serializeTo(w2, v2);
}
REQUIRE(s1 == s2);
}
}
}
}
} // namespace