aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS8
-rw-r--r--doc/manual.xhtml126
-rw-r--r--odb/header.cxx1
-rw-r--r--odb/pragma.cxx38
-rw-r--r--odb/relational/header.hxx11
-rw-r--r--odb/relational/inline.hxx90
-rw-r--r--odb/relational/source.hxx6
-rw-r--r--odb/tracer/header.cxx10
-rw-r--r--odb/tracer/inline.cxx14
-rw-r--r--odb/validator.cxx43
10 files changed, 343 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index 8dde908..7851416 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,13 @@
Version 1.5.0
+ * Support for database operations callbacks. Now a persistent class can
+ register a callback function that will be called before and after every
+ database operation, such as persist, load, update, or erase, is performed
+ on an object of this class. A database operations callback can be used to
+ implement object-specific pre and post initializations, registrations,
+ and cleanups. For more information and an example refer to Section 10.1.4,
+ "Callback" in the ODB manual.
+
* New option, --include-regex, allows the modification of the #include
directive paths generated by the ODB compiler. This is primarily useful
when placing the generating code into subdirectories and the #include
diff --git a/doc/manual.xhtml b/doc/manual.xhtml
index b60eafc..a06bfa6 100644
--- a/doc/manual.xhtml
+++ b/doc/manual.xhtml
@@ -414,6 +414,7 @@ for consistency.
<tr><th>10.1.1</th><td><a href="#10.1.1"><code>table</code></a></td></tr>
<tr><th>10.1.2</th><td><a href="#10.1.2"><code>pointer</code></a></td></tr>
<tr><th>10.1.3</th><td><a href="#10.1.3"><code>abstract</code></a></td></tr>
+ <tr><th>10.1.4</th><td><a href="#10.1.4"><code>callback</code></a></td></tr>
</table>
</td>
</tr>
@@ -5749,8 +5750,13 @@ class person
<td><a href="#10.1.3">10.1.3</a></td>
</tr>
- </table>
+ <tr>
+ <td><code>callback</code></td>
+ <td>database operations callback</td>
+ <td><a href="#10.1.4">10.1.4</a></td>
+ </tr>
+ </table>
<h3><a name="10.1.1">10.1.1 <code>table</code></a></h3>
@@ -5851,6 +5857,124 @@ class contractor: public person
discussion of persistent class inheritance, refer to
<a href="#8">Chapter 8, "Inheritance"</a>.</p>
+ <h3><a name="10.1.4">10.1.4 <code>callback</code></a></h3>
+
+ <p>The <code>callback</code> specifier specifies the persist class
+ member function that should be called before and after a
+ database operation is performed on an object of this class.
+ For example:</p>
+
+ <pre class="c++">
+#include &lt;odb/callback.hxx>
+
+#pragma db object callback(init)
+class person
+{
+ ...
+
+ void
+ init (odb::callback_event, odb::database&amp;);
+};
+ </pre>
+
+ <p>The callback function has the following signature and can be
+ overloaded for constant objects:</p>
+
+ <pre class="c++">
+void
+name (odb::callback_event, odb::database&amp;);
+
+void
+name (odb::callback_event, odb::database&amp;) const;
+ </pre>
+
+ <p>The first argument to the callback function is the event that
+ triggered this call. The <code>odb::callback_event</code>
+ enum-like type is defined in the <code>&lt;odb/callback.hxx></code>
+ header file and has the following interface:</p>
+
+ <pre class="c++">
+namespace odb
+{
+ struct callback_event
+ {
+ enum value
+ {
+ pre_persist,
+ post_persist,
+ pre_load,
+ post_load,
+ pre_update,
+ post_update,
+ pre_erase,
+ post_erase
+ };
+
+ callback_event (value v);
+ operator value () const;
+ };
+}
+ </pre>
+
+ <p>The second argument to the callback function is the database
+ on which the operation is about to be performed or has just
+ been performed.</p>
+
+ <p>If only the non-<code>const</code> version of the callback function
+ is provided, then only database operations that are performed on
+ unrestricted objects will trigger callback calls. If only the
+ <code>const</code> version is provided, then the database
+ operations on both constant and unrestricted objects will trigger
+ callback calls but the object will always be passed as constant.
+ Finally, if both versions are provided, then the <code>const</code>
+ overload will be called for constant objects and the non-<code>const</code>
+ overload for unrestricted objects. These rules are modeled after
+ the standard C++ overload resolution rules. A callback function can
+ be inline or virtual.</p>
+
+ <p>A database operations callback can be used to implement object-specific
+ pre and post initializations, registrations, and cleanups. As an example,
+ the following code fragment outlines an implementation of a
+ <code>person</code> class that maintains the transient <code>age</code>
+ data member in addition to the persistent date of birth. A callback
+ is used to calculate the value of the former from the latter every
+ time a <code>person</code> object is loaded from the database.</p>
+
+ <pre class="c++">
+#include &lt;odb/core.hxx>
+#include &lt;odb/callback.hxx>
+
+#pragma db object callback(init)
+class person
+{
+ ...
+
+private:
+ friend class odb::access;
+
+ date born_;
+
+ #pragma db transient
+ unsigned short age_;
+
+ void
+ init (odb::callback_event e, odb::database&amp;)
+ {
+ switch (e)
+ {
+ case odb::callback_event::post_load:
+ {
+ // Calculate age from the date of birth.
+ ...
+ break;
+ }
+ default:
+ break;
+ }
+ }
+};
+ </pre>
+
<h2><a name="10.2">10.2 Value Type Pragmas</a></h2>
<p>A pragma with the <code>value</code> qualifier describes a value
diff --git a/odb/header.cxx b/odb/header.cxx
index 140a781..62902f6 100644
--- a/odb/header.cxx
+++ b/odb/header.cxx
@@ -88,6 +88,7 @@ namespace header
os << "#include <odb/core.hxx>" << endl
<< "#include <odb/traits.hxx>" << endl
+ << "#include <odb/callback.hxx>" << endl
<< "#include <odb/pointer-traits.hxx>" << endl;
// In case of a boost TR1 implementation, we cannot distinguish
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index a22c1d1..3b8d222 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -127,7 +127,8 @@ check_decl_type (tree d, string const& name, string const& p, location_t l)
}
else if (p == "object" ||
p == "pointer" ||
- p == "abstract")
+ p == "abstract" ||
+ p == "callback")
{
if (tc != RECORD_TYPE)
{
@@ -374,6 +375,41 @@ handle_pragma (cpp_reader* reader,
tt = pragma_lex (&t);
}
+ else if (p == "callback")
+ {
+ // callback (name)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl != 0 && !check_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (pragma_lex (&t) != CPP_OPEN_PAREN)
+ {
+ error () << "'(' expected after db pragma '" << p << "'" << endl;
+ return;
+ }
+
+ tt = pragma_lex (&t);
+
+ if (tt != CPP_NAME)
+ {
+ error () << "member function name expected in db pragma '" << p
+ << "'" << endl;
+ return;
+ }
+
+ val = IDENTIFIER_POINTER (t);
+
+ if (pragma_lex (&t) != CPP_CLOSE_PAREN)
+ {
+ error () << "')' expected at the end of db pragma '" << p << "'" << endl;
+ return;
+ }
+
+ tt = pragma_lex (&t);
+ }
else if (p == "id")
{
// id
diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx
index 16c7855..62c2e92 100644
--- a/odb/relational/header.hxx
+++ b/odb/relational/header.hxx
@@ -940,6 +940,16 @@ namespace relational
// Functions (concrete).
//
+ // callback ()
+ //
+ os << "static void" << endl
+ << "callback (database&, object_type&, callback_event);"
+ << endl;
+
+ os << "static void" << endl
+ << "callback (database&, const object_type&, callback_event);"
+ << endl;
+
// persist ()
//
os << "static void" << endl
@@ -1090,6 +1100,7 @@ namespace relational
generate ()
{
os << "#include <odb/details/buffer.hxx>" << endl
+ << "#include <odb/details/unused.hxx>" << endl
<< endl;
os << "#include <odb/" << db << "/version.hxx>" << endl
diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx
index 856965c..89fc720 100644
--- a/odb/relational/inline.hxx
+++ b/odb/relational/inline.hxx
@@ -13,6 +13,67 @@ namespace relational
{
namespace inline_
{
+ //
+ //
+ struct callback_calls: traversal::class_, virtual context
+ {
+ typedef callback_calls base;
+
+ callback_calls ()
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ callback_calls (callback_calls const&)
+ : root_context (), //@@ -Wextra
+ context ()
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c, bool constant)
+ {
+ const_ = constant;
+ traverse (c);
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!c.count ("object"))
+ return;
+
+ if (c.count ("callback"))
+ {
+ string name (c.get<string> ("callback"));
+
+ // In case of the const object, we only generate the call if
+ // there is a const callback.
+ //
+ if (const_)
+ {
+ if (c.count ("callback-const"))
+ os << "static_cast< const " << c.fq_name () << "& > (obj)." <<
+ name << " (e, db);";
+ }
+ else
+ os << "static_cast< " << c.fq_name () << "& > (obj)." <<
+ name << " (e, db);";
+ }
+ else
+ inherits (c);
+ }
+
+ protected:
+ bool const_;
+ traversal::inherits inherits_;
+ };
+
+ //
+ //
struct class_: traversal::class_, virtual context
{
typedef class_ base;
@@ -113,6 +174,32 @@ namespace relational
if (abst)
return;
+ // callback ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "callback (database& db, object_type& obj, callback_event e)"
+ << endl
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (obj);"
+ << "ODB_POTENTIALLY_UNUSED (e);"
+ << endl;
+ callback_calls_->traverse (c, false);
+ os << "}";
+
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "callback (database& db, const object_type& obj, " <<
+ "callback_event e)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (obj);"
+ << "ODB_POTENTIALLY_UNUSED (e);"
+ << endl;
+ callback_calls_->traverse (c, true);
+ os << "}";
+
// query_type
//
if (options.generate_query ())
@@ -164,6 +251,9 @@ namespace relational
*/
}
+
+ private:
+ instance<callback_calls> callback_calls_;
};
}
}
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index 4d2f10a..42d0f9b 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -2331,9 +2331,11 @@ namespace relational
<< endl
<< "if (l.locked ())"
<< "{"
+ << "callback (db, obj, callback_event::pre_load);"
<< "init (obj, sts.image (), db);"
<< "load_ (sts, obj);"
<< "sts.load_delayed ();"
+ << "callback (db, obj, callback_event::post_load);"
<< "l.unlock ();"
<< "}"
<< "else" << endl
@@ -2367,9 +2369,11 @@ namespace relational
<< endl
<< "if (l.locked ())"
<< "{"
+ << "callback (db, obj, callback_event::pre_load);"
<< "init (obj, sts.image (), db);"
<< "load_ (sts, obj);"
<< "sts.load_delayed ();"
+ << "callback (db, obj, callback_event::post_load);"
<< "l.unlock ();"
<< "}"
<< "else" << endl
@@ -2689,8 +2693,6 @@ namespace relational
if (embedded_schema)
os << "#include <odb/schema-catalog-impl.hxx>" << endl;
- os << "#include <odb/details/unused.hxx>" << endl;
-
if (options.generate_query ())
os << "#include <odb/details/shared-ptr.hxx>" << endl;
diff --git a/odb/tracer/header.cxx b/odb/tracer/header.cxx
index 16bee82..ac89ec7 100644
--- a/odb/tracer/header.cxx
+++ b/odb/tracer/header.cxx
@@ -88,6 +88,16 @@ namespace tracer
os << "static bool" << endl
<< "find (database&, const id_type&, object_type&);";
+ // callback ()
+ //
+ os << "static void" << endl
+ << "callback (database&, object_type&, callback_event);"
+ << endl;
+
+ os << "static void" << endl
+ << "callback (database&, const object_type&, callback_event);"
+ << endl;
+
os << "};";
}
};
diff --git a/odb/tracer/inline.cxx b/odb/tracer/inline.cxx
index f4125a9..e8feec8 100644
--- a/odb/tracer/inline.cxx
+++ b/odb/tracer/inline.cxx
@@ -42,6 +42,20 @@ namespace tracer
<< "{"
<< "return obj." << id.name () << ";" << endl
<< "}";
+
+ // callback ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "callback (database&, object_type&, callback_event)"
+ << "{"
+ << "}";
+
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "callback (database&, const object_type&, callback_event)"
+ << "{"
+ << "}";
}
};
}
diff --git a/odb/validator.cxx b/odb/validator.cxx
index 998bd8b..487d69a 100644
--- a/odb/validator.cxx
+++ b/odb/validator.cxx
@@ -3,6 +3,8 @@
// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
// license : GNU GPL v3; see accompanying LICENSE file
+#include <odb/gcc.hxx>
+
#include <iostream>
#include <odb/traversal.hxx>
@@ -138,6 +140,47 @@ namespace
virtual void
traverse_object (type& c)
{
+ // Check that the callback function exist.
+ //
+ if (c.count ("callback"))
+ {
+ string name (c.get<string> ("callback"));
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (name.c_str ()), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
+ {
+ cerr << c.file () << ":" << c.line () << ":" << c.column () << ": "
+ << "error: unable to resolve member function '" << name << "' "
+ << "specified with '#pragma db callback' for class '"
+ << c.name () << "'" << endl;
+
+ valid_ = false;
+ }
+
+ // Figure out if we have a const version of the callback. OVL_*
+ // macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+ {
+ tree f (OVL_CURRENT (o));
+ if (DECL_CONST_MEMFUNC_P (f))
+ {
+ c.set ("callback-const", true);
+ break;
+ }
+ }
+
+ //@@ Would be nice to check the signature of the function(s)
+ // instead of postponing it until the C++ compilation. Though
+ // we may still get C++ compilation errors because of const
+ // mismatch.
+ //
+ }
+
+ // Check bases.
+ //
bool base (false);
for (type::inherits_iterator i (c.inherits_begin ());