From 1f217e38f7507758da1d33d46e675e801621aa38 Mon Sep 17 00:00:00 2001 From: Michael Shepanski Date: Thu, 30 Oct 2014 15:21:32 +1100 Subject: Allow lambdas & std::functions as query factories with C++-98 builds of libodb --- odb/connection.cxx | 11 +-- odb/database.cxx | 32 ++++++++ odb/database.hxx | 38 ++++++--- odb/database.ixx | 27 +------ odb/details/function-wrapper.hxx | 83 ++++++++++++++++++++ odb/details/function-wrapper.ixx | 47 +++++++++++ odb/details/function-wrapper.txx | 87 +++++++++++++++++++++ odb/schema-catalog.cxx | 20 +++-- odb/schema-catalog.hxx | 164 ++++++++++++++++++++++++++++++++++----- 9 files changed, 441 insertions(+), 68 deletions(-) create mode 100644 odb/details/function-wrapper.hxx create mode 100644 odb/details/function-wrapper.ixx create mode 100644 odb/details/function-wrapper.txx diff --git a/odb/connection.cxx b/odb/connection.cxx index 4319f9f..14e89e2 100644 --- a/odb/connection.cxx +++ b/odb/connection.cxx @@ -96,16 +96,11 @@ namespace odb if (i == prepared_map_.end ()) { - // See if there is a factory. + // Use a factory, if there is one. // - database_type::query_factory_type f ( - database_.lookup_query_factory (name)); - - if (f) - { - f (name, const_cast (*this)); + if (database_.call_query_factory (name, + const_cast (*this))) i = prepared_map_.find (name); - } } if (i == prepared_map_.end ()) diff --git a/odb/database.cxx b/odb/database.cxx index 5578abd..0279acd 100644 --- a/odb/database.cxx +++ b/odb/database.cxx @@ -49,4 +49,36 @@ namespace odb schema_version_seq_++; } } + + bool database:: + call_query_factory (const char* name, connection_type& c) const + { + query_factory_map::const_iterator i (query_factory_map_.find (name)); + + if (i == query_factory_map_.end ()) + i = query_factory_map_.find (""); // Wildcard factory. + + if (i == query_factory_map_.end ()) + return false; + + const query_factory_wrapper& fw (i->second); + if (fw.std_function == 0) + fw.function (name, c); + else + { + typedef void (*caller) (const void*, const char*, connection_type&); + reinterpret_cast (fw.function) (fw.std_function, name, c); + } + + return true; + } + + void database:: + query_factory (const char* name, query_factory_wrapper w) + { + if (w) + query_factory_map_[name] = w; // Destructive copy assignment (move). + else + query_factory_map_.erase (name); + } } diff --git a/odb/database.hxx b/odb/database.hxx index 91d8e94..4e59aaf 100644 --- a/odb/database.hxx +++ b/odb/database.hxx @@ -15,7 +15,9 @@ #include // std::size_t #ifdef ODB_CXX11 -# include // std::function +# include // std::move +# include // std::function +# include // std::enable_if, std::is_convertible #endif #include @@ -30,6 +32,7 @@ #include #include #include +#include namespace odb { @@ -44,7 +47,6 @@ namespace odb // Object persistence API. // public: - // Make the object persistent. // template @@ -281,19 +283,31 @@ namespace odb public: typedef odb::connection connection_type; -#ifdef ODB_CXX11 - typedef - std::function - query_factory_type; + typedef void query_factory_type (const char* name, connection_type&); + typedef query_factory_type* query_factory_ptr; + typedef details::function_wrapper< + query_factory_type> query_factory_wrapper; + +#ifndef ODB_CXX11 + void + query_factory (const char* name, query_factory_ptr); #else - typedef void (*query_factory_type) (const char*, connection_type&); + template + typename std::enable_if< + std::is_convertible< + F, std::function>::value, void>::type + query_factory (const char* name, F f) + { + query_factory (name, query_factory_wrapper (std::move (f))); + } #endif - void - query_factory (const char* name, query_factory_type); + bool + call_query_factory (const char* name, connection_type&) const; - query_factory_type - lookup_query_factory (const char* name) const; + private: + void + query_factory (const char* name, query_factory_wrapper); // Native database statement execution. // @@ -480,7 +494,7 @@ namespace odb protected: typedef - std::map + std::map query_factory_map; typedef std::map schema_version_map; diff --git a/odb/database.ixx b/odb/database.ixx index a15bb7d..6cfbeec 100644 --- a/odb/database.ixx +++ b/odb/database.ixx @@ -3,7 +3,6 @@ // license : GNU GPL v2; see accompanying LICENSE file #include // std::strlen() -#include // std::move #include @@ -65,31 +64,13 @@ namespace odb return connection_ptr (connection_ ()); } +#ifndef ODB_CXX11 inline void database:: - query_factory (const char* name, query_factory_type f) + query_factory (const char* name, query_factory_ptr f) { - if (f) -#ifdef ODB_CXX11 - query_factory_map_[name] = std::move (f); -#else - query_factory_map_[name] = f; -#endif - else - query_factory_map_.erase (name); - } - - inline database::query_factory_type database:: - lookup_query_factory (const char* name) const - { - query_factory_map::const_iterator i (query_factory_map_.find (name)); - - if (i == query_factory_map_.end ()) - i = query_factory_map_.find (""); // Wildcard factory. - - return i != query_factory_map_.end () - ? i->second - : database::query_factory_type (); + query_factory (name, query_factory_wrapper (f)); } +#endif inline void database:: tracer (tracer_type& t) diff --git a/odb/details/function-wrapper.hxx b/odb/details/function-wrapper.hxx new file mode 100644 index 0000000..5615fca --- /dev/null +++ b/odb/details/function-wrapper.hxx @@ -0,0 +1,83 @@ +// file : odb/details/function-wrapper.hxx +// copyright : Copyright (c) 2009-2014 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_DETAILS_FUNCTION_WRAPPER_HXX +#define ODB_DETAILS_FUNCTION_WRAPPER_HXX + +#include + +#include // ODB_CXX11 + +#ifdef ODB_CXX11 +# include // std::function +# include // std::enable_if, std::is_convertible +#endif + +namespace odb +{ + namespace details + { + // Low-level 'callable object' wrapper similar to std::function but + // that works in both C++98 and 11. In particular, the call site can + // be compiled in C++98 and the registration site in C++11 and it + // will work. + // + template + struct function_wrapper + { + ~function_wrapper (); + + explicit + function_wrapper (F* = 0); + +#ifdef ODB_CXX11 + typedef typename std::function std_function_type; + + // This overload accepts lambdas and std::functions, but when the + // argument is convertible to F*, then we disable it in favor of the + // other overload (above), which is more efficient. + // + // Subtlety alert: if you're thinking of changing this to accept a + // std::function argument, stop. That creates an overload ambiguity + // when the actual parameter is a lambda, which is convertible to either + // std::function or F*. + // + template + function_wrapper(F1, + typename std::enable_if< + !std::is_convertible::value>::type* = 0); +#endif + + // Destructive copy construction and assignment (aka move). These + // should really only be called by containers when they need to + // reallocate the underlying buffer and move the elements. + // + function_wrapper (const function_wrapper&); + function_wrapper& + operator= (const function_wrapper&); + + void swap (function_wrapper&); + + // Conversion to bool. + // + public: + typedef void (function_wrapper::*bool_convertible) (); + void true_value () {} + + operator bool_convertible () const; + + public: + F* function; + void (*deleter) (const void*); + const void* std_function; + }; + } +} + +#include +#include + +#include + +#endif // ODB_DETAILS_FUNCTION_WRAPPER_HXX diff --git a/odb/details/function-wrapper.ixx b/odb/details/function-wrapper.ixx new file mode 100644 index 0000000..58ede04 --- /dev/null +++ b/odb/details/function-wrapper.ixx @@ -0,0 +1,47 @@ +// file : odb/details/function-wrapper.ixx +// copyright : Copyright (c) 2009-2014 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +namespace odb +{ + namespace details + { + template + inline function_wrapper:: + ~function_wrapper () + { + if (deleter != 0) + deleter (std_function); + } + + template + inline function_wrapper:: + function_wrapper (F* f) + : function (f), deleter (0), std_function (0) + { + } + + template + inline function_wrapper:: + function_wrapper (const function_wrapper& x) + : function (0), deleter (0), std_function (0) + { + swap (const_cast&> (x)); + } + + template + inline function_wrapper& function_wrapper:: + operator= (const function_wrapper& x) + { + swap (const_cast&> (x)); + return *this; + } + + template + inline function_wrapper:: + operator bool_convertible () const + { + return function != 0 ? &function_wrapper::true_value : 0; + } + } +} diff --git a/odb/details/function-wrapper.txx b/odb/details/function-wrapper.txx new file mode 100644 index 0000000..70dcb0f --- /dev/null +++ b/odb/details/function-wrapper.txx @@ -0,0 +1,87 @@ +// file : odb/details/function-wrapper.txx +// copyright : Copyright (c) 2009-2014 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include // std::swap, std::move + +namespace odb +{ + namespace details + { +#ifdef ODB_CXX11 + template + struct caller_impl; + +#ifdef ODB_CXX11_VARIADIC_TEMPLATE + template + struct caller_impl + { + static R + function (const void* f, A... a) + { + return (*static_cast*> (f)) (a...); + } + }; +#else + template + struct caller_impl + { + static R + function (const void* f, A1 a1) + { + return (*static_cast*> (f)) (a1); + } + }; + + template + struct caller_impl + { + static R + function (const void* f, A1 a1, A2 a2) + { + return (*static_cast*> (f)) (a1, a2); + } + }; +#endif + + template + void + deleter_impl (const void* f) + { + delete static_cast*> (f); + } + + template + template + function_wrapper:: + function_wrapper ( + F1 f, + typename std::enable_if::value>::type*) + { + std_function_type sf (std::move (f)); + + if (F* const* const f = sf.template target ()) + { + function = *f; + deleter = 0; + std_function = 0; + } + else + { + function = reinterpret_cast (&caller_impl::function); + deleter = &deleter_impl; + std_function = new std_function_type (std::move (sf)); + } + } +#endif + + template + void function_wrapper:: + swap (function_wrapper& x) + { + std::swap (function, x.function); + std::swap (deleter, x.deleter); + std::swap (std_function, x.std_function); + } + } +} diff --git a/odb/schema-catalog.cxx b/odb/schema-catalog.cxx index d223647..f2df4e2 100644 --- a/odb/schema-catalog.cxx +++ b/odb/schema-catalog.cxx @@ -38,13 +38,15 @@ namespace odb struct data_function { - typedef schema_catalog::data_migration_function_type function_type; + typedef schema_catalog::data_migration_function_wrapper + function_wrapper_type; data_function () {} - data_function (database_id i, function_type m): id (i), migrate (m) {} + data_function (database_id i, function_wrapper_type m) + : id (i), migrate (m) {} database_id id; - function_type migrate; + function_wrapper_type migrate; }; typedef vector data_functions; typedef map data_map; @@ -218,7 +220,15 @@ namespace odb { if (i->id == id_common || i->id == db.id ()) { - i->migrate (db); + const data_migration_function_wrapper &m = i->migrate; + + if (m.std_function == 0) + m.function (db); + else + { + typedef void (*caller) (const void*, database&); + reinterpret_cast (m.function) (m.std_function, db); + } r++; } } @@ -229,7 +239,7 @@ namespace odb void schema_catalog:: data_migration_function (database_id id, schema_version v, - data_migration_function_type f, + data_migration_function_wrapper f, const string& name) { // This function can be called from a static initializer in which diff --git a/odb/schema-catalog.hxx b/odb/schema-catalog.hxx index 7d675fe..510126a 100644 --- a/odb/schema-catalog.hxx +++ b/odb/schema-catalog.hxx @@ -13,7 +13,9 @@ #include // std::size_t #ifdef ODB_CXX11 -# include // std::function +# include // std::move +# include // std::function +# include // std::enable_if, std::is_convertible #endif #include // schema_version, odb::core @@ -74,11 +76,11 @@ namespace odb schema_version = 0, const std::string& name = ""); -#ifdef ODB_CXX11 - typedef std::function data_migration_function_type; -#else - typedef void (*data_migration_function_type) (database&); -#endif + typedef void data_migration_function_type (database&); + typedef data_migration_function_type* data_migration_function_ptr; + + typedef details::function_wrapper + data_migration_function_wrapper; // The following three variants of the registration functions make // sure that the version is greater that the base model version. @@ -87,38 +89,59 @@ namespace odb // // Data migration functions are called in the order of registration. // +#ifndef ODB_CXX11 template static void - data_migration_function (data_migration_function_type f, + data_migration_function (data_migration_function_ptr f, const std::string& name = "") { data_migration_function (id_common, f, name); } +#else + template + static typename std::enable_if< + std::is_convertible< + F, std::function>::value, void>::type + data_migration_function (F f, const std::string& name = "") + { + data_migration_function (id_common, std::move (f), name); + } +#endif + // Database-specific data migration. // +#ifndef ODB_CXX11 template static void data_migration_function (database& db, - data_migration_function_type f, + data_migration_function_ptr f, const std::string& name = "") { data_migration_function (db.id (), f, name); } +#else + template + static typename std::enable_if< + std::is_convertible< + F, std::function>::value, void>::type + data_migration_function (database& db, F f, const std::string& name = "") + { + data_migration_function (db.id (), std::move (f), name); + } +#endif +#ifndef ODB_CXX11 template static void data_migration_function (database_id id, - data_migration_function_type f, + data_migration_function_ptr f, const std::string& name = "") { // If the data migration version is below the base model version // then it will never be called. // -#ifdef ODB_CXX11 - static_assert (v > base || base == 0, - "data migration function is no longer necessary"); -#else + // Poor man's static_assert. // typedef details::meta::static_assert_test<(v > base || base == 0)> @@ -126,36 +149,109 @@ namespace odb char sa [sizeof (data_migration_function_is_no_longer_necessary)]; ODB_POTENTIALLY_UNUSED (sa); -#endif data_migration_function (id, v, f, name); } +#else + template + static typename std::enable_if< + std::is_convertible< + F, std::function>::value, void>::type + data_migration_function (database_id id, F f, const std::string& name = "") + { + // If the data migration version is below the base model version + // then it will never be called. + // + static_assert (v > base || base == 0, + "data migration function is no longer necessary"); + + data_migration_function (id, v, std::move (f), name); + } +#endif // The same as above but take the version as an argument and do // not check whether it is greater than the base model version. // +#ifndef ODB_CXX11 static void data_migration_function (schema_version v, - data_migration_function_type f, + data_migration_function_ptr f, const std::string& name = "") { data_migration_function (id_common, v, f, name); } +#else + template + static typename std::enable_if< + std::is_convertible< + F, std::function>::value, void>::type + data_migration_function (schema_version v, + F f, + const std::string& name = "") + { + data_migration_function (id_common, v, std::move (f), name); + } +#endif +#ifndef ODB_CXX11 static void data_migration_function (database& db, schema_version v, - data_migration_function_type f, + data_migration_function_ptr f, const std::string& name = "") { data_migration_function (db.id (), v, f, name); } +#else + template + static typename std::enable_if< + std::is_convertible< + F, std::function>::value, void>::type + data_migration_function (database& db, + schema_version v, + F f, + const std::string& name = "") + { + data_migration_function (db.id (), v, std::move (f), name); + } +#endif + +#ifndef ODB_CXX11 + static void + data_migration_function (database_id i, + schema_version v, + data_migration_function_ptr f, + const std::string& name = "") + { + data_migration_function (i, + v, + data_migration_function_wrapper (f), + name); + } +#else + template + static typename std::enable_if< + std::is_convertible< + F, std::function>::value, void>::type + data_migration_function (database_id i, + schema_version v, + F f, + const std::string& name = "") + { + data_migration_function ( + i, + v, + data_migration_function_wrapper (std::move (f)), + name); + } +#endif + private: static void data_migration_function (database_id, schema_version, - data_migration_function_type, - const std::string& name = ""); + data_migration_function_wrapper, + const std::string& name); // Combined schema and data migration. // @@ -244,17 +340,45 @@ namespace odb { typedef schema_catalog::data_migration_function_type function_type; - data_migration_entry (function_type f, const std::string& name = "") +#ifndef ODB_CXX11 + data_migration_entry (function_type* f, const std::string& name = "") { schema_catalog::data_migration_function (f, name); } +#else + template + data_migration_entry (F f, + const std::string& name = "", + typename std::enable_if>::value> + ::type* = 0) + { + schema_catalog::data_migration_function (std::move (f), name); + } +#endif +#ifndef ODB_CXX11 data_migration_entry (database_id id, - function_type f, + function_type *f, const std::string& name = "") { schema_catalog::data_migration_function (id, v, f, name); } +#else + template + data_migration_entry (database_id id, + F f, + const std::string& name = "", + typename std::enable_if>::value> + ::type* = 0) + { + schema_catalog::data_migration_function (id, + v, + std::move (f), + name); + } +#endif }; namespace common -- cgit v1.1