From be97326d67365e16175cc599e23348feaf80e0fe Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 8 Oct 2012 16:09:07 +0200 Subject: Ground work for multi-database support All generated code now includes database id. The database-specific database class interface has been updated to include all the database operations. The database-specific tests now use this interface. --- odb/database.hxx | 61 ++++++++++-- odb/database.ixx | 228 ++++++++++++++++++++++++++++---------------- odb/database.txx | 99 +++++++++---------- odb/forward.hxx | 35 ++++++- odb/polymorphic-info.hxx | 6 +- odb/polymorphic-map.hxx | 33 +++---- odb/polymorphic-map.ixx | 12 +-- odb/polymorphic-map.txx | 8 +- odb/query.hxx | 37 +++---- odb/schema-catalog-impl.hxx | 3 +- odb/schema-catalog.cxx | 11 ++- odb/traits.hxx | 77 +++++++++++---- 12 files changed, 398 insertions(+), 212 deletions(-) diff --git a/odb/database.hxx b/odb/database.hxx index 64f9d44..99205e7 100644 --- a/odb/database.hxx +++ b/odb/database.hxx @@ -63,7 +63,7 @@ namespace odb typename object_traits::id_type persist (const typename object_traits::pointer_type& obj_ptr); - // Throw object_not_persistent if not found. + // Load an object. Throw object_not_persistent if not found. // template typename object_traits::pointer_type @@ -73,6 +73,8 @@ namespace odb void load (const typename object_traits::id_type& id, T& object); + // Reload an object. + // template void reload (T& object); @@ -101,7 +103,7 @@ namespace odb void reload (const typename object_traits::pointer_type& obj_ptr); - // Return NULL/false if not found. + // Loan an object if found. Return NULL/false if not found. // template typename object_traits::pointer_type @@ -250,8 +252,14 @@ namespace odb tracer_type* tracer () const; + // Database id. + // + public: + database_id + id () const; + protected: - database (); + database (database_id); private: database (const database&); @@ -264,22 +272,61 @@ namespace odb connection_ () = 0; protected: - template + template + typename object_traits::id_type + persist_ (T&); + + template typename object_traits::id_type persist_ (const typename object_traits::pointer_type&); - template + template + typename object_traits::pointer_type + load_ (const typename object_traits::id_type&); + + template + void + load_ (const typename object_traits::id_type&, T&); + + template + void + reload_ (T&); + + template + typename object_traits::pointer_type + find_ (const typename object_traits::id_type&); + + template + bool + find_ (const typename object_traits::id_type&, T&); + + template + void + update_ (T&); + + template void update_ (const typename object_traits::pointer_type&); - template + template + void + erase_ (const typename object_traits::id_type&); + + template + void + erase_ (T&); + + template void erase_ (const typename object_traits::pointer_type&); - template + template ::kind> struct query_; protected: + database_id id_; tracer_type* tracer_; }; } diff --git a/odb/database.ixx b/odb/database.ixx index 4a00a74..aeffdec 100644 --- a/odb/database.ixx +++ b/odb/database.ixx @@ -7,11 +7,17 @@ namespace odb { inline database:: - database () - : tracer_ (0) + database (database_id id) + : id_ (id), tracer_ (0) { } + inline database_id database:: + id () const + { + return id_; + } + inline connection_ptr database:: connection () { @@ -38,6 +44,13 @@ namespace odb template inline typename object_traits::id_type database:: + persist (T& obj) + { + return persist_ (obj); + } + + template + inline typename object_traits::id_type database:: persist (T* p) { typedef typename object_traits::pointer_type object_pointer; @@ -48,7 +61,7 @@ namespace odb // const object_pointer& pobj (p); - return persist_ (pobj); + return persist_ (pobj); } template class P> @@ -63,7 +76,7 @@ namespace odb // const object_pointer& pobj (p); - return persist_ (pobj); + return persist_ (pobj); } template class P> @@ -78,7 +91,7 @@ namespace odb // const object_pointer& pobj (p); - return persist_ (pobj); + return persist_ (pobj); } template class P> @@ -101,29 +114,42 @@ namespace odb inline typename object_traits::id_type database:: persist (const typename object_traits::pointer_type& pobj) { - return persist_ (pobj); + return persist_ (pobj); } template inline typename object_traits::pointer_type database:: - find (const typename object_traits::id_type& id) + load (const typename object_traits::id_type& id) { - // T is always object_type. - // + return load_ (id); + } - // Compiler error pointing here? Perhaps the object doesn't have the - // default constructor? - // - return object_traits::find (*this, id); + template + inline void database:: + load (const typename object_traits::id_type& id, T& obj) + { + return load_ (id, obj); + } + + template + inline typename object_traits::pointer_type database:: + find (const typename object_traits::id_type& id) + { + return find_ (id); } template inline bool database:: find (const typename object_traits::id_type& id, T& obj) { - // T is always object_type. - // - return object_traits::find (*this, id, obj); + return find_ (id, obj); + } + + template + inline void database:: + reload (T& obj) + { + reload_ (obj); } template @@ -172,6 +198,13 @@ namespace odb template inline void database:: + update (T& obj) + { + update_ (obj); + } + + template + inline void database:: update (T* p) { typedef typename object_traits::pointer_type object_pointer; @@ -182,7 +215,7 @@ namespace odb // const object_pointer& pobj (p); - update_ (pobj); + update_ (pobj); } template class P> @@ -197,7 +230,7 @@ namespace odb // const object_pointer& pobj (p); - update_ (pobj); + update_ (pobj); } template class P> @@ -212,7 +245,7 @@ namespace odb // const object_pointer& pobj (p); - update_ (pobj); + update_ (pobj); } template class P> @@ -235,42 +268,21 @@ namespace odb inline void database:: update (const typename object_traits::pointer_type& pobj) { - update_ (pobj); + update_ (pobj); } template inline void database:: - update (T& obj) + erase (const typename object_traits::id_type& id) { - // T can be const T while object_type will always be T. - // - typedef typename odb::object_traits::object_type object_type; - typedef odb::object_traits object_traits; - - // Compiler error pointing here? Perhaps the object is readonly or - // doesn't have an object id? Such objects cannot be updated. - // - object_traits::update (*this, obj); + return erase_ (id); } template inline void database:: - update_ (const typename object_traits::pointer_type& pobj) + erase (T& obj) { - // T can be const T while object_type will always be T. - // - typedef typename odb::object_traits::object_type object_type; - typedef odb::object_traits object_traits; - - typedef typename odb::object_traits::pointer_type pointer_type; - typedef odb::pointer_traits pointer_traits; - - T& obj (pointer_traits::get_ref (pobj)); - - // Compiler error pointing here? Perhaps the object is readonly or - // doesn't have an object id? Such objects cannot be updated. - // - object_traits::update (*this, obj); + return erase_ (obj); } template @@ -285,7 +297,7 @@ namespace odb // const object_pointer& pobj (p); - erase_ (pobj); + erase_ (pobj); } template class P> @@ -300,7 +312,7 @@ namespace odb // const object_pointer& pobj (p); - erase_ (pobj); + erase_ (pobj); } template class P> @@ -315,7 +327,7 @@ namespace odb // const object_pointer& pobj (p); - erase_ (pobj); + erase_ (pobj); } template class P> @@ -338,37 +350,7 @@ namespace odb inline void database:: erase (const typename object_traits::pointer_type& pobj) { - erase_ (pobj); - } - - template - inline void database:: - erase_ (const typename object_traits::pointer_type& pobj) - { - typedef typename object_traits::pointer_type pointer_type; - typedef pointer_traits pointer_traits; - - erase (pointer_traits::get_ref (pobj)); - } - - template - inline void database:: - erase (const typename object_traits::id_type& id) - { - // T is always object_type. - // - object_traits::erase (*this, id); - } - - template - inline void database:: - erase (T& obj) - { - // T can be const T while object_type will always be T. - // - typedef typename object_traits::object_type object_type; - - object_traits::erase (*this, obj); + erase_ (pobj); } template @@ -404,7 +386,7 @@ namespace odb { // T is always object_type. // - return object_traits::erase_query (*this, q); + return object_traits_impl::erase_query (*this, q); } template @@ -428,6 +410,92 @@ namespace odb return query (odb::query (q), cache); } + // Implementations (i.e., the *_() functions). + // + template + inline typename object_traits::pointer_type database:: + find_ (const typename object_traits::id_type& id) + { + // T is always object_type. + // + + // Compiler error pointing here? Perhaps the object doesn't have the + // default constructor? + // + return object_traits_impl::find (*this, id); + } + + template + inline bool database:: + find_ (const typename object_traits::id_type& id, T& obj) + { + // T is always object_type. + // + return object_traits_impl::find (*this, id, obj); + } + + template + inline void database:: + update_ (T& obj) + { + // T can be const T while object_type will always be T. + // + typedef typename object_traits::object_type object_type; + + // Compiler error pointing here? Perhaps the object is readonly or + // doesn't have an object id? Such objects cannot be updated. + // + object_traits_impl::update (*this, obj); + } + + template + inline void database:: + update_ (const typename object_traits::pointer_type& pobj) + { + // T can be const T while object_type will always be T. + // + typedef typename object_traits::object_type object_type; + typedef typename object_traits::pointer_type pointer_type; + + T& obj (pointer_traits::get_ref (pobj)); + + // Compiler error pointing here? Perhaps the object is readonly or + // doesn't have an object id? Such objects cannot be updated. + // + object_traits_impl::update (*this, obj); + } + + template + inline void database:: + erase_ (const typename object_traits::id_type& id) + { + // T is always object_type. + // + object_traits_impl::erase (*this, id); + } + + template + inline void database:: + erase_ (T& obj) + { + // T can be const T while object_type will always be T. + // + typedef typename object_traits::object_type object_type; + + object_traits_impl::erase (*this, obj); + } + + template + inline void database:: + erase_ (const typename object_traits::pointer_type& pobj) + { + typedef typename object_traits::pointer_type pointer_type; + + erase_ (pointer_traits::get_ref (pobj)); + } + + // execute() + // inline unsigned long long database:: execute (const char* statement) { diff --git a/odb/database.txx b/odb/database.txx index 7ac19d7..23ea712 100644 --- a/odb/database.txx +++ b/odb/database.txx @@ -10,13 +10,30 @@ namespace odb { template + result database:: + query (const odb::query& q, bool cache) + { + // T is always object_type. We also don't need to check for transaction + // here; object_traits::query () does this. + // + result r (query_::call (*this, q)); + + if (cache) + r.cache (); + + return r; + } + + // Implementations (i.e., the *_() functions). + // + template typename object_traits::id_type database:: - persist (T& obj) + persist_ (T& obj) { // T can be const T while object_type will always be T. // - typedef typename odb::object_traits::object_type object_type; - typedef odb::object_traits object_traits; + typedef typename object_traits::object_type object_type; + typedef object_traits_impl object_traits; object_traits::persist (*this, obj); @@ -26,19 +43,18 @@ namespace odb return object_traits::id (obj); } - template + template typename object_traits::id_type database:: persist_ (const typename object_traits::pointer_type& pobj) { // T can be const T while object_type will always be T. // - typedef typename odb::object_traits::object_type object_type; - typedef odb::object_traits object_traits; + typedef typename object_traits::object_type object_type; + typedef typename object_traits::pointer_type pointer_type; - typedef typename odb::object_traits::pointer_type pointer_type; - typedef odb::pointer_traits pointer_traits; + typedef object_traits_impl object_traits; - T& obj (pointer_traits::get_ref (pobj)); + T& obj (pointer_traits::get_ref (pobj)); object_traits::persist (*this, obj); // Get the canonical object pointer and insert it into object cache. @@ -49,81 +65,60 @@ namespace odb return object_traits::id (obj); } - template + template typename object_traits::pointer_type database:: - load (const typename object_traits::id_type& id) + load_ (const typename object_traits::id_type& id) { // T is always object_type. // typedef typename object_traits::pointer_type pointer_type; - typedef odb::pointer_traits pointer_traits; - pointer_type r (find (id)); + pointer_type r (find_ (id)); - if (pointer_traits::null_ptr (r)) + if (pointer_traits::null_ptr (r)) throw object_not_persistent (); return r; } - template + template void database:: - load (const typename object_traits::id_type& id, T& obj) + load_ (const typename object_traits::id_type& id, T& obj) { - if (!find (id, obj)) + if (!find_ (id, obj)) throw object_not_persistent (); } - template + template void database:: - reload (T& obj) + reload_ (T& obj) { - // T should be object_type (cannot be const). + // T should be object_type (cannot be const). We also don't need to + // check for transaction here; object_traits::reload () does this. // - typedef odb::object_traits object_traits; - - // We don't need to check for transaction here; - // object_traits::reload () does this. - - if (!object_traits::reload (*this, obj)) + if (!object_traits_impl::reload (*this, obj)) throw object_not_persistent (); } - template - struct database::query_ + template + struct database::query_ { + template static result - call (database& db, const odb::query& q) + call (database& db, const Q& q) { - return object_traits::query (db, q); + return object_traits_impl::query (db, q); } }; - template - struct database::query_ + template + struct database::query_ { + template static result - call (database& db, const odb::query& q) + call (database& db, const Q& q) { - return view_traits::query (db, q); + return view_traits_impl::query (db, q); } }; - - template - result database:: - query (const odb::query& q, bool cache) - { - // T is always object_type. - // - - // We don't need to check for transaction here; - // object_traits::query () does this. - - result r (query_::kind>::call (*this, q)); - - if (cache) - r.cache (); - - return r; - } } diff --git a/odb/forward.hxx b/odb/forward.hxx index 4c5e4b9..ff4a531 100644 --- a/odb/forward.hxx +++ b/odb/forward.hxx @@ -7,6 +7,8 @@ #include +#include // std::size_t + #include #include @@ -42,25 +44,50 @@ namespace odb // Implementation details. // + // Keep real databases first since their enumerators are used as array + // indexes + // + enum database_id + { + id_mysql, + id_sqlite, + id_pgsql, + id_oracle, + id_mssql, + id_default + }; + + // Number of real databases in the database_id enum. + // + const std::size_t database_count = id_default; + + // Traits. + // class access { public: template class object_traits; + template + class object_traits_impl; + template class object_factory; template class view_traits; + template + class view_traits_impl; + template class view_factory; template class pointer_factory; - template + template class composite_value_traits; template @@ -70,9 +97,15 @@ namespace odb template struct object_traits; + template + struct object_traits_impl; + template struct view_traits; + template + struct view_traits_impl; + // Cache traits. // template struct no_id_pointer_cache_traits; diff --git a/odb/polymorphic-info.hxx b/odb/polymorphic-info.hxx index abed648..54291ce 100644 --- a/odb/polymorphic-info.hxx +++ b/odb/polymorphic-info.hxx @@ -86,14 +86,12 @@ namespace odb // Register concrete type T in the root's map. // - template + template struct polymorphic_entry { typedef T object_type; - typedef odb::object_traits object_traits; - + typedef object_traits_impl object_traits; typedef typename object_traits::root_type root_type; - typedef odb::object_traits root_traits; polymorphic_entry (); ~polymorphic_entry (); diff --git a/odb/polymorphic-map.hxx b/odb/polymorphic-map.hxx index c3c1374..5d23e6f 100644 --- a/odb/polymorphic-map.hxx +++ b/odb/polymorphic-map.hxx @@ -66,11 +66,11 @@ namespace odb discriminator_map discriminator_map_; }; - template + template struct polymorphic_entry_impl { typedef R root_type; - typedef object_traits root_traits; + typedef object_traits_impl root_traits; typedef polymorphic_concrete_info info_type; static void @@ -103,18 +103,18 @@ namespace odb return r; } - template + template struct dispatch_load { static void call (database& db, T& obj, std::size_t d) { - object_traits::load_ (db, obj, d); + object_traits_impl::load_ (db, obj, d); } }; - template - struct dispatch_load + template + struct dispatch_load { static void call (database&, R&, std::size_t) @@ -123,7 +123,7 @@ namespace odb } }; - template + template struct dispatch_persist { static void @@ -131,23 +131,24 @@ namespace odb { // Top-level call, no dynamic type checking. // - object_traits::persist (db, obj, true, false); + object_traits_impl::persist (db, obj, true, false); } }; - template - struct dispatch_persist + template + struct dispatch_persist { static void call (database& db, const T& obj) { // Top-level call, no dynamic type checking. // - object_traits::persist (db, const_cast (obj), true, false); + object_traits_impl::persist ( + db, const_cast (obj), true, false); } }; - template + template bool dispatch_impl ( typename polymorphic_concrete_info< typename object_traits::root_type>::call_type c, @@ -155,9 +156,9 @@ namespace odb const typename object_traits::root_type* pobj, const void* arg) { - typedef object_traits derived_traits; + typedef object_traits_impl derived_traits; typedef typename derived_traits::root_type root_type; - typedef object_traits root_traits; + typedef object_traits_impl root_traits; typedef typename root_traits::id_type id_type; typedef polymorphic_concrete_info info_type; @@ -175,7 +176,7 @@ namespace odb } case info_type::call_persist: { - dispatch_persist::call ( + dispatch_persist::call ( db, *static_cast (pobj)); break; @@ -208,7 +209,7 @@ namespace odb } case info_type::call_load: { - dispatch_load::call ( + dispatch_load::call ( db, *const_cast (static_cast (pobj)), *static_cast (arg)); diff --git a/odb/polymorphic-map.ixx b/odb/polymorphic-map.ixx index 4dbd174..546d997 100644 --- a/odb/polymorphic-map.ixx +++ b/odb/polymorphic-map.ixx @@ -4,17 +4,17 @@ namespace odb { - template - inline polymorphic_entry:: + template + inline polymorphic_entry:: polymorphic_entry () { - polymorphic_entry_impl::insert (object_traits::info); + polymorphic_entry_impl::insert (object_traits::info); } - template - inline polymorphic_entry:: + template + inline polymorphic_entry:: ~polymorphic_entry () { - polymorphic_entry_impl::erase (object_traits::info); + polymorphic_entry_impl::erase (object_traits::info); } } diff --git a/odb/polymorphic-map.txx b/odb/polymorphic-map.txx index d7b6e36..26534a6 100644 --- a/odb/polymorphic-map.txx +++ b/odb/polymorphic-map.txx @@ -39,8 +39,8 @@ namespace odb // polymorphic_entry_impl // - template - void polymorphic_entry_impl:: + template + void polymorphic_entry_impl:: insert (const info_type& i) { // VC10 cannot grok constructor call syntax here. @@ -56,8 +56,8 @@ namespace odb pm->discriminator_map_[&i.discriminator] = &i; } - template - void polymorphic_entry_impl:: + template + void polymorphic_entry_impl:: erase (const info_type& i) { // VC10 cannot grok constructor call syntax here. diff --git a/odb/query.hxx b/odb/query.hxx index 9f889db..d5d0a19 100644 --- a/odb/query.hxx +++ b/odb/query.hxx @@ -7,6 +7,7 @@ #include +#include #include namespace odb @@ -21,16 +22,16 @@ namespace odb // we need straight tables instead of aliases. // // - template + template struct alias_traits; - template + template struct query_columns_base; - template + template struct query_columns; - template + template struct pointer_query_columns; // Object pointer syntax wrapper. @@ -56,29 +57,33 @@ namespace odb // we have to use the impl trick below instead of simply having kind // as a second template argument with a default value. // - template + template struct query_selector_impl; - template - struct query_selector_impl + template + struct query_selector_impl { - typedef typename object_traits::query_base_type base_type; - typedef query_columns > columns_type; + typedef typename object_traits_impl::query_base_type base_type; + + typedef + query_columns > + columns_type; }; - template - struct query_selector_impl + template + struct query_selector_impl { - typedef typename view_traits::query_base_type base_type; - typedef typename view_traits::query_columns columns_type; + typedef typename view_traits_impl::query_base_type base_type; + typedef typename view_traits_impl::query_columns columns_type; }; - template - struct query_selector: query_selector_impl::kind> + template + struct query_selector: query_selector_impl::kind> { }; - template ::base_type> + template ::base_type> class query; namespace core diff --git a/odb/schema-catalog-impl.hxx b/odb/schema-catalog-impl.hxx index 71d5d89..9b94cf1 100644 --- a/odb/schema-catalog-impl.hxx +++ b/odb/schema-catalog-impl.hxx @@ -35,8 +35,9 @@ namespace odb struct LIBODB_EXPORT schema_catalog_entry { schema_catalog_entry ( + database_id id, const char* name, - bool (*entry) (database&, unsigned short pass, bool drop)); + bool (*create_function) (database&, unsigned short pass, bool drop)); }; } diff --git a/odb/schema-catalog.cxx b/odb/schema-catalog.cxx index 7f00c3c..cd1ded4 100644 --- a/odb/schema-catalog.cxx +++ b/odb/schema-catalog.cxx @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -19,8 +20,9 @@ namespace odb // for example, for foreign key generation. // typedef bool (*create_function) (database&, unsigned short pass, bool drop); + typedef pair key; typedef vector create_functions; - struct schema_catalog_impl: map {}; + struct schema_catalog_impl: map {}; schema_catalog_impl* schema_catalog_init::catalog = 0; size_t schema_catalog_init::count = 0; @@ -29,8 +31,7 @@ namespace odb create_schema (database& db, const string& name) { const schema_catalog_impl& c (*schema_catalog_init::catalog); - - schema_catalog_impl::const_iterator i (c.find (name)); + schema_catalog_impl::const_iterator i (c.find (key (db.id (), name))); if (i == c.end ()) throw unknown_schema (name); @@ -94,9 +95,9 @@ namespace odb // schema_catalog_entry // schema_catalog_entry:: - schema_catalog_entry (const char* name, create_function entry) + schema_catalog_entry (database_id id, const char* name, create_function cf) { schema_catalog_impl& c (*schema_catalog_init::catalog); - c[name].push_back (entry); + c[key(id, name)].push_back (cf); } } diff --git a/odb/traits.hxx b/odb/traits.hxx index 0743dba..3711601 100644 --- a/odb/traits.hxx +++ b/odb/traits.hxx @@ -99,17 +99,16 @@ namespace odb // template + // + // If a C++ compiler issues an error pointing to this struct and + // saying that it is incomplete, then you are most likely trying to + // perform a database operation on a C++ type that is not a persistent + // object. Or you forgot to include the corresponding -odb.hxx file. + // struct object_traits: access::object_traits, access::object_factory::pointer_type> { - // - // If a C++ compiler issues an error pointing to this struct and - // saying that it is incomplete, then you are most likely trying to - // perform a database operation on a C++ type that is not a persistent - // object. Or you forgot to include the corresponding -odb.hxx file. - // - typedef odb::pointer_traits::pointer_type> pointer_traits; @@ -160,45 +159,64 @@ namespace odb }; template class P> - struct object_traits< P > + struct object_traits > { struct id_type {}; }; template class P> - struct object_traits< P > + struct object_traits > { struct id_type {}; }; template class P> - struct object_traits< const P > + struct object_traits > { struct id_type {}; }; template class P> - struct object_traits< const P > + struct object_traits > { struct id_type {}; }; + template + // + // If a C++ compiler issues an error pointing to this struct and + // saying that it is incomplete, then you are most likely trying to + // perform a database operation on a C++ type that is not a persistent + // object. Or you forgot to include the corresponding -odb.hxx file. + // + struct object_traits_impl: + access::object_traits_impl, + access::object_factory::pointer_type> + { + typedef + odb::pointer_traits::pointer_type> + pointer_traits; + + typedef typename access::object_traits::object_type object_type; + typedef typename access::object_traits::pointer_type pointer_type; + typedef typename pointer_traits::const_pointer_type const_pointer_type; + }; + // // view_traits // template + // + // If a C++ compiler issues an error pointing to this struct and + // saying that it is incomplete, then you are most likely trying to + // perform a database operation on a C++ type that is not a view + // Or you forgot to include the corresponding -odb.hxx file. + // struct view_traits: access::view_traits, access::view_factory::pointer_type> { - // - // If a C++ compiler issues an error pointing to this struct and - // saying that it is incomplete, then you are most likely trying to - // perform a database operation on a C++ type that is not a view - // Or you forgot to include the corresponding -odb.hxx file. - // - typedef odb::pointer_traits::pointer_type> pointer_traits; @@ -227,12 +245,31 @@ namespace odb typedef const_pointer_type pointer_type; }; + template + // + // If a C++ compiler issues an error pointing to this struct and + // saying that it is incomplete, then you are most likely trying to + // perform a database operation on a C++ type that is not a view + // Or you forgot to include the corresponding -odb.hxx file. + // + struct view_traits_impl: + access::view_traits_impl, + access::view_factory::pointer_type> + { + typedef + odb::pointer_traits::pointer_type> + pointer_traits; + + typedef typename access::view_traits::view_type view_type; + typedef typename access::view_traits::pointer_type pointer_type; + }; + // // composite_value_traits // - template - struct composite_value_traits: access::composite_value_traits + template + struct composite_value_traits: access::composite_value_traits { }; } -- cgit v1.1