aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Shepanski <michael@codesynthesis.com>2014-10-30 15:21:32 +1100
committerMichael Shepanski <michael@codesynthesis.com>2014-10-31 08:39:49 +1100
commit1f217e38f7507758da1d33d46e675e801621aa38 (patch)
treebad382ee315a820ec08bd2a83eaece12ac8169e3
parent31af5a488f2be9f4059e159492d4fe62f25a895a (diff)
Allow lambdas & std::functions as query factories with C++-98 builds of libodb
-rw-r--r--odb/connection.cxx11
-rw-r--r--odb/database.cxx32
-rw-r--r--odb/database.hxx38
-rw-r--r--odb/database.ixx27
-rw-r--r--odb/details/function-wrapper.hxx83
-rw-r--r--odb/details/function-wrapper.ixx47
-rw-r--r--odb/details/function-wrapper.txx87
-rw-r--r--odb/schema-catalog.cxx20
-rw-r--r--odb/schema-catalog.hxx164
9 files changed, 441 insertions, 68 deletions
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<connection&> (*this));
+ if (database_.call_query_factory (name,
+ const_cast<connection&> (*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<caller> (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 <cstddef> // std::size_t
#ifdef ODB_CXX11
-# include <functional> // std::function
+# include <utility> // std::move
+# include <functional> // std::function
+# include <type_traits> // std::enable_if, std::is_convertible
#endif
#include <odb/traits.hxx>
@@ -30,6 +32,7 @@
#include <odb/details/export.hxx>
#include <odb/details/mutex.hxx>
#include <odb/details/c-string.hxx>
+#include <odb/details/function-wrapper.hxx>
namespace odb
{
@@ -44,7 +47,6 @@ namespace odb
// Object persistence API.
//
public:
-
// Make the object persistent.
//
template <typename T>
@@ -281,19 +283,31 @@ namespace odb
public:
typedef odb::connection connection_type;
-#ifdef ODB_CXX11
- typedef
- std::function<void (const char*, connection_type&)>
- 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 F>
+ typename std::enable_if<
+ std::is_convertible<
+ F, std::function<query_factory_type>>::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<const char*, query_factory_type, details::c_string_comparator>
+ std::map<const char*, query_factory_wrapper, details::c_string_comparator>
query_factory_map;
typedef std::map<std::string, schema_version_info> 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 <cstring> // std::strlen()
-#include <utility> // std::move
#include <odb/transaction.hxx>
@@ -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 <odb/pre.hxx>
+
+#include <odb/details/config.hxx> // ODB_CXX11
+
+#ifdef ODB_CXX11
+# include <functional> // std::function
+# include <type_traits> // 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 <typename F>
+ struct function_wrapper
+ {
+ ~function_wrapper ();
+
+ explicit
+ function_wrapper (F* = 0);
+
+#ifdef ODB_CXX11
+ typedef typename std::function<F> 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<F> argument, stop. That creates an overload ambiguity
+ // when the actual parameter is a lambda, which is convertible to either
+ // std::function<F> or F*.
+ //
+ template <typename F1>
+ function_wrapper(F1,
+ typename std::enable_if<
+ !std::is_convertible<F1, F*>::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<F>&);
+ function_wrapper&
+ operator= (const function_wrapper<F>&);
+
+ void swap (function_wrapper<F>&);
+
+ // Conversion to bool.
+ //
+ public:
+ typedef void (function_wrapper<F>::*bool_convertible) ();
+ void true_value () {}
+
+ operator bool_convertible () const;
+
+ public:
+ F* function;
+ void (*deleter) (const void*);
+ const void* std_function;
+ };
+ }
+}
+
+#include <odb/details/function-wrapper.ixx>
+#include <odb/details/function-wrapper.txx>
+
+#include <odb/post.hxx>
+
+#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 <typename F>
+ inline function_wrapper<F>::
+ ~function_wrapper ()
+ {
+ if (deleter != 0)
+ deleter (std_function);
+ }
+
+ template <typename F>
+ inline function_wrapper<F>::
+ function_wrapper (F* f)
+ : function (f), deleter (0), std_function (0)
+ {
+ }
+
+ template <typename F>
+ inline function_wrapper<F>::
+ function_wrapper (const function_wrapper<F>& x)
+ : function (0), deleter (0), std_function (0)
+ {
+ swap (const_cast<function_wrapper<F>&> (x));
+ }
+
+ template <typename F>
+ inline function_wrapper<F>& function_wrapper<F>::
+ operator= (const function_wrapper<F>& x)
+ {
+ swap (const_cast<function_wrapper<F>&> (x));
+ return *this;
+ }
+
+ template <typename F>
+ inline function_wrapper<F>::
+ operator bool_convertible () const
+ {
+ return function != 0 ? &function_wrapper<F>::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 <utility> // std::swap, std::move
+
+namespace odb
+{
+ namespace details
+ {
+#ifdef ODB_CXX11
+ template <typename F>
+ struct caller_impl;
+
+#ifdef ODB_CXX11_VARIADIC_TEMPLATE
+ template <typename R, typename... A>
+ struct caller_impl<R (A...)>
+ {
+ static R
+ function (const void* f, A... a)
+ {
+ return (*static_cast<const std::function<R (A...)>*> (f)) (a...);
+ }
+ };
+#else
+ template <typename R, typename A1>
+ struct caller_impl<R (A1)>
+ {
+ static R
+ function (const void* f, A1 a1)
+ {
+ return (*static_cast<const std::function<R (A1)>*> (f)) (a1);
+ }
+ };
+
+ template <typename R, typename A1, typename A2>
+ struct caller_impl<R (A1, A2)>
+ {
+ static R
+ function (const void* f, A1 a1, A2 a2)
+ {
+ return (*static_cast<const std::function<R (A1, A2)>*> (f)) (a1, a2);
+ }
+ };
+#endif
+
+ template <typename F>
+ void
+ deleter_impl (const void* f)
+ {
+ delete static_cast<const std::function<F>*> (f);
+ }
+
+ template <typename F>
+ template <typename F1>
+ function_wrapper<F>::
+ function_wrapper (
+ F1 f,
+ typename std::enable_if<!std::is_convertible<F1, F*>::value>::type*)
+ {
+ std_function_type sf (std::move (f));
+
+ if (F* const* const f = sf.template target<F*> ())
+ {
+ function = *f;
+ deleter = 0;
+ std_function = 0;
+ }
+ else
+ {
+ function = reinterpret_cast<F*> (&caller_impl<F>::function);
+ deleter = &deleter_impl<F>;
+ std_function = new std_function_type (std::move (sf));
+ }
+ }
+#endif
+
+ template <typename F>
+ void function_wrapper<F>::
+ swap (function_wrapper<F>& 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_function> data_functions;
typedef map<data_key, data_functions> 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<caller> (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 <cstddef> // std::size_t
#ifdef ODB_CXX11
-# include <functional> // std::function
+# include <utility> // std::move
+# include <functional> // std::function
+# include <type_traits> // std::enable_if, std::is_convertible
#endif
#include <odb/forward.hxx> // schema_version, odb::core
@@ -74,11 +76,11 @@ namespace odb
schema_version = 0,
const std::string& name = "");
-#ifdef ODB_CXX11
- typedef std::function<void (database&)> 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_type>
+ 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 <schema_version v, schema_version base>
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<v, base> (id_common, f, name);
}
+#else
+ template <schema_version v, schema_version base, typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (F f, const std::string& name = "")
+ {
+ data_migration_function<v, base> (id_common, std::move (f), name);
+ }
+#endif
+
// Database-specific data migration.
//
+#ifndef ODB_CXX11
template <schema_version v, schema_version base>
static void
data_migration_function (database& db,
- data_migration_function_type f,
+ data_migration_function_ptr f,
const std::string& name = "")
{
data_migration_function<v, base> (db.id (), f, name);
}
+#else
+ template <schema_version v, schema_version base, typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::value, void>::type
+ data_migration_function (database& db, F f, const std::string& name = "")
+ {
+ data_migration_function<v, base> (db.id (), std::move (f), name);
+ }
+#endif
+#ifndef ODB_CXX11
template <schema_version v, schema_version base>
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 <schema_version v, schema_version base, typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::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 <typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::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 <typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::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 <typename F>
+ static typename std::enable_if<
+ std::is_convertible<
+ F, std::function<data_migration_function_type>>::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<v, base> (f, name);
}
+#else
+ template <typename F>
+ data_migration_entry (F f,
+ const std::string& name = "",
+ typename std::enable_if<std::is_convertible<
+ F, std::function<function_type>>::value>
+ ::type* = 0)
+ {
+ schema_catalog::data_migration_function<v, base> (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<v, base> (id, v, f, name);
}
+#else
+ template <typename F>
+ data_migration_entry (database_id id,
+ F f,
+ const std::string& name = "",
+ typename std::enable_if<std::is_convertible<
+ F, std::function<function_type>>::value>
+ ::type* = 0)
+ {
+ schema_catalog::data_migration_function<v, base> (id,
+ v,
+ std::move (f),
+ name);
+ }
+#endif
};
namespace common