www.pudn.com > Loki.rar > Variant.h


//////////////////////////////////////////////////////////////////////////////// 
// 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