SAGA C++ API 1.6
hold_any.hpp
Go to the documentation of this file.
00001 //  Copyright (c) 2007-2009 Hartmut Kaiser
00002 //  Copyright (c) Christopher Diggins 2005
00003 //  Copyright (c) Pablo Aguilar 2005
00004 //  Copyright (c) Kevlin Henney 2001
00005 //
00006 //  Distributed under the Boost Software License, Version 1.0. (See accompanying
00007 //  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
00008 //
00009 //  The class saga::detail::hold_any is built based on the any class
00010 //  published here: http://www.codeproject.com/cpp/dynamic_typing.asp. 
00011 
00012 #if !defined(SAGA_HOLD_ANY_DEC_01_2007_0133PM)
00013 #define SAGA_HOLD_ANY_DEC_01_2007_0133PM
00014 
00015 #include <boost/config.hpp>
00016 #include <boost/type_traits/remove_reference.hpp>
00017 #include <boost/type_traits/is_reference.hpp>
00018 #include <boost/throw_exception.hpp>
00019 #include <boost/static_assert.hpp>
00020 #include <boost/mpl/bool.hpp>
00021 #include <boost/assert.hpp>
00022 #include <boost/static_assert.hpp>
00023 #include <boost/type_traits/is_const.hpp>
00024 
00025 #include <stdexcept>
00026 #include <typeinfo>
00027 #include <algorithm>
00028 
00031 
00032 namespace saga { namespace detail
00033 {
00034     struct bad_any_cast
00035       : std::bad_cast
00036     {
00037         bad_any_cast(std::type_info const& src, std::type_info const& dest)
00038           : from(src.name()), to(dest.name())
00039         {}
00040 
00041         virtual const char* what() const throw() { return "bad any cast"; }
00042 
00043         const char* from;
00044         const char* to;
00045     };
00046 
00048     namespace internals
00049     {
00050         // function pointer table
00051         struct fxn_ptr_table
00052         {
00053             std::type_info const& (*get_type)();
00054             void (*static_delete)(void**);
00055             void (*destruct)(void**);
00056             void (*clone)(void* const*, void**);
00057             void (*move)(void* const*, void**);
00058         };
00059 
00060         // static functions for small value-types
00061         template<typename Small>
00062         struct fxns;
00063 
00064         template<>
00065         struct fxns<boost::mpl::true_>
00066         {
00067             template<typename T>
00068             struct type
00069             {
00070                 static std::type_info const& get_type()
00071                 {
00072                     return typeid(T);
00073                 }
00074                 static void static_delete(void** x)
00075                 {
00076                     reinterpret_cast<T*>(x)->~T();
00077                 }
00078                 static void destruct(void** x)
00079                 {
00080                     reinterpret_cast<T*>(x)->~T();
00081                 }
00082                 static void clone(void* const* src, void** dest)
00083                 {
00084                     new (dest) T(*reinterpret_cast<T const*>(src));
00085                 }
00086                 static void move(void* const* src, void** dest)
00087                 {
00088                     reinterpret_cast<T*>(dest)->~T();
00089                     *reinterpret_cast<T*>(dest) =
00090                         *reinterpret_cast<T const*>(src);
00091                 }
00092             };
00093         };
00094 
00095         // static functions for big value-types (bigger than a void*)
00096         template<>
00097         struct fxns<boost::mpl::false_>
00098         {
00099             template<typename T>
00100             struct type
00101             {
00102                 static std::type_info const& get_type()
00103                 {
00104                     return typeid(T);
00105                 }
00106                 static void static_delete(void** x)
00107                 {
00108                     // destruct and free memory
00109                     delete (*reinterpret_cast<T**>(x));
00110                 }
00111                 static void destruct(void** x)
00112                 {
00113                     // destruct only, we'll reuse memory
00114                     (*reinterpret_cast<T**>(x))->~T();
00115                 }
00116                 static void clone(void* const* src, void** dest)
00117                 {
00118                     *dest = new T(**reinterpret_cast<T* const*>(src));
00119                 }
00120                 static void move(void* const* src, void** dest)
00121                 {
00122                     (*reinterpret_cast<T**>(dest))->~T();
00123                     **reinterpret_cast<T**>(dest) =
00124                         **reinterpret_cast<T* const*>(src);
00125                 }
00126             };
00127         };
00128 
00129         template<typename T>
00130         struct get_table
00131         {
00132             typedef boost::mpl::bool_<(sizeof(T) <= sizeof(void*))> is_small;
00133 
00134             static fxn_ptr_table* get()
00135             {
00136                 static fxn_ptr_table static_table =
00137                 {
00138                     fxns<is_small>::template type<T>::get_type,
00139                     fxns<is_small>::template type<T>::static_delete,
00140                     fxns<is_small>::template type<T>::destruct,
00141                     fxns<is_small>::template type<T>::clone,
00142                     fxns<is_small>::template type<T>::move
00143                 };
00144                 return &static_table;
00145             }
00146         };
00147 
00149         struct empty {};
00150 
00151     }
00153 
00155     // We need to be able to default construct any type stored in a hold_any
00156     // Some of the saga API objects do have a default constructor doing more
00157     // than just creating an empty API (facade object), which is evil. So we
00158     // use the create_default template. This template is specialized for those 
00159     // picky types elsewhere.
00160     template <typename T>
00161     struct create_default
00162     {
00163         static T* call() { return new T; }
00164         template <typename T_> static void call(T_* obj) { new (obj) T; }
00165     };
00166 
00168     class hold_any
00169     {
00170     public:
00171         // constructors
00172         template <typename T>
00173         hold_any(T const& x)
00174           : table(saga::detail::internals::get_table<T>::get()), object(0)
00175         {
00176             if (saga::detail::internals::get_table<T>::is_small::value)
00177                 new (&object) T(x);
00178             else
00179                 object = new T(x);
00180         }
00181 
00182         hold_any()
00183           : table(saga::detail::internals::get_table<saga::detail::internals::empty>::get()),
00184             object(0)
00185         {
00186         }
00187 
00188         hold_any(hold_any const& x)
00189           : table(saga::detail::internals::get_table<saga::detail::internals::empty>::get()),
00190             object(0)
00191         {
00192             assign(x);
00193         }
00194 
00195         ~hold_any()
00196         {
00197             table->static_delete(&object);
00198         }
00199 
00200         // assignment
00201         hold_any& assign(hold_any const& x)
00202         {
00203             if (&x != this) {
00204                 // are we copying between the same type?
00205                 if (table == x.table) {
00206                     // if so, we can avoid reallocation
00207                     table->move(&x.object, &object);
00208                 }
00209                 else {
00210                     reset();
00211                     x.table->clone(&x.object, &object);
00212                     table = x.table;
00213                 }
00214             }
00215             return *this;
00216         }
00217 
00218         template <typename T>
00219         hold_any& assign(T const& x)
00220         {
00221             // are we copying between the same type?
00222             saga::detail::internals::fxn_ptr_table* x_table = 
00223                 saga::detail::internals::get_table<T>::get();
00224             if (table == x_table) {
00225             // if so, we can avoid deallocating and re-use memory
00226                 table->destruct(&object);    // first destruct the old content
00227                 if (saga::detail::internals::get_table<T>::is_small::value) {
00228                     // create copy on-top of object pointer itself
00229                     new (&object) T(x);
00230                 }
00231                 else {
00232                     // create copy on-top of old version
00233                     new (object) T(x);
00234                 }
00235             }
00236             else {
00237                 if (saga::detail::internals::get_table<T>::is_small::value) {
00238                     // create copy on-top of object pointer itself
00239                     table->destruct(&object); // first destruct the old content
00240                     new (&object) T(x); 
00241                 }
00242                 else {
00243                     reset();                  // first delete the old content
00244                     object = new T(x);
00245                 }
00246                 table = x_table;      // update table pointer
00247             }
00248             return *this;
00249         }
00250 
00251         template <typename T>
00252         hold_any& init()
00253         {
00254             // are we copying between the same type?
00255             saga::detail::internals::fxn_ptr_table* x_table = 
00256                 saga::detail::internals::get_table<T>::get();
00257             if (table == x_table) {
00258             // if so, we can avoid deallocating and re-use memory
00259                 table->destruct(&object);    // first destruct the old content
00260                 if (saga::detail::internals::get_table<T>::is_small::value) {
00261                     // create copy on-top of object pointer itself
00262                     create_default<T>::call(&object);
00263                 }
00264                 else {
00265                     // create copy on-top of old version
00266                     create_default<T>::call(object);
00267                 }
00268             }
00269             else {
00270                 if (saga::detail::internals::get_table<T>::is_small::value) {
00271                     // create copy on-top of object pointer itself
00272                     table->destruct(&object); // first destruct the old content
00273                     create_default<T>::call(&object); 
00274                 }
00275                 else {
00276                     reset();                  // first delete the old content
00277                     object = create_default<T>::call();
00278                 }
00279                 table = x_table;      // update table pointer
00280             }
00281             return *this;
00282         }
00283 
00284         // assignment operator
00285         template <typename T>
00286         hold_any& operator=(T const& x)
00287         {
00288             return assign(x);
00289         }
00290 
00291         // utility functions
00292         hold_any& swap(hold_any& x)
00293         {
00294             std::swap(table, x.table);
00295             std::swap(object, x.object);
00296             return *this;
00297         }
00298 
00299         std::type_info const& type() const
00300         {
00301             return table->get_type();
00302         }
00303 
00304         template <typename T>
00305         T const& cast() const
00306         {
00307             if (type() != typeid(T))
00308               throw bad_any_cast(type(), typeid(T));
00309 
00310             return saga::detail::internals::get_table<T>::is_small::value ?
00311                 *reinterpret_cast<T const*>(&object) :
00312                 *reinterpret_cast<T const*>(object);
00313         }
00314 
00315 // implicit casting is disabled by default for compatibility with boost::any
00316 #ifdef BOOST_SPIRIT_ANY_IMPLICIT_CASTING
00317         // automatic casting operator
00318         template <typename T>
00319         operator T const& () const { return cast<T>(); }
00320 #endif // implicit casting
00321 
00322         bool empty() const
00323         {
00324             return 0 == object;
00325         }
00326 
00327         void reset()
00328         {
00329             if (!empty())
00330             {
00331                 table->static_delete(&object);
00332                 table = saga::detail::internals::get_table<saga::detail::internals::empty>::get();
00333                 object = 0;
00334             }
00335         }
00336 
00337 #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS
00338     private: // types
00339       template<typename T>
00340       friend T* any_cast(hold_any *);
00341       template<typename T>
00342       friend T* any_cast(hold_any *, boost::mpl::true_);
00343       template<typename T>
00344       friend T* any_cast(hold_any *, boost::mpl::false_);
00345 #else
00346     public: // types (public so any_cast can be non-friend)
00347 #endif
00348         // fields
00349         saga::detail::internals::fxn_ptr_table* table;
00350         void* object;
00351     };
00352 
00353     // boost::any-like casting
00354     template <typename T>
00355     inline T* any_cast (hold_any* operand, boost::mpl::true_)
00356     {
00357         if (operand && operand->type() == typeid(T)) {
00358             BOOST_STATIC_ASSERT(sizeof(std::size_t) >= sizeof(void*));
00359             return saga::detail::internals::get_table<T>::is_small::value ?
00360                 reinterpret_cast<T*>(
00361                   reinterpret_cast<std::size_t>(&operand->object)) :
00362                 reinterpret_cast<T*>(operand->object);
00363         }
00364         return 0;
00365     }
00366 
00367     template <typename T>
00368     inline T* any_cast (hold_any* operand, boost::mpl::false_)
00369     {
00370         if (operand) {
00371             if (operand->empty()) 
00372                 operand->init<T>();
00373             
00374             if (operand->type() == typeid(T)) {
00375                 BOOST_STATIC_ASSERT(sizeof(std::size_t) >= sizeof(void*));
00376                 return saga::detail::internals::get_table<T>::is_small::value ?
00377                     reinterpret_cast<T*>(
00378                        reinterpret_cast<std::size_t>(&operand->object)) :
00379                     reinterpret_cast<T*>(operand->object);
00380             }
00381         }
00382         return 0;
00383     }
00384 
00385     template <typename T>
00386     inline T* any_cast (hold_any* operand)
00387     {
00388         return any_cast<T>(operand, boost::mpl::bool_<boost::is_const<T>::value>());
00389     }
00390     
00391     template <typename T>
00392     inline T const* any_cast(hold_any const* operand)
00393     {
00394         return any_cast<T>(const_cast<hold_any*>(operand));
00395     }
00396 
00397     template <typename T>
00398     T any_cast(hold_any& operand)
00399     {
00400         typedef BOOST_DEDUCED_TYPENAME boost::remove_reference<T>::type nonref;
00401 
00402 #ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
00403         // If 'nonref' is still reference type, it means the user has not
00404         // specialized 'remove_reference'.
00405 
00406         // Please use BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION macro
00407         // to generate specialization of remove_reference for your class
00408         // See type traits library documentation for details
00409         BOOST_STATIC_ASSERT(!is_reference<nonref>::value);
00410 #endif
00411 
00412         nonref* result = any_cast<nonref>(&operand);
00413         if(!result)
00414             boost::throw_exception(bad_any_cast(operand.type(), typeid(T)));
00415         return *result;
00416     }
00417 
00418     template <typename T>
00419     T const& any_cast(hold_any const& operand)
00420     {
00421         typedef BOOST_DEDUCED_TYPENAME boost::remove_reference<T>::type nonref;
00422 
00423 #ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
00424         // The comment in the above version of 'any_cast' explains when this
00425         // assert is fired and what to do.
00426         BOOST_STATIC_ASSERT(!is_reference<nonref>::value);
00427 #endif
00428 
00429         return any_cast<nonref const&>(const_cast<hold_any &>(operand));
00430     }
00431 
00432 }}
00433 
00435 
00436 #endif
00437 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines