//////////////////////////////////////////////////////////////////////////////// // Copyright (c) 2001, 2002 by Andrei Alexandrescu // Permission to use, copy, modify, distribute and sell this software for any // purpose is hereby granted without fee, provided that the above copyright // notice appear in all copies and that both that copyright notice and this // permission notice appear in supporting documentation. // The author makes no representations about the suitability of this software // for any purpose. It is provided "as is" // without express or implied warranty. //////////////////////////////////////////////////////////////////////////////// #ifndef VARIANT_INC_ #define VARIANT_INC_ #include #include #include #include "Visitor.h" #include "Typelist.h" #include "static_check.h" // // At the moment there is no namespace for Variant // #ifdef _MSC_VER # include "VC_Alignment.h" #endif //////////////////////////////////////////////////////////////////////////////// // class template ConfigurableUnion // Builds a union that contains each type in a typelist // Usage: ConfigurableUnion is the very type //////////////////////////////////////////////////////////////////////////////// template union ConfigurableUnion; template <> union ConfigurableUnion< ::Loki::NullType > { }; template union ConfigurableUnion { private: ASSERT_TYPELIST(TList); typedef typename TList::Head Head; typedef typename TList::Tail Tail; public: Head head_; ConfigurableUnion tail_; }; //////////////////////////////////////////////////////////////////////////////// // class template MaxSize // Computes the maximum sizeof for all types in a typelist // Usage: MaxSize::result //////////////////////////////////////////////////////////////////////////////// template struct MaxSize; template <> struct MaxSize< ::Loki::NullType > { enum { result = 0 }; }; template struct MaxSize { private: ASSERT_TYPELIST(TList); typedef typename TList::Head Head; typedef typename TList::Tail Tail; private: enum { headResult = sizeof(Head) }; enum { tailResult = MaxSize::result }; public: enum { result = headResult > tailResult ? headResult : tailResult }; }; //////////////////////////////////////////////////////////////////////////////// // class AlignedPODBase // Defines a host of protected types used by AlignedPOD (defined later) // Could be just part of AlignedPOD itself, but making it separate ought to // reduce compile times //////////////////////////////////////////////////////////////////////////////// class AlignedPODBase { protected: template struct ComputeAlignBound { private: ASSERT_TYPELIST(TList); typedef typename TList::Head Head; typedef typename TList::Tail Tail; private: template struct In { private: typedef typename TList1::Head Head1; typedef typename TList1::Tail Tail1; typedef typename ComputeAlignBound::Result TailResult; public: typedef typename ::Loki::Select < sizeof(Head1) <= size, ::Loki::Typelist, TailResult > ::Result Result; }; template<> struct In< ::Loki::NullType > { typedef ::Loki::NullType Result; }; public: typedef typename In::Result Result; }; template struct Structify { U dummy_; }; class Unknown; // VC7: fatal error C1067: compiler limit : // debug information module size exceeded // Therfore I decreased the list to 26 without // changing the rage of detectable alignment typedef TYPELIST_26( char, wchar_t, short int, int, long int, float, double, long double, char*, void*, Unknown (*)(Unknown), Unknown* Unknown::*, Unknown (Unknown::*)(Unknown), Structify, Structify, Structify, Structify, Structify, Structify, Structify, Structify, Structify, Structify, Structify, Structify, Structify ) TypesOfAllAlignments; }; //////////////////////////////////////////////////////////////////////////////// // class template AlignedPOD // Computes the alignment of all types in a typelist // Usage: ConfigurableUnion is the very type //////////////////////////////////////////////////////////////////////////////// template class AlignedPOD : private AlignedPODBase { enum { maxSize = MaxSize::result }; typedef typename ComputeAlignBound < TypesOfAllAlignments, maxSize > ::Result AlignTypes; public: typedef ConfigurableUnion Result; }; //////////////////////////////////////////////////////////////////////////////// // class template MakeConst // Given a typelist TList, returns a typelist that contains the types in TList // adding a const qualifier to each. // Usage: MakeConst::Result //////////////////////////////////////////////////////////////////////////////// template struct MakeConst; template <> struct MakeConst< ::Loki::NullType > { typedef ::Loki::NullType Result; // terminator is not const }; template struct MakeConst { private: ASSERT_TYPELIST(TList); typedef typename TList::Head Head; typedef typename TList::Tail Tail; private: typedef typename MakeConst::Result NewTail; public: typedef ::Loki::Typelist Result; }; //////////////////////////////////////////////////////////////////////////////// // class template Converter // Supports the Variant-to-Variant conversion constructor // Guaranteed to issue an internal compiler error on: // 1. Metrowerks CodeWarrior 7.0 (internal compiler error: File: // 'CTemplateTools.c' Line: 1477 // Variant.h line 244 UnitBase > VisitorBase;) // 2. Microsoft Visual C++ 7.1 alpha release (Assertion failed: // ( name - nameBuf ) < LIMIT_ID_LENGTH, // file f:\vs70builds\2108\vc\Compiler\CxxFE\sl\P1\C\outdname.c, // line 4583) // 3. GNU gcc 2.95.3-6 (Internal compiler error 980422) //////////////////////////////////////////////////////////////////////////////// template struct Converter { //private: VC7 complains struct UnitBase : public VariantFrom::ConstStrictVisitor { protected: VariantTo* storageForDestination; }; template struct Unit : public Base { private: void DoVisit(const Type& obj, ::Loki::Int2Type) { new(this->storageForDestination) VariantTo(obj); } void DoVisit(const Type&, ::Loki::Int2Type) { throw std::runtime_error("Cannot convert"); } virtual void Visit(const Type& obj) { using namespace Loki; typedef typename VariantTo::Types TList; enum { dispatch = TL::IndexOf::value > -1 }; this->DoVisit(obj, Int2Type()); } }; private: typedef ::Loki::GenLinearHierarchy < typename VariantFrom::Types, Unit, UnitBase > VisitorBase; public: struct Visitor : public VisitorBase { explicit Visitor(VariantTo& dest) { // Initialize the pointer to destination this->storageForDestination = &dest; } }; }; //////////////////////////////////////////////////////////////////////////////// // class template ConverterTo // Supports Variant-to-T conversion //////////////////////////////////////////////////////////////////////////////// #if 0 template struct ConverterTo { private: struct DestHolder : VariantFrom::ConstStrictVisitor { protected: DestHolder() {} DestHolder(const T& dest) : destination_(dest) {} T destination_; }; template struct VisitorBase : VisitorBase { private: ASSERT_TYPELIST(TList); typedef typename TList::Head Type; typedef typename TList::Tail Tail; protected: VisitorBase() {} VisitorBase(const T& dest) : VisitorBase(dest) {} private: void DoVisit(const Type& obj, ::Loki::Int2Type) { // // T temp(obj) // swap(destination_, temp) or destination_ = temp // this->destination_ = obj; } void DoVisit(const Type&, ::Loki::Int2Type) { throw std::runtime_error("Cannot convert"); } virtual void Visit(const Type& obj) { using namespace Loki; enum { dispatch = Conversion::exists != 0 }; this->DoVisit(obj, Int2Type()); } }; template <> struct VisitorBase< ::Loki::NullType > : DestHolder { protected: VisitorBase< ::Loki::NullType >() {} VisitorBase< ::Loki::NullType >(const T& dest) : DestHolder(dest) {} }; typedef VisitorBase < typename VariantFrom::Types > VisitorBaseType; public: struct Visitor : public VisitorBaseType { Visitor() {} explicit Visitor(const T& dest) : VisitorBaseType(dest) {} const T &GetDestination() const { return this->destination_; } }; }; #else template struct ConverterTo { //private: struct UnitBase : public VariantFrom::ConstStrictVisitor { protected: T destination_; }; template struct Unit : public Base { private: void DoVisit(const Type& obj, ::Loki::Int2Type) { this->destination_ = obj; } void DoVisit(const Type&, ::Loki::Int2Type) { throw std::runtime_error("Cannot convert"); } virtual void Visit(const Type& obj) { using namespace Loki; enum { dispatch = Conversion::exists != 0 }; this->DoVisit(obj, Int2Type()); } }; typedef ::Loki::GenLinearHierarchy< typename VariantFrom::Types, Unit, UnitBase > VisitorBase; public: struct Visitor : public VisitorBase { const T &GetDestination() const { return this->destination_; } }; }; #endif namespace Private { template struct RawDataKeeper { private: typedef char RawBuffer_t[sizeof(T)]; enum ObjectState_e { eNone, ePreConstruct, ePostConstruct }; T &obj_; ObjectState_e eObjState_; RawBuffer_t bufferOrg_; RawBuffer_t bufferNew_; private: void SetObj(const RawBuffer_t &buf) throw() { memcpy(&reinterpret_cast(obj_), buf, sizeof(buf)); } void GetObj(RawBuffer_t &buf) throw() { memcpy(buf, &reinterpret_cast(obj_), sizeof(buf)); } public: explicit RawDataKeeper(T &obj) : obj_(obj), eObjState_(eNone) {} // add const U & version ? template void ConstructNew(U &src) { assert(eObjState_ == eNone); GetObj(bufferOrg_); eObjState_ = ePreConstruct; new (&obj_) T(src); eObjState_ = ePostConstruct; GetObj(bufferNew_); SetObj(bufferOrg_); } void SetNew() throw() { assert(eObjState_ == ePostConstruct); obj_.~T(); SetObj(bufferNew_); eObjState_ = eNone; } ~RawDataKeeper() { switch(eObjState_) { case ePostConstruct: SetObj(bufferNew_); obj_.~T(); // fall case ePreConstruct: SetObj(bufferOrg_); // fall case eNone: break; } } private: RawDataKeeper(const RawDataKeeper &); RawDataKeeper& operator=(const RawDataKeeper &); }; // // based on Eric Fridman's safe_swap from boost Variant // // strong exception-safety guarantee. // // !WARNING! // SafeSwap CANNOT be safely used in the general case if either // argument's data members may be accessed concurrently. // template void SafeSwap(T &lhs, T &rhs) { typedef RawDataKeeper RhsDataKeeper; typedef RawDataKeeper LhsDataKeeper; RhsDataKeeper rhsKeeper(rhs); rhsKeeper.ConstructNew(lhs); LhsDataKeeper lhsKeeper(lhs); lhsKeeper.ConstructNew(rhs); rhsKeeper.SetNew(); lhsKeeper.SetNew(); } // // based on Eric Fridman's safe_assign from boost Variant // // strong exception-safety guarantee. // // !WARNING! // SafeAssign CANNOT be safely used in the general case if either // argument's data members may be accessed concurrently. // template void SafeAssign(T &lhs, const U &src) { typedef RawDataKeeper LhsDataKeeper; LhsDataKeeper lhsKeeper(lhs); lhsKeeper.ConstructNew(src); lhsKeeper.SetNew(); } template inline void SwapHelper(T &lhs, T &rhs) { using namespace std; swap(lhs, rhs); } } // namespace Private //////////////////////////////////////////////////////////////////////////////// // class template Variant // Implements a discriminated union in C++ //////////////////////////////////////////////////////////////////////////////// template > class Variant { // VC7: fatal error C1067: compiler limit : // debug information module size exceeded // Therfore define this type here typedef typename AlignedPODType::Result Align; public: typedef TList Types; // Default constructor // Initializes the Variant with a default-constructed object of the // first type in the typelist Variant() { typedef typename TList::Head T; new(&buffer_[0]) T; vptr_ = VTableImpl::GetVPTR(); } // Copy constructor Variant(const Variant& rhs) { (rhs.vptr_->clone_)(rhs, *this); } private: // Converting constructor; accepts any type in the typelist // @@@ Suggested simple improvement: accept any type convertible to one of // the types in the typelist. Use Loki::Conversion as a building block template void VariantConstruct(const T& val, double) { STATIC_CHECK((::Loki::TL::IndexOf::value >= 0), Invalid_Type_Used_As_Initializer); new(&buffer_[0]) T(val); vptr_ = VTableImpl::GetVPTR(); } // Inter-Variant conversion constructor // Current policy is: conversion succeeds iff the actual type of the source // is one of the types accepted by the target // @@@ Possible change: accept if the actual type of the source is // convertible to one of the types accepted by the target. Problem to // solve: handle ambiguities in a satisfactory manner. Suggestion: when // in doubt, do closest to what the compiler would do. template void VariantConstruct(const Variant& rhs, int) { typename Converter, Variant>::Visitor v(*this); typename Variant::ConstStrictVisitor& visitor = v; rhs.Accept(visitor); } public: // VC7 don't support partial ordering // The constructor initialization section // is empty which make it to use function instead // without the int = 0 the explicit seems to confuse // VC7 when the copy constructor should be selected // Crazy stuff template explicit Variant(const T& val, int = 0) { VariantConstruct(val, int(0)); } // Canonic assignment operator Variant& operator=(const Variant& rhs) { Private::SafeAssign(*this, rhs); return *this; } private: // Assignment operator from one of the allowed types // This is necessary because the constructor is explicit template void VariantAssign(const T& rhs, double) { Private::SafeAssign(*this, rhs); } // Assignment from another Variant instantiation template void VariantAssign(const Variant& rhs, int) { Private::SafeAssign(*this, rhs); } public: // VC7 don't support partial ordering template Variant& operator=(const T& rhs) { // Both VariantAssign are the same in this implementation VariantAssign(rhs, int(0)); return *this; } // ~ ~Variant() { (vptr_->destroy_)(*this); } // Visitors definitions // @@@ Possible improvement: add defintions of visitor who return // something else than void typedef ::Loki::Visitor StrictVisitor; typedef ::Loki::Visitor::Result, void> ConstStrictVisitor; typedef ::Loki::NonStrictVisitor NonStrictVisitor; typedef ::Loki::NonStrictVisitor::Result, void> ConstNonStrictVisitor; private: // VTable structure // The essential component of the fake vtable idiom, VTable contains // pointers to functions, pointers that will be filled up with addresses // of functions in VTableImpl struct VTable { const std::type_info& (*typeId_)(); void (*destroy_)(const Variant&); void (*clone_)(const Variant&, Variant&); void (*swap_)(void* lhs, void* rhs); void (*accept_)(Variant&, StrictVisitor&); void (*acceptConst_)(const Variant&, ConstStrictVisitor&); }; // VTable concrete implementations // VTableImpl contains definitions for all of a VTable's pointer to // functions. // VC7 thinks that Variant inside VTableImpl is template typedef Variant VariantType; template struct VTableImpl { private: static const std::type_info& TypeId() { return typeid(T); } static void Destroy(const VariantType& var) { const T& data = *reinterpret_cast(&var.buffer_[0]); (void)data.~T(); } static void Swap(void* lhs, void* rhs) { Private::SwapHelper(*static_cast(lhs), *static_cast(rhs)); } static void Clone(const VariantType& src, VariantType& dest) { new(&dest.buffer_[0]) T( *reinterpret_cast(&src.buffer_[0])); dest.vptr_ = src.vptr_; } static void Accept(VariantType& var, StrictVisitor& visitor) { typedef typename StrictVisitor::ReturnType RType; ::Loki::Visitor &v = visitor; v.Visit(*reinterpret_cast(&var.buffer_[0])); } static void AcceptConst(const VariantType& var, ConstStrictVisitor& visitor) { typedef typename ConstStrictVisitor::ReturnType RType; ::Loki::Visitor &v = visitor; v.Visit(*reinterpret_cast(&var.buffer_[0])); } public: static const VTable *GetVPTR() { static const VTable vTbl_ = { &TypeId, &Destroy, &Clone, &Swap, &Accept, &AcceptConst, }; return &vTbl_; } }; template friend struct VTableImpl; private: // should be private; some compilers prefer 'public' :o} enum { neededSize = MaxSize::result }; VTable const * vptr_; union { Align dummy_; char buffer_[neededSize]; }; public: void swap(Variant& rhs) { if (this->TypeId() == rhs.TypeId()) { (vptr_->swap_)(this->buffer_, rhs.buffer_); } else { Private::SafeSwap(*this, rhs); } } const std::type_info& TypeId() const { return (vptr_->typeId_)(); } template T* GetPtr() { return TypeId() == typeid(T) ? reinterpret_cast(&buffer_[0]) : 0; } template const T* GetPtr() const { return TypeId() == typeid(T) ? reinterpret_cast(&buffer_[0]) : 0; } template T& Get() { T* p = GetPtr(); if (!p) throw std::runtime_error("Variant::Get() Invalid variant type"); return *p; } template const T& Get() const { const T* p = GetPtr(); if (!p) throw std::runtime_error("Variant::Get() const Invalid variant type"); return *p; } // Visitation primitives // Although there are four visitor types, only two Accept functions are // necessary, because the non-strict visitors inherit the strict visitors void Accept(StrictVisitor& visitor) { (vptr_->accept_)(*this, visitor); } void Accept(ConstStrictVisitor& visitor) const { (vptr_->acceptConst_)(*this, visitor); } // Extracts the value of a Variant converted to a specific type template To ConvertTo() const { typename ConverterTo::Visitor v; ConstStrictVisitor& visitor = v; this->Accept(visitor); return v.GetDestination(); } // Changes the type of a Variant in-place template void ChangeType() { Variant(this->ConvertTo()).swap(*this); } }; //////////////////////////////////////////////////////////////////////////////// // Change log: // July 10, 2002: ported by Rani Sharoni to VC7 (RTM - 9466) //////////////////////////////////////////////////////////////////////////////// #endif