Ich möchte eine an boost::any angelehnte, allerdings etwas zeitgemäßere Variant-Klasse mit Move-Semantics und eventuell, je nachdem was der Profiler sagt, einem "Small-String-Optimization"-Äquivalent dafür hinzufügen. Das Ziel dabei ist, dass ich gewisse Typen direkt als atomare Typen (alle Built-In, Vector, Matrix, usw.) unterstützen möchte und zusätzlich andere, die Reflection unterstützen (von Reflectable-Basisklasse abgeleitet), um die Variantklasse für in meinem Reflectionsystem zu verwenden. Hierfür habe ich in die Template-Metaprogrammierung-Trickkiste gegriffen, um festzustellen, ob eine Klasse Reflectable ist. Sonst müsste ich alle Typen einzeln dem Variant bekannt machen. Wenn ein Typ weder "atomar" noch Reflectable ist, meldet sich deshalb ein static_assert und außerdem noch der Compiler selbst. Wo ich schon einmal dabei war, habe ich dynamic_cast durch das System ersetzt, sodass zur Laufzeit Casts nur noch als Integer-Vergleich möglich sind, anstatt C++-RTTI zu verwenden.
Code: Alles auswählen
template<class U> struct D
{
private:
class IsRefl { char c[2]; };
class IsNotRefl { char c[3]; };
static IsRefl checkRefl(Reflectable*);
static IsNotRefl checkRefl(...);
public:
enum { v = ((sizeof(IsRefl) == sizeof(checkRefl(static_cast<Reflectable*>(static_cast<U*>(nullptr))))) ? Flags::Refl : 0), p = 0 };
static_assert(v != 0, "Occurence of type not supported by type system.");
};
template<> struct D<Bool> { enum { v = Flags::Bool, p = 0 }; };
template<> struct D<Char> { enum { v = Flags::Char, p = 0 }; };
template<> struct D<Int1> { enum { v = Flags::Int1, p = 0 }; };
template<> struct D<Int2> { enum { v = Flags::Int2, p = 0 }; };
template<> struct D<Int4> { enum { v = Flags::Int4, p = 0 }; };
template<> struct D<Int8> { enum { v = Flags::Int8, p = 0 }; };
template<> struct D<UInt1> { enum { v = Flags::UInt1, p = 0 }; };
template<> struct D<UInt2> { enum { v = Flags::UInt2, p = 0 }; };
template<> struct D<UInt4> { enum { v = Flags::UInt4, p = 0 }; };
template<> struct D<UInt8> { enum { v = Flags::UInt8, p = 0 }; };
template<> struct D<Float4> { enum { v = Flags::Float4, p = 0 }; };
template<> struct D<Float8> { enum { v = Flags::Float8, p = 0 }; };
template<> struct D<String> { enum { v = Flags::String, p = 0 }; };
template<class U> struct D<U&> { enum { v = Flags::Ref | D<U>::v, p = D<U>::p }; };
template<class U> struct D<U*> { enum { v = (1 << Flags::PtrOffset) + D<U>::v, p = D<U>::p + 1 }; static_assert(p < 7, "More than 6 pointer indirections not supported"); };
template<class U> struct D<const U> { enum { v = 1 << (Flags::ConstOffset + D<U>::p) | D<U>::v, p = D<U>::p }; };
// BEISPIEL
Variant v(42.0);
v.as<String>(); // knallt
Float8 = v.as<Float8>(); // geht
Sind da irgendwelche Fehler drinnen? Soweit ich das bisher getestet habe, funktioniert es. Jeder Reflectable-Type wird als nächsten Schritt eine ID bekommen, die dann auch in diesem Fall korrektes Casten ermöglicht.
Zurück zu dem ursprünglichen Problem: Gäbe es sonst noch ähnlich einfache alternative Variant-Lösungen, oder muss ich auf Move-Semantics verzichten? Oder das boost::variant-Monster verwenden? Wollte eigentlich so viel wie möglich selber bauen.