diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2011-07-04 17:53:47 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2011-07-04 17:53:47 +0200 |
commit | 932cd7a53b3996468fee5cfa63c2b2998dbe971a (patch) | |
tree | 55424fe09a95310bc2b8e6d1473108b64107e06c | |
parent | 1ac1ae65e6cbf42c002acb27615127bbc0b20d9e (diff) |
Implement support for database operations callbacks
New object pragma: callback. New test: common/callback. New manual
section: 10.1.4, "callback".
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | doc/manual.xhtml | 126 | ||||
-rw-r--r-- | odb/header.cxx | 1 | ||||
-rw-r--r-- | odb/pragma.cxx | 38 | ||||
-rw-r--r-- | odb/relational/header.hxx | 11 | ||||
-rw-r--r-- | odb/relational/inline.hxx | 90 | ||||
-rw-r--r-- | odb/relational/source.hxx | 6 | ||||
-rw-r--r-- | odb/tracer/header.cxx | 10 | ||||
-rw-r--r-- | odb/tracer/inline.cxx | 14 | ||||
-rw-r--r-- | odb/validator.cxx | 43 |
10 files changed, 343 insertions, 4 deletions
@@ -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 <odb/callback.hxx> + +#pragma db object callback(init) +class person +{ + ... + + void + init (odb::callback_event, odb::database&); +}; + </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&); + +void +name (odb::callback_event, odb::database&) 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><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 <odb/core.hxx> +#include <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&) + { + 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 ()); |