flatten 20260225
This commit is contained in:
24
tjp/core/variant/ConcreteVariant+IO.h
Normal file
24
tjp/core/variant/ConcreteVariant+IO.h
Normal 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
|
||||
|
||||
13
tjp/core/variant/ConcreteVariant.h
Normal file
13
tjp/core/variant/ConcreteVariant.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
namespace variant {
|
||||
|
||||
template<typename T>
|
||||
struct ConcreteVariant;
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
84
tjp/core/variant/ConcreteVariant.hpp
Normal file
84
tjp/core/variant/ConcreteVariant.hpp
Normal 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
|
||||
|
||||
21
tjp/core/variant/Notes.txt
Normal file
21
tjp/core/variant/Notes.txt
Normal 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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
115
tjp/core/variant/VType+Functional.hpp
Normal file
115
tjp/core/variant/VType+Functional.hpp
Normal 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
|
||||
|
||||
13
tjp/core/variant/VType+IO.cpp
Normal file
13
tjp/core/variant/VType+IO.cpp
Normal 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
|
||||
18
tjp/core/variant/VType+IO.h
Normal file
18
tjp/core/variant/VType+IO.h
Normal 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
|
||||
10
tjp/core/variant/VType.cpp
Normal file
10
tjp/core/variant/VType.cpp
Normal 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
48
tjp/core/variant/VType.h
Normal 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
|
||||
47
tjp/core/variant/VType.hpp
Normal file
47
tjp/core/variant/VType.hpp
Normal 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"
|
||||
39
tjp/core/variant/VType.inl
Normal file
39
tjp/core/variant/VType.inl
Normal 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
|
||||
|
||||
102
tjp/core/variant/VType_of.hpp
Normal file
102
tjp/core/variant/VType_of.hpp
Normal 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
|
||||
22
tjp/core/variant/VVector.h
Normal file
22
tjp/core/variant/VVector.h
Normal 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
|
||||
28
tjp/core/variant/Variant+Dispatch.h
Normal file
28
tjp/core/variant/Variant+Dispatch.h
Normal 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"
|
||||
99
tjp/core/variant/Variant+Dispatch.inl
Normal file
99
tjp/core/variant/Variant+Dispatch.inl
Normal 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
|
||||
27
tjp/core/variant/Variant+Functional+Generate.hpp
Normal file
27
tjp/core/variant/Variant+Functional+Generate.hpp
Normal 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();
|
||||
}
|
||||
|
||||
25
tjp/core/variant/Variant+Functional.cpp
Normal file
25
tjp/core/variant/Variant+Functional.cpp
Normal 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
|
||||
|
||||
605
tjp/core/variant/Variant+Functional.hpp
Normal file
605
tjp/core/variant/Variant+Functional.hpp
Normal 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
|
||||
|
||||
137
tjp/core/variant/Variant+IO.hpp
Normal file
137
tjp/core/variant/Variant+IO.hpp
Normal 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
|
||||
77
tjp/core/variant/Variant+VariantFunctionalUsingMap.hpp
Normal file
77
tjp/core/variant/Variant+VariantFunctionalUsingMap.hpp
Normal 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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
23
tjp/core/variant/Variant.h
Normal file
23
tjp/core/variant/Variant.h
Normal 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
|
||||
|
||||
12
tjp/core/variant/Variant.hpp
Normal file
12
tjp/core/variant/Variant.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "Variant+Functional.hpp"
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
namespace variant {
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
13
tjp/core/variant/VariantIO.h
Normal file
13
tjp/core/variant/VariantIO.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Variant.h"
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
namespace variant {
|
||||
|
||||
struct VariantIO;
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
41
tjp/core/variant/VariantIO.hpp
Normal file
41
tjp/core/variant/VariantIO.hpp
Normal 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
|
||||
24
tjp/core/variant/VariantSerialization+IO.hpp
Normal file
24
tjp/core/variant/VariantSerialization+IO.hpp
Normal 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
|
||||
|
||||
14
tjp/core/variant/VariantSerialization.h
Normal file
14
tjp/core/variant/VariantSerialization.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "VType.hpp"
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
namespace variant {
|
||||
|
||||
struct VariantSerialization;
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
36
tjp/core/variant/VariantSerialization.hpp
Normal file
36
tjp/core/variant/VariantSerialization.hpp
Normal 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
|
||||
|
||||
132
tjp/core/variant/_tests/VType_of.cpp
Normal file
132
tjp/core/variant/_tests/VType_of.cpp
Normal 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
|
||||
379
tjp/core/variant/_tests/Variant.cpp
Normal file
379
tjp/core/variant/_tests/Variant.cpp
Normal 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
|
||||
Reference in New Issue
Block a user