aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-08-15 11:46:00 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-08-15 11:46:00 +0200
commiteacf52a9a4f3832274fdefc909ab23c13413e128 (patch)
treef1f754b6149797315f91d6f872363555201d5a65
parent396cad633b6f0559a39e5111827f9b1125c67506 (diff)
Add support for member accessors/modifiers
New pragmas: get, set, access. New test: common/access.
-rw-r--r--odb/context.cxx386
-rw-r--r--odb/context.hxx66
-rw-r--r--odb/cxx-token.hxx6
-rw-r--r--odb/diagnostics.cxx42
-rw-r--r--odb/diagnostics.hxx18
-rw-r--r--odb/parser.cxx48
-rw-r--r--odb/pragma.cxx62
-rw-r--r--odb/relational/inline.hxx18
-rw-r--r--odb/relational/mssql/source.cxx35
-rw-r--r--odb/relational/oracle/source.cxx39
-rw-r--r--odb/relational/processor.cxx432
-rw-r--r--odb/relational/source.cxx339
-rw-r--r--odb/relational/source.hxx379
-rw-r--r--odb/semantics/derived.cxx106
-rw-r--r--odb/semantics/derived.hxx70
-rw-r--r--odb/semantics/elements.hxx2
16 files changed, 1717 insertions, 331 deletions
diff --git a/odb/context.cxx b/odb/context.cxx
index df94dfb..8a80030 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -6,10 +6,12 @@
#include <cctype> // std::toupper
#include <cassert>
+#include <sstream>
#include <odb/context.hxx>
#include <odb/common.hxx>
#include <odb/pragma.hxx>
+#include <odb/cxx-lexer.hxx>
#include <odb/relational/mssql/context.hxx>
#include <odb/relational/mysql/context.hxx>
@@ -33,6 +35,263 @@ name () const
}
//
+// member_access
+//
+
+bool member_access::
+placeholder () const
+{
+ for (cxx_tokens::const_iterator i (expr.begin ()), e (expr.end ()); i != e;)
+ {
+ if (i->type == CPP_OPEN_PAREN)
+ {
+ if (++i != e && i->type == CPP_QUERY)
+ {
+ if (++i != e && i->type == CPP_CLOSE_PAREN)
+ return true;
+ }
+ }
+ else
+ ++i;
+ }
+
+ return false;
+}
+
+static inline void
+add_space (string& s)
+{
+ string::size_type n (s.size ());
+ if (n != 0 && s[n - 1] != ' ')
+ s += ' ';
+}
+
+string member_access::
+translate (string const& obj, string const& val) const
+{
+ // This code is similar to translate_expression() from relations/source.cxx.
+ //
+ string r;
+
+ cxx_tokens_lexer l;
+ l.start (expr);
+
+ string tl;
+ for (cpp_ttype tt (l.next (tl)), ptt (CPP_EOF); tt != CPP_EOF;)
+ {
+ // Try to format the expression to resemble the style of the
+ // generated code.
+ //
+ switch (tt)
+ {
+ case CPP_NOT:
+ {
+ add_space (r);
+ r += '!';
+ break;
+ }
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD)
+ add_space (r);
+
+ r += '(';
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ r += ')';
+ break;
+ }
+ case CPP_OPEN_SQUARE:
+ {
+ r += '[';
+ break;
+ }
+ case CPP_CLOSE_SQUARE:
+ {
+ r += ']';
+ break;
+ }
+ case CPP_OPEN_BRACE:
+ {
+ add_space (r);
+ r += "{ ";
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ add_space (r);
+ r += '}';
+ break;
+ }
+ case CPP_SEMICOLON:
+ {
+ r += ';';
+ break;
+ }
+ case CPP_ELLIPSIS:
+ {
+ add_space (r);
+ r += "...";
+ break;
+ }
+ case CPP_PLUS:
+ case CPP_MINUS:
+ {
+ bool unary (ptt != CPP_NAME &&
+ ptt != CPP_SCOPE &&
+ ptt != CPP_NUMBER &&
+ ptt != CPP_STRING &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_PLUS_PLUS &&
+ ptt != CPP_MINUS_MINUS);
+
+ if (!unary)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+
+ if (!unary)
+ r += ' ';
+ break;
+ }
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ {
+ if (ptt != CPP_NAME &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_CLOSE_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_DEREF:
+ case CPP_DEREF_STAR:
+ case CPP_DOT:
+ case CPP_DOT_STAR:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_STRING:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += context::strlit (tl);
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_SCOPE:
+ {
+ // Add space except for a few common cases.
+ //
+ if (ptt != CPP_NAME &&
+ ptt != CPP_OPEN_PAREN &&
+ ptt != CPP_OPEN_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_NAME:
+ {
+ // Start of a name.
+ //
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_QUERY:
+ {
+ if (ptt == CPP_OPEN_PAREN)
+ {
+ // Get the next token and see if it is ')'.
+ //
+ ptt = tt;
+ tt = l.next (tl);
+
+ if (tt == CPP_CLOSE_PAREN)
+ r += val;
+ else
+ {
+ // The same as in the default case.
+ //
+ add_space (r);
+ r += "? ";
+ }
+ continue; // We have already gotten the next token.
+ }
+ // Fall through.
+ }
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_KEYWORD)
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ // Translate 'this'.
+ //
+ r += (tl == "this" ? obj : tl);
+ }
+ else
+ {
+ // All the other operators.
+ //
+ add_space (r);
+ r += cxx_lexer::token_spelling[tt];
+ r += ' ';
+ }
+ break;
+ }
+ }
+
+ //
+ // Watch out for the continue statements above if you add any
+ // logic here.
+ //
+
+ ptt = tt;
+ tt = l.next (tl);
+ }
+
+ return r;
+}
+
+//
// context
//
@@ -614,6 +873,133 @@ member_type (semantics::data_member& m, string const& key_prefix)
return *indirect_value<semantics::type*> (t, key);
}
+string context::
+type_ref_type (semantics::type& t,
+ semantics::names* hint,
+ bool mc,
+ string const& var)
+{
+ using semantics::array;
+ string r;
+
+ // Note that trailing const syntax is used for a reason (consider
+ // t == const foo*). We also have to decay top-level arrays.
+ //
+ if (array* a = dynamic_cast<array*> (&utype (t)))
+ {
+ semantics::type& bt (a->base_type ());
+ hint = a->contains ().hint ();
+
+ if (bt.is_a<array> ())
+ {
+ // If we need to add/strip const or no name was used in the
+ // declaration, then create an array declaration (e.g., for
+ // char x[2][3] we will have char const (*x)[3]).
+ //
+ if (mc != const_type (t) || hint == 0)
+ return type_val_type (bt, 0, mc, "(*" + var + ")");
+ }
+
+ // Array base type is always cvr-unqualified.
+ //
+ if (mc)
+ r = bt.fq_name (hint) + " const";
+ else
+ r = bt.fq_name (hint);
+
+ r += '*';
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+ else
+ {
+ if (mc == const_type (t))
+ r = t.fq_name (hint);
+ else if (mc)
+ r = t.fq_name (hint) + " const";
+ else
+ {
+ semantics::type& ut (utype (t, hint));
+ r = ut.fq_name (hint);
+ }
+
+ r += '&';
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+
+ return r;
+}
+
+string context::
+type_val_type (semantics::type& t,
+ semantics::names* hint,
+ bool mc,
+ string const& var)
+{
+ using semantics::array;
+ string r;
+
+ // Arrays are a complicated case. Firstly, we may need to add/strip const
+ // to/from the base type. Secondly, the array dimensions are written after
+ // the variable name. All this is further complicated by multiple dimensions.
+ // Thanks, Dennis!
+ //
+ if (array* a = dynamic_cast<array*> (&utype (t)))
+ {
+ semantics::type& bt (a->base_type ());
+
+ // If we don't need to add/strip const and a name was used in the
+ // declaration, then use that name.
+ //
+ if (mc == const_type (t) && hint != 0)
+ {
+ r = t.fq_name (hint);
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+ else
+ {
+ // Otherwise, construct the array declaration.
+ //
+ string v (var);
+ v += '[';
+ ostringstream ostr;
+ ostr << a->size ();
+ v += ostr.str ();
+
+ if (a->size () > 0xFFFFFFFF)
+ v += "ULL";
+ else if (a->size () > 2147483647)
+ v += "U";
+
+ v += ']';
+
+ r = type_val_type (bt, a->contains ().hint (), mc, v);
+ }
+ }
+ else
+ {
+ if (mc == const_type (t))
+ r = t.fq_name (hint);
+ else if (mc)
+ r = t.fq_name (hint) + " const";
+ else
+ {
+ semantics::type& ut (utype (t, hint));
+ r = ut.fq_name (hint);
+ }
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+
+ return r;
+}
+
bool context::
composite_ (semantics::class_& c)
{
diff --git a/odb/context.hxx b/odb/context.hxx
index f201596..5d8c339 100644
--- a/odb/context.hxx
+++ b/odb/context.hxx
@@ -217,6 +217,27 @@ struct column_expr: std::vector<column_expr_part>
location_t loc;
};
+//
+//
+struct member_access
+{
+ member_access (location_t l): loc (l), by_value (false) {}
+
+ // Return true of we have the (?) placeholder.
+ //
+ bool
+ placeholder () const;
+
+ std::string
+ translate (std::string const& obj,
+ std::string const& val = std::string ()) const;
+
+ location_t loc; // If zero, then this is a synthesized expression.
+ cxx_tokens expr;
+ bool by_value; // True if accessor returns by value. False doesn't
+ // necessarily mean that it is by reference.
+};
+
class context
{
public:
@@ -260,11 +281,14 @@ public:
static semantics::type&
utype (semantics::data_member&, semantics::names*& hint);
+ // For arrays this function returns true if the (innermost) element
+ // type is const.
+ //
static bool
const_type (semantics::type&);
static semantics::type&
- member_type (semantics::data_member& m, string const& key_prefix);
+ member_type (semantics::data_member&, string const& key_prefix);
static semantics::type&
member_utype (semantics::data_member& m, string const& key_prefix)
@@ -272,6 +296,44 @@ public:
return utype (member_type (m, key_prefix));
}
+ // Form a reference type for a member type. If make_const is true, then
+ // add top-level const qualifier, unless it is already there. If it is
+ // false, then strip it if it is already there. If var is not empty,
+ // then embed the variable name into the type (e.g., char (*v)[3]).
+ //
+ static string
+ member_ref_type (semantics::data_member& m,
+ bool make_const,
+ string const& var = "")
+ {
+ return type_ref_type (m.type (), m.belongs ().hint (), make_const, var);
+ }
+
+ static string
+ type_ref_type (semantics::type&,
+ semantics::names* hint,
+ bool make_const,
+ string const& var = "");
+
+ // Form a value type for a member type. If make_const is true, then add
+ // top-level const qualifier, unless it is already there. If it is false,
+ // then strip it if it is already there. If var is not empty, then embed
+ // the variable name into the type (e.g., char v[3]).
+ //
+ static string
+ member_val_type (semantics::data_member& m,
+ bool make_const,
+ string const& var = "")
+ {
+ return type_val_type (m.type (), m.belongs ().hint (), make_const, var);
+ }
+
+ static string
+ type_val_type (semantics::type&,
+ semantics::names* hint,
+ bool make_const,
+ string const& var = "");
+
// Predicates.
//
public:
@@ -617,7 +679,7 @@ public:
// Return a string literal that can be used in C++ source code. It
// includes "".
//
- string
+ static string
strlit (string const&);
// Counts and other information.
diff --git a/odb/cxx-token.hxx b/odb/cxx-token.hxx
index 6478fc1..ae5fe60 100644
--- a/odb/cxx-token.hxx
+++ b/odb/cxx-token.hxx
@@ -12,7 +12,11 @@
struct cxx_token
{
- cxx_token (location_t l, unsigned int t): loc (l), type (t), node (0) {}
+ cxx_token (location_t l,
+ unsigned int t,
+ std::string const& lt = std::string (),
+ tree n = 0)
+ : loc (l), type (t), literal (lt), node (n) {}
location_t loc; // Location of this token.
unsigned int type; // Untyped cpp_ttype.
diff --git a/odb/diagnostics.cxx b/odb/diagnostics.cxx
index bce1102..0ebfba9 100644
--- a/odb/diagnostics.cxx
+++ b/odb/diagnostics.cxx
@@ -3,13 +3,17 @@
// license : GNU GPL v3; see accompanying LICENSE file
#include <odb/gcc.hxx>
+
+#include <sstream>
+
#include <odb/cxx-lexer.hxx>
#include <odb/diagnostics.hxx>
using namespace std;
+using cutl::fs::path;
std::ostream&
-error (cutl::fs::path const& p, size_t line, size_t clmn)
+error (path const& p, size_t line, size_t clmn)
{
//@@ We only need to do this if we are still parsing (i.e.,
// pragma parsing). Is there a way to detect this?
@@ -21,7 +25,7 @@ error (cutl::fs::path const& p, size_t line, size_t clmn)
}
std::ostream&
-warn (cutl::fs::path const& p, size_t line, size_t clmn)
+warn (path const& p, size_t line, size_t clmn)
{
warningcount++;
@@ -30,7 +34,7 @@ warn (cutl::fs::path const& p, size_t line, size_t clmn)
}
std::ostream&
-info (cutl::fs::path const& p, size_t line, size_t clmn)
+info (path const& p, size_t line, size_t clmn)
{
cerr << p << ':' << line << ':' << clmn << ": info: ";
return cerr;
@@ -86,10 +90,38 @@ info (cxx_lexer& l)
return info (l.location ());
}
-cutl::fs::path
+std::string
+location_string (path const& p, size_t line, size_t clmn, bool leaf)
+{
+ ostringstream ostr;
+
+ if (leaf)
+ ostr << p.leaf ();
+ else
+ ostr << p;
+
+ ostr << ':' << line << ':' << clmn;
+ return ostr.str ();
+}
+
+std::string
+location_string (location_t loc, bool leaf)
+{
+ ostringstream ostr;
+
+ if (leaf)
+ ostr << path (LOCATION_FILE (loc)).leaf ();
+ else
+ ostr << LOCATION_FILE (loc);
+
+ ostr << ':' << LOCATION_LINE (loc) << ':' << LOCATION_COLUMN (loc);
+ return ostr.str ();
+}
+
+path
location_file (location_t loc)
{
- return cutl::fs::path (LOCATION_FILE (loc));
+ return path (LOCATION_FILE (loc));
}
size_t
diff --git a/odb/diagnostics.hxx b/odb/diagnostics.hxx
index b1e89b0..448b83f 100644
--- a/odb/diagnostics.hxx
+++ b/odb/diagnostics.hxx
@@ -7,6 +7,7 @@
#include <odb/gcc-fwd.hxx>
+#include <string>
#include <cstddef>
#include <iostream>
@@ -65,6 +66,23 @@ warn (cxx_lexer&);
std::ostream&
info (cxx_lexer&);
+// Location as a string in the "<file>:<line>:<column>" format.
+//
+std::string
+location_string (cutl::fs::path const&,
+ std::size_t line,
+ std::size_t clmn,
+ bool leaf = false);
+
+inline std::string
+location_string (location const& l, bool leaf = false)
+{
+ return location_string (l.file, l.line, l.column, leaf);
+}
+
+std::string
+location_string (location_t, bool leaf = false);
+
// location_t macro wrappers.
//
cutl::fs::path
diff --git a/odb/parser.cxx b/odb/parser.cxx
index e011663..9a19440 100644
--- a/odb/parser.cxx
+++ b/odb/parser.cxx
@@ -1388,7 +1388,7 @@ emit_type (tree t,
<< " main " << mv << endl;
for (tree v (TYPE_MAIN_VARIANT (t)); v != 0; v = TYPE_NEXT_VARIANT (v))
- ts << "\tvariant " << v << endl;
+ ts << "\tvariant " << v << " " << CP_TYPE_CONST_P (v) << endl;
}
node* n (unit_->find (mv));
@@ -1709,24 +1709,46 @@ create_type (tree t,
}
}
- type& bt (
- emit_type (TREE_TYPE (t), access::public_, file, line, clmn));
+ // In GCC tree a const array has both the array type itself and the
+ // element type marked as const. This doesn't bode well with our
+ // semantic graph model where we have a separate type node for
+ // qualifiers. To fix this, we are going to strip the const
+ // qualification from the element type and only preserve it in
+ // the array type. In other words, we view it as "constant array"
+ // rather than "array of constant elements".
+ //
+ tree bt (TREE_TYPE (t));
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+ type& bt_node (emit_type (bt_mv, access::public_, file, line, clmn));
t = TYPE_MAIN_VARIANT (t);
array& a (unit_->new_node<array> (file, line, clmn, t, size));
unit_->insert (t, a);
- unit_->new_edge<contains> (a, bt);
+ contains& edge (unit_->new_edge<contains> (a, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (
+ cp_type_quals (bt) == TYPE_UNQUALIFIED ? bt : bt_mv))
+ edge.hint (*hint);
+
process_named_pragmas (t, a);
r = &a;
break;
}
case REFERENCE_TYPE:
{
- type& bt (
- emit_type (TREE_TYPE (t), access::public_, file, line, clmn));
+ tree bt (TREE_TYPE (t));
+ type& bt_node (emit_type (bt, access::public_, file, line, clmn));
t = TYPE_MAIN_VARIANT (t);
reference& ref (unit_->new_node<reference> (file, line, clmn, t));
unit_->insert (t, ref);
- unit_->new_edge<references> (ref, bt);
+ references& edge (unit_->new_edge<references> (ref, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (bt))
+ edge.hint (*hint);
+
process_named_pragmas (t, ref);
r = &ref;
break;
@@ -1735,12 +1757,18 @@ create_type (tree t,
{
if (!TYPE_PTRMEM_P (t))
{
- type& bt (
- emit_type (TREE_TYPE (t), access::public_, file, line, clmn));
+ tree bt (TREE_TYPE (t));
+ type& bt_node (emit_type (bt, access::public_, file, line, clmn));
t = TYPE_MAIN_VARIANT (t);
pointer& p (unit_->new_node<pointer> (file, line, clmn, t));
unit_->insert (t, p);
- unit_->new_edge<points> (p, bt);
+ points& edge (unit_->new_edge<points> (p, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (bt))
+ edge.hint (*hint);
+
process_named_pragmas (t, p);
r = &p;
}
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index ffe711f..d440f82 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -378,7 +378,10 @@ check_spec_decl_type (tree d,
p == "transient" ||
p == "version" ||
p == "index" ||
- p == "unique")
+ p == "unique" ||
+ p == "get" ||
+ p == "set" ||
+ p == "access")
{
if (tc != FIELD_DECL)
{
@@ -853,12 +856,56 @@ handle_pragma (cxx_lexer& l,
{
// index
// unique
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl != 0 && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "get" ||
+ p == "set" ||
+ p == "access")
+ {
+ // get(name|expr)
+ // set(name|expr)
+ // access(name|expr)
+ //
// Make sure we've got the correct declaration type.
//
if (decl != 0 && !check_spec_decl_type (decl, decl_name, p, loc))
return;
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ val = member_access (loc);
+ if (!parse_expression (l, tt, tl, tn, val.value<member_access> ().expr, p))
+ return; // Diagnostics has already been issued.
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ // Convert access to the get/set pair.
+ //
+ if (p == "access")
+ {
+ add_pragma (
+ pragma (p, "get", val, loc, &check_spec_decl_type, 0), decl, ns);
+ name = "set";
+ }
+
tt = l.next (tl, &tn);
}
else if (p == "table")
@@ -1259,10 +1306,7 @@ handle_pragma (cxx_lexer& l,
// Expression.
//
if (s)
- {
- vq.expr.push_back (cxx_token (0, CPP_STRING));
- vq.expr.back ().literal = str;
- }
+ vq.expr.push_back (cxx_token (0, CPP_STRING, str));
if (!parse_expression (l, tt, tl, tn, vq.expr, p))
return; // Diagnostics has already been issued.
@@ -2160,10 +2204,7 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p)
if (qual)
break;
- cxx_token ct (l.location (), tt);
- ct.literal = tl;
- ct.node = tn;
- saved_tokens.push_back (ct);
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
}
if (balance != 0)
@@ -2431,6 +2472,9 @@ handle_pragma_qualifier (cxx_lexer& l, string const& p)
else if (p == "id" ||
p == "auto" ||
p == "unique" ||
+ p == "get" ||
+ p == "set" ||
+ p == "access" ||
p == "column" ||
p == "value_column" ||
p == "index_column" ||
diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx
index 39801cb..2642809 100644
--- a/odb/relational/inline.hxx
+++ b/odb/relational/inline.hxx
@@ -5,6 +5,7 @@
#ifndef ODB_RELATIONAL_INLINE_HXX
#define ODB_RELATIONAL_INLINE_HXX
+#include <odb/diagnostics.hxx>
#include <odb/relational/context.hxx>
#include <odb/relational/common.hxx>
@@ -258,16 +259,27 @@ namespace relational
os << "inline" << endl
<< traits << "::id_type" << endl
<< traits << "::" << endl
- << "id (const object_type&" << (id != 0 ? " obj" : "") << ")"
+ << "id (const object_type&" << (id != 0 ? " o" : "") << ")"
<< "{";
if (id != 0)
{
if (base_id)
os << "return object_traits< " << class_fq_name (*base) <<
- " >::id (obj);";
+ " >::id (o);";
else
- os << "return obj." << id->name () << ";";
+ {
+ // Get the id using the accessor expression. If this is not
+ // a synthesized expression, then output its location for
+ // easier error tracking.
+ //
+ member_access& ma (id->get<member_access> ("get"));
+
+ if (ma.loc != 0)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << "return " << ma.translate ("o") << ";";
+ }
}
os << "}";
diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx
index 1201c87..7fce155 100644
--- a/odb/relational/mssql/source.cxx
+++ b/odb/relational/mssql/source.cxx
@@ -307,6 +307,24 @@ namespace relational
}
virtual void
+ check_accessor (member_info& mi, member_access& ma)
+ {
+ // We cannot use accessors that return by-value for long data
+ // members.
+ //
+ if (long_data (*mi.st) && ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a data member of SQL Server long data "
+ << "type" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
traverse_integer (member_info& mi)
{
os << traits << "::set_image (" << endl
@@ -541,6 +559,23 @@ namespace relational
}
virtual void
+ check_modifier (member_info& mi, member_access& ma)
+ {
+ // We cannot use by-value modifier for long data members.
+ //
+ if (long_data (*mi.st) && ma.placeholder ())
+ {
+ error (ma.loc) << "modifier accepting a value cannot be used "
+ << "for a data member of SQL Server long data "
+ << "type" << endl;
+ info (ma.loc) << "modifier returning a non-const reference is "
+ << "required" << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
traverse_integer (member_info& mi)
{
os << traits << "::set_value (" << endl
diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx
index 780dc06..85be6cc 100644
--- a/odb/relational/oracle/source.cxx
+++ b/odb/relational/oracle/source.cxx
@@ -217,6 +217,26 @@ namespace relational
}
virtual void
+ check_accessor (member_info& mi, member_access& ma)
+ {
+ // We cannot use accessors that return by-value for LOB
+ // members.
+ //
+ if ((mi.st->type == sql_type::BLOB ||
+ mi.st->type == sql_type::CLOB ||
+ mi.st->type == sql_type::NCLOB) &&
+ ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a data member of Oracle LOB type" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
set_null (member_info& mi)
{
os << "i." << mi.var << "indicator = -1;";
@@ -363,6 +383,25 @@ namespace relational
}
virtual void
+ check_modifier (member_info& mi, member_access& ma)
+ {
+ // We cannot use by-value modifier for LOB members.
+ //
+ if ((mi.st->type == sql_type::BLOB ||
+ mi.st->type == sql_type::CLOB ||
+ mi.st->type == sql_type::NCLOB) &&
+ ma.placeholder ())
+ {
+ error (ma.loc) << "modifier accepting a value cannot be used "
+ << "for a data member of Oracle LOB type" << endl;
+ info (ma.loc) << "modifier returning a non-const reference is "
+ << "required" << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
traverse_int32 (member_info& mi)
{
os << traits << "::set_value (" << endl
diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx
index 64987a2..7e2bf53 100644
--- a/odb/relational/processor.cxx
+++ b/odb/relational/processor.cxx
@@ -120,8 +120,6 @@ namespace relational
if (transient (m))
return;
- process_index (m);
-
semantics::names* hint;
semantics::type& t (utype (m, hint));
@@ -151,146 +149,378 @@ namespace relational
m.set ("readonly", true);
}
- // Nothing to do if this is a composite value type.
+ // Determine the member kind.
//
- if (composite_wrapper (t))
- return;
-
- string type, id_type;
+ enum {simple, composite, container, unknown} kind (unknown);
- if (m.count ("id-type"))
- id_type = m.get<string> ("id-type");
+ // See if this is a composite value type.
+ //
+ if (composite_wrapper (t))
+ kind = composite;
- if (m.count ("type"))
+ // If not, see if it is a simple value.
+ //
+ if (kind == unknown)
{
- type = m.get<string> ("type");
+ string type, id_type;
- if (id_type.empty ())
- id_type = type;
- }
+ if (m.count ("id-type"))
+ id_type = m.get<string> ("id-type");
- if (semantics::class_* c = process_object_pointer (m, t))
- {
- // This is an object pointer. The column type is the pointed-to
- // object id type.
- //
- semantics::data_member& id (*id_member (*c));
+ if (m.count ("type"))
+ {
+ type = m.get<string> ("type");
- semantics::names* idhint;
- semantics::type& idt (utype (id, idhint));
+ if (id_type.empty ())
+ id_type = type;
+ }
- semantics::type* wt (0);
- semantics::names* whint (0);
- if (process_wrapper (idt))
+ if (semantics::class_* c = process_object_pointer (m, t))
{
- whint = idt.get<semantics::names*> ("wrapper-hint");
- wt = &utype (*idt.get<semantics::type*> ("wrapper-type"), whint);
+ // This is an object pointer. The column type is the pointed-to
+ // object id type.
+ //
+ semantics::data_member& id (*id_member (*c));
+
+ semantics::names* idhint;
+ semantics::type& idt (utype (id, idhint));
+
+ semantics::type* wt (0);
+ semantics::names* whint (0);
+ if (process_wrapper (idt))
+ {
+ whint = idt.get<semantics::names*> ("wrapper-hint");
+ wt = &utype (*idt.get<semantics::type*> ("wrapper-type"), whint);
+ }
+
+ // The id type can be a composite value type.
+ //
+ if (composite_wrapper (idt))
+ kind = composite;
+ else
+ {
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
+
+ if (type.empty () && id.count ("type"))
+ type = id.get<string> ("type");
+
+ // The rest should be identical to the code for the id_type in
+ // the else block.
+ //
+ if (type.empty () && idt.count ("id-type"))
+ type = idt.get<string> ("id-type");
+
+ if (type.empty () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
+
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (type.empty ())
+ type = database_type (idt, idhint, true);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, true);
+
+ id_type = type;
+ }
}
+ else
+ {
+ if (id_type.empty () && t.count ("id-type"))
+ id_type = t.get<string> ("id-type");
- // Nothing to do if this is a composite value type.
- //
- if (composite_wrapper (idt))
- return;
+ if (id_type.empty () && wt != 0 && wt->count ("id-type"))
+ id_type = wt->get<string> ("id-type");
- if (type.empty () && id.count ("id-type"))
- type = id.get<string> ("id-type");
+ if (type.empty () && t.count ("type"))
+ type = t.get<string> ("type");
- if (type.empty () && id.count ("type"))
- type = id.get<string> ("type");
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
- // The rest should be identical to the code for the id_type in
- // the else block.
- //
- if (type.empty () && idt.count ("id-type"))
- type = idt.get<string> ("id-type");
+ if (id_type.empty ())
+ id_type = type;
- if (type.empty () && wt != 0 && wt->count ("id-type"))
- type = wt->get<string> ("id-type");
+ if (id_type.empty ())
+ id_type = database_type (t, hint, true);
- if (type.empty () && idt.count ("type"))
- type = idt.get<string> ("type");
+ if (type.empty ())
+ type = database_type (t, hint, false);
- if (type.empty () && wt != 0 && wt->count ("type"))
- type = wt->get<string> ("type");
+ if (id_type.empty () && wt != 0)
+ id_type = database_type (*wt, whint, true);
- if (type.empty ())
- type = database_type (idt, idhint, true);
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, false);
- if (type.empty () && wt != 0)
- type = database_type (*wt, whint, true);
+ // Use id mapping for discriminators.
+ //
+ if (id (m) || discriminator (m))
+ type = id_type;
+ }
+
+ if (kind == unknown && !type.empty ())
+ {
+ m.set ("column-type", type);
+ m.set ("column-id-type", id_type);
+
+ // Issue a warning if we are relaxing null-ness.
+ //
+ if (m.count ("null") && t.count ("not-null"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " warning: data member declared null while its type is "
+ << "declared not null" << endl;
+ }
- id_type = type;
+ kind = simple;
+ }
}
- else
+
+ // If not a simple value, see if this is a container.
+ //
+ if (kind == unknown &&
+ (process_container (m, t) ||
+ (wt != 0 && process_container (m, *wt))))
+ kind = container;
+
+ // If it is none of the above then we have an error.
+ //
+ if (kind == unknown)
{
- if (id_type.empty () && t.count ("id-type"))
- id_type = t.get<string> ("id-type");
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unable to map C++ type '" << t.fq_name (hint)
+ << "' used in data member '" << m.name () << "' to a "
+ << "database type" << endl;
- if (id_type.empty () && wt != 0 && wt->count ("id-type"))
- id_type = wt->get<string> ("id-type");
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use '#pragma db type' to specify the database type"
+ << endl;
- if (type.empty () && t.count ("type"))
- type = t.get<string> ("type");
+ throw operation_failed ();
+ }
- if (type.empty () && wt != 0 && wt->count ("type"))
- type = wt->get<string> ("type");
+ process_access (m, "get");
+ process_access (m, "set");
+ process_index (m);
+ }
- if (id_type.empty ())
- id_type = type;
+ // Process member access expressions.
+ //
+ void
+ process_access (semantics::data_member& m, std::string const& k)
+ {
+ // If we don't have an access expression, synthesize one which
+ // goes directly for the member. Zero location indicates it is
+ // a synthesized one.
+ //
+ if (!m.count (k))
+ {
+ member_access& ma (m.set (k, member_access (0)));
+ ma.expr.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ ma.expr.push_back (cxx_token (0, CPP_DOT));
+ ma.expr.push_back (cxx_token (0, CPP_NAME, m.name ()));
+ return;
+ }
- if (id_type.empty ())
- id_type = database_type (t, hint, true);
+ semantics::type& t (utype (m));
+ member_access& ma (m.get<member_access> (k));
+ cxx_tokens& e (ma.expr);
- if (type.empty ())
- type = database_type (t, hint, false);
+ // If it is just a name, resolve it and convert to an
+ // appropriate expression.
+ //
+ if (e.size () == 1 && e.back ().type == CPP_NAME)
+ {
+ string n (e.back ().literal);
+ e.clear ();
- if (id_type.empty () && wt != 0)
- id_type = database_type (*wt, whint, true);
+ tree decl (
+ lookup_qualified_name (
+ m.scope ().tree_node (),
+ get_identifier (n.c_str ()),
+ false,
+ false));
- if (type.empty () && wt != 0)
- type = database_type (*wt, whint, false);
+ if (decl == error_mark_node)
+ {
+ error (ma.loc) << "unable to resolve data member or function "
+ << "name '" << n << "'" << endl;
+ throw operation_failed ();
+ }
- // Use id mapping for discriminators.
- //
- if (id (m) || discriminator (m))
- type = id_type;
+ switch (TREE_CODE (decl))
+ {
+ case FIELD_DECL:
+ {
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ break;
+ }
+ case BASELINK:
+ {
+ // 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));
+
+ // We are only interested in non-static member functions.
+ //
+ if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f))
+ continue;
+
+ // Note that we have to use TREE_TYPE(TREE_TYPE()) and
+ // not DECL_RESULT, as suggested by the documentation.
+ //
+ tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))));
+ tree a (DECL_ARGUMENTS (f));
+ a = DECL_CHAIN (a); // Skip this.
+
+ if (k == "get")
+ {
+ // For an accessor, look for a function with a non-void
+ // return value and no arguments (other than 'this').
+ //
+ if (r != void_type_node && a == NULL_TREE)
+ {
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN, n));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n));
+
+ // See if it returns by value.
+ //
+ int tc (TREE_CODE (r));
+ ma.by_value = (tc != REFERENCE_TYPE && tc != POINTER_TYPE);
+ break;
+ }
+ }
+ else
+ {
+ // For a modifier, it can either be a function that
+ // returns a non-const reference (or non-const pointer,
+ // in case the member is an array) or a proper modifier
+ // that sets a new value. If both are available, we
+ // prefer the former for efficiency.
+ //
+ semantics::array* ar (dynamic_cast<semantics::array*> (&t));
+ int tc (TREE_CODE (r));
+
+ if (a == NULL_TREE &&
+ ((ar == 0 && tc == REFERENCE_TYPE) ||
+ (ar != 0 && tc == POINTER_TYPE)))
+ {
+ tree bt (TREE_TYPE (r)); // Base type.
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if (!CP_TYPE_CONST_P (bt) &&
+ ((ar == 0 && bt_mv == t.tree_node ()) ||
+ (ar != 0 && bt_mv == ar->base_type ().tree_node ())))
+ {
+ e.clear (); // Could contain proper modifier.
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN, n));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n));
+ break;
+ }
+ }
+
+ // Any function with a single argument works for us.
+ // And we don't care what it returns.
+ //
+ if (a != NULL_TREE && DECL_CHAIN (a) == NULL_TREE)
+ {
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN, n));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n));
+ }
+
+ // Continue searching in case there is version that
+ // returns a non-const reference (which we prefer).
+ }
+ }
+ }
+
+ if (e.empty ())
+ {
+ error (ma.loc) << "unable to find suitable "
+ << (k == "get" ? "accessor" : "modifier")
+ << " function '" << n << "'" << endl;
+ throw operation_failed ();
+ }
+ break;
+ }
+ default:
+ {
+ error (ma.loc) << "name '" << n << "' does not refer to a data "
+ << "member or function" << endl;
+ throw operation_failed ();
+ }
+ }
}
- if (!type.empty ())
+ // If there is no 'this' keyword, then add it as a prefix.
+ //
{
- m.set ("column-type", type);
- m.set ("column-id-type", id_type);
-
- // Issue a warning if we are relaxing null-ness.
- //
- if (m.count ("null") && t.count ("not-null"))
+ bool t (false);
+ for (cxx_tokens::iterator i (e.begin ()); i != e.end (); ++i)
{
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " warning: data member declared null while its type is "
- << "declared not null" << endl;
+ if (i->type == CPP_KEYWORD && i->literal == "this")
+ {
+ t = true;
+ break;
+ }
}
- return;
+ if (!t)
+ {
+ e.insert (e.begin (), cxx_token (0, CPP_DOT));
+ e.insert (e.begin (), cxx_token (0, CPP_KEYWORD, "this"));
+ }
}
- // See if this is a container type.
+ // Check that there is no placeholder in the accessor expression.
//
- if (process_container (m, t) ||
- (wt != 0 && process_container (m, *wt)))
- return;
+ if (k == "get" && ma.placeholder ())
+ {
+ error (ma.loc) << "(?) placeholder in the accessor expression"
+ << endl;
+ throw operation_failed ();
+ }
- // If it is none of the above then we have an error.
+ // Check that the member type is default-constructible if we
+ // have a placeholder in the modifier.
//
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: unable to map C++ type '" << t.fq_name (hint)
- << "' used in data member '" << m.name () << "' to a "
- << "database type" << endl;
-
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " info: use '#pragma db type' to specify the database type"
- << endl;
+ if (k == "set" && ma.placeholder ())
+ {
+ // Assume all other types are default-constructible.
+ //
+ semantics::class_* c (dynamic_cast<semantics::class_*> (&t));
- throw operation_failed ();
+ if (c != 0 && !c->default_ctor ())
+ {
+ error (ma.loc) << "modifier expression requires member type "
+ << "to be default-constructible" << endl;
+ throw operation_failed ();
+ }
+ }
}
// Convert index/unique specifiers to the index entry in the object.
@@ -2480,9 +2710,7 @@ namespace relational
tt != CPP_EOF;
tt = lex_.next (t))
{
- cxx_token ct (lex_.location (), tt);
- ct.literal = t;
- i->cond.push_back (ct);
+ i->cond.push_back (cxx_token (lex_.location (), tt, t));
}
}
}
diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx
index a338b09..e525089 100644
--- a/odb/relational/source.cxx
+++ b/odb/relational/source.cxx
@@ -20,10 +20,13 @@ traverse_object (type& c)
data_member* id (id_member (c));
bool auto_id (id ? id->count ("auto") : false);
bool base_id (id ? &id->scope () != &c : false); // Comes from base.
+ member_access* id_ma (id ? &id->get<member_access> ("get") : 0);
bool has_ptr (has_a (c, test_pointer));
- data_member* optimistic (context::optimistic (c));
+ data_member* opt (optimistic (c));
+ member_access* opt_ma_get (opt ? &opt->get<member_access> ("get") : 0);
+ member_access* opt_ma_set (opt ? &opt->get<member_access> ("set") : 0);
type* poly_root (polymorphic (c));
bool poly (poly_root != 0);
@@ -42,7 +45,7 @@ traverse_object (type& c)
{
grow = context::grow (c);
grow_id = (id ? context::grow (*id) : false) ||
- (optimistic ? context::grow (*optimistic) : false);
+ (opt ? context::grow (*opt) : false);
}
string const& type (class_fq_name (c));
@@ -105,6 +108,8 @@ traverse_object (type& c)
//
if (!poly_derived && id != 0 && !base_id)
{
+ // id (image)
+ //
if (options.generate_query ())
{
os << traits << "::id_type" << endl
@@ -120,14 +125,16 @@ traverse_object (type& c)
<< "}";
}
- if (optimistic != 0)
+ // version (image)
+ //
+ if (opt != 0)
{
os << traits << "::version_type" << endl
<< traits << "::" << endl
<< "version (const image_type& i)"
<< "{"
<< "version_type v;";
- init_version_value_member_->traverse (*optimistic);
+ init_version_value_member_->traverse (*opt);
os << "return v;"
<< "}";
}
@@ -277,7 +284,7 @@ traverse_object (type& c)
{
os << "void " << traits << "::" << endl
<< "bind (" << bind_vector << " b, id_image_type& i" <<
- (optimistic != 0 ? ", bool bv" : "") << ")"
+ (opt != 0 ? ", bool bv" : "") << ")"
<< "{"
<< "std::size_t n (0);";
@@ -286,14 +293,14 @@ traverse_object (type& c)
bind_id_member_->traverse (*id);
- if (optimistic != 0)
+ if (opt != 0)
{
os << "if (bv)"
<< "{"
<< "n += " << column_count (c).id << ";"
<< endl;
- bind_version_member_->traverse (*optimistic);
+ bind_version_member_->traverse (*opt);
os << "}";
}
@@ -370,7 +377,7 @@ traverse_object (type& c)
{
os << "void " << traits << "::" << endl
<< "init (id_image_type& i, const id_type& id" <<
- (optimistic != 0 ? ", const version_type* v" : "") << ")"
+ (opt != 0 ? ", const version_type* v" : "") << ")"
<< "{";
if (grow_id)
@@ -381,13 +388,13 @@ traverse_object (type& c)
init_id_image_member_->traverse (*id);
- if (optimistic != 0)
+ if (opt != 0)
{
// Here we rely on the fact that init_image_member
// always wraps the statements in a block.
//
os << "if (v != 0)";
- init_version_image_member_->traverse (*optimistic);
+ init_version_image_member_->traverse (*opt);
}
if (grow_id)
@@ -625,8 +632,8 @@ traverse_object (type& c)
instance<object_columns> t (qtable, sk, sc);
t->traverse (*discriminator);
- if (optimistic != 0)
- t->traverse (*optimistic);
+ if (opt != 0)
+ t->traverse (*opt);
process_statement_columns (sc, statement_select);
}
@@ -696,10 +703,10 @@ traverse_object (type& c)
convert_to (qp->next (), i->type, *i->member));
}
- if (optimistic != 0 && !poly_derived)
+ if (opt != 0 && !poly_derived)
os << endl
- << strlit (" AND " + column_qname (*optimistic) + "=" +
- convert_to (qp->next (), *optimistic));
+ << strlit (" AND " + column_qname (*opt) + "=" +
+ convert_to (qp->next (), *opt));
os << ";"
<< endl;
}
@@ -725,7 +732,7 @@ traverse_object (type& c)
<< endl;
}
- if (optimistic != 0 && !poly_derived)
+ if (opt != 0 && !poly_derived)
{
instance<query_parameters> qp (table);
@@ -743,8 +750,8 @@ traverse_object (type& c)
}
os << endl
- << strlit (" AND " + column_qname (*optimistic) + "=" +
- convert_to (qp->next (), *optimistic)) << ";"
+ << strlit (" AND " + column_qname (*opt) + "=" +
+ convert_to (qp->next (), *opt)) << ";"
<< endl;
}
}
@@ -936,26 +943,66 @@ traverse_object (type& c)
if (!poly_derived && auto_id)
{
- if (const_type (id->type ()))
- os << "const_cast< id_type& > (obj." << id->name () << ")";
+ member_access& ma (id->get<member_access> ("set"));
+
+ if (ma.loc != 0)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (ma.placeholder ())
+ os << ma.translate ("obj", "static_cast< id_type > (st.id ())") << ";"
+ << endl;
else
- os << "obj." << id->name ();
+ {
+ // If this member is const and we have a synthesized access, then
+ // cast away constness. Otherwise, we assume that the user-provided
+ // expression handles this.
+ //
+ bool cast (ma.loc == 0 && const_type (id->type ()));
+ if (cast)
+ os << "const_cast< id_type& > (" << endl;
- os << " = static_cast< id_type > (st.id ());"
- << endl;
+ os << ma.translate ("obj");
+
+ if (cast)
+ os << ")";
+
+ os << " = static_cast< id_type > (st.id ());"
+ << endl;
+ }
}
- if (optimistic != 0 && !poly_derived)
+ // Set the optimistic concurrency version in the object member.
+ //
+ if (opt != 0 && !poly_derived)
{
- // Set the version in the object member.
+ // If we don't have auto id, then obj is a const reference.
//
- if (!auto_id || const_type (optimistic->type ()))
- os << "const_cast< version_type& > (obj." << optimistic->name () <<
- ") = 1;";
+ string obj (auto_id ? "obj" : "const_cast< object_type& > (obj)");
+
+ if (opt_ma_set->loc != 0)
+ os << "// From " << location_string (opt_ma_set->loc, true) << endl;
+
+ if (opt_ma_set->placeholder ())
+ os << opt_ma_set->translate (obj, "1") << ";"
+ << endl;
else
- os << "obj." << optimistic->name () << " = 1;";
+ {
+ // If this member is const and we have a synthesized access, then
+ // cast away constness. Otherwise, we assume that the user-provided
+ // expression handles this.
+ //
+ bool cast (opt_ma_set->loc == 0 && const_type (opt->type ()));
+ if (cast)
+ os << "const_cast< version_type& > (" << endl;
- os << endl;
+ os << opt_ma_set->translate (obj);
+
+ if (cast)
+ os << ")";
+
+ os << " = 1;"
+ << endl;
+ }
}
// Initialize id_image and binding if we are a root of a polymorphic
@@ -971,8 +1018,12 @@ traverse_object (type& c)
os << "if (!top)"
<< "{";
- os << "id_image_type& i (sts.id_image ());"
- << "init (i, obj." << id->name () << ");"
+ os << "id_image_type& i (sts.id_image ());";
+
+ if (id_ma->loc != 0)
+ os << "// From " << location_string (id_ma->loc, true) << endl;
+
+ os << "init (i, " << id_ma->translate ("obj") << ");"
<< endl
<< "binding& idb (sts.id_image_binding ());"
<< "if (i.version != sts.id_image_version () || idb.version == 0)"
@@ -1100,8 +1151,12 @@ traverse_object (type& c)
os << "if (!top)";
os << "{"
- << "id_image_type& i (sts.id_image ());"
- << "init (i, obj." << id->name () << ");"
+ << "id_image_type& i (sts.id_image ());";
+
+ if (id_ma->loc != 0)
+ os << "// From " << location_string (id_ma->loc, true) << endl;
+
+ os << "init (i, " << id_ma->translate ("obj") << ");"
<< endl;
os << "binding& idb (sts.id_image_binding ());"
@@ -1161,9 +1216,11 @@ traverse_object (type& c)
// exists in the database. Use the discriminator_() call for
// that.
//
- os << "root_traits::discriminator_ (sts.root_statements (), obj." <<
- id->name () << ", 0);"
- << endl;
+ if (id_ma->loc != 0)
+ os << "// From " << location_string (id_ma->loc, true) << endl;
+
+ os << "root_traits::discriminator_ (sts.root_statements (), " <<
+ id_ma->translate ("obj") << ", 0);" << endl;
}
// Otherwise, nothing else to do here if we don't have any columns
// to update.
@@ -1179,15 +1236,27 @@ traverse_object (type& c)
// Initialize object and id images.
//
+ if (opt != 0)
+ {
+ if (opt_ma_get->loc != 0)
+ os << "// From " << location_string (opt_ma_get->loc, true) << endl;
+
+ os << "const version_type& v (" << endl
+ << opt_ma_get->translate ("obj") << ");";
+ }
+
os << "id_image_type& i (sts.id_image ());";
- if (optimistic == 0)
- os << "init (i, obj." << id->name () << ");";
- else
- os << "init (i, obj." << id->name () << ", &obj." <<
- optimistic->name () << ");";
+ if (id_ma->loc != 0)
+ os << "// From " << location_string (id_ma->loc, true) << endl;
- os << endl
+ os << "init (i, " << id_ma->translate ("obj");
+
+ if (opt != 0)
+ os << ", &v";
+
+ os << ");"
+ << endl
<< "image_type& im (sts.image ());";
if (generate_grow)
@@ -1245,7 +1314,7 @@ traverse_object (type& c)
os << "if (sts.update_statement ().execute () == 0)" << endl;
- if (optimistic == 0)
+ if (opt == 0)
os << "throw object_not_persistent ();";
else
os << "throw object_changed ();";
@@ -1265,21 +1334,29 @@ traverse_object (type& c)
<< "conn.statement_cache ().find_object<object_type> ());"
<< endl;
+ if (id_ma->loc != 0)
+ os << "// From " << location_string (id_ma->loc, true) << endl;
+
+ os << "const id_type& id (" << endl
+ << id_ma->translate ("obj") << ");"
+ << endl;
+
if (poly)
{
// In case of a polymorphic root, use discriminator_(), which
// is faster. And initialize the id image, unless this is a
// top-level call.
//
- os << "discriminator_ (sts, obj." << id->name () << ", 0);"
+ os << "discriminator_ (sts, id, 0);"
<< endl;
if (!abst)
os << "if (!top)";
os << "{"
- << "id_image_type& i (sts.id_image ());"
- << "init (i, obj." << id->name () << ");"
+ << "id_image_type& i (sts.id_image ());";
+
+ os << "init (i, id);"
<< endl;
os << "binding& idb (sts.id_image_binding ());"
@@ -1293,7 +1370,7 @@ traverse_object (type& c)
}
else
{
- os << "if (!find_ (sts, &obj." << id->name () << "))" << endl
+ os << "if (!find_ (sts, &id))" << endl
<< "throw object_not_persistent ();"
<< endl;
@@ -1312,11 +1389,46 @@ traverse_object (type& c)
t->traverse (c);
}
- if (optimistic != 0 && !poly_derived)
+ // Update the optimistic concurrency version in the object member.
+ //
+ if (opt != 0 && !poly_derived)
{
- // Update version in the object member.
+ // Object is passed as const reference so we need to cast away
+ // constness.
//
- os << "const_cast<version_type&> (obj." << optimistic->name () << ")++;";
+ string obj ("const_cast< object_type& > (obj)");
+
+ if (opt_ma_set->loc != 0)
+ os << "// From " << location_string (opt_ma_set->loc, true) << endl;
+
+ if (opt_ma_set->placeholder ())
+ {
+ if (opt_ma_get->loc != 0)
+ os << "// From " << location_string (opt_ma_get->loc, true) <<
+ endl;
+
+ os << opt_ma_set->translate (
+ obj, opt_ma_get->translate ("obj") + " + 1") << ";"
+ << endl;
+ }
+ else
+ {
+ // If this member is const and we have a synthesized access, then
+ // cast away constness. Otherwise, we assume that the user-provided
+ // expression handles this.
+ //
+ bool cast (opt_ma_set->loc == 0 && const_type (opt->type ()));
+ if (cast)
+ os << "const_cast< version_type& > (" << endl;
+
+ os << opt_ma_set->translate (obj);
+
+ if (cast)
+ os << ")";
+
+ os << "++;"
+ << endl;
+ }
}
// Call callback (post_update).
@@ -1457,7 +1569,7 @@ traverse_object (type& c)
// erase (object)
//
- if (id != 0 && (poly || optimistic != 0))
+ if (id != 0 && (poly || opt != 0))
{
os << "void " << traits << "::" << endl
<< "erase (database& db, const object_type& obj";
@@ -1497,7 +1609,7 @@ traverse_object (type& c)
// Determine the dynamic type of this object.
//
- if (optimistic == 0)
+ if (opt == 0)
{
os << "callback (db, obj, callback_event::pre_erase);"
<< "erase (db, id (obj), true, false);"
@@ -1530,6 +1642,16 @@ traverse_object (type& c)
<< endl;
}
+ if (!abst || straight_containers)
+ {
+ if (id_ma->loc != 0)
+ os << "// From " << location_string (id_ma->loc, true) << endl;
+
+ os << "const id_type& id (" << endl
+ << id_ma->translate ("obj") << ");"
+ << endl;
+ }
+
// Initialize id + managed column image.
//
os << "binding& idb (" << rsts << ".id_image_binding ());"
@@ -1541,9 +1663,13 @@ traverse_object (type& c)
os << "if (top)"
<< "{";
- os << "id_image_type& i (" << rsts << ".id_image ());"
- << "init (i, obj." << id->name () << ", &obj." <<
- optimistic->name () << ");"
+ if (opt_ma_get->loc != 0)
+ os << "// From " << location_string (opt_ma_get->loc, true) << endl;
+
+ os << "const version_type& v (" << endl
+ << opt_ma_get->translate ("obj") << ");"
+ << "id_image_type& i (" << rsts << ".id_image ());"
+ << "init (i, id, &v);"
<< endl;
// To update the id part of the optimistic id binding we have
@@ -1592,10 +1718,13 @@ traverse_object (type& c)
os << "if (top)"
<< "{"
<< "version_type v;"
- << "root_traits::discriminator_ (" << rsts << ", obj." <<
- id->name () << ", 0, &v);"
- << endl
- << "if (v != obj." << optimistic->name () << ")" << endl
+ << "root_traits::discriminator_ (" << rsts << ", id, 0, &v);"
+ << endl;
+
+ if (opt_ma_get->loc != 0)
+ os << "// From " << location_string (opt_ma_get->loc, true) << endl;
+
+ os << "if (v != " << opt_ma_get->translate ("obj") << ")" << endl
<< "throw object_changed ();"
<< "}";
}
@@ -1618,15 +1747,19 @@ traverse_object (type& c)
// have been more efficient but it would complicated and bloat
// things significantly.
//
- os << "if (!find_ (sts, &obj." << id->name () << "))" << endl
+ os << "if (!find_ (sts, &id))" << endl
<< "throw object_changed ();"
<< endl;
if (delay_freeing_statement_result)
- os << "sts.find_statement ().free_result ();";
+ os << "sts.find_statement ().free_result ();"
+ << endl;
+
+ if (opt_ma_get->loc != 0)
+ os << "// From " << location_string (opt_ma_get->loc, true) << endl;
- os << "if (version (sts.image ()) != obj." <<
- optimistic->name () << ")" << endl
+ os << "if (version (sts.image ()) != " <<
+ opt_ma_get->translate ("obj") << ")" << endl
<< "throw object_changed ();"
<< endl;
}
@@ -1666,7 +1799,7 @@ traverse_object (type& c)
// Remove from the object cache.
//
- os << "pointer_cache_traits::erase (db, obj." << id->name () << ");";
+ os << "pointer_cache_traits::erase (db, id);";
// Call callback (post_erase).
//
@@ -2015,7 +2148,14 @@ traverse_object (type& c)
<< "statements_type::auto_lock l (" << rsts << ");"
<< endl;
- os << "if (!find_ (sts, &obj." << id->name () << "))" << endl
+ if (id_ma->loc != 0)
+ os << "// From " << location_string (id_ma->loc, true) << endl;
+
+ os << "const id_type& id (" << endl
+ << id_ma->translate ("obj") << ");"
+ << endl;
+
+ os << "if (!find_ (sts, &id))" << endl
<< "return false;"
<< endl;
@@ -2028,10 +2168,14 @@ traverse_object (type& c)
os << "auto_result ar (st);"
<< endl;
- if (optimistic != 0)
+ if (opt != 0)
{
+ if (opt_ma_get->loc != 0)
+ os << "// From " << location_string (opt_ma_get->loc, true) << endl;
+
os << "if (" << (poly_derived ? "root_traits::" : "") << "version (" <<
- rsts << ".image ()) == obj." << optimistic->name () << ")" << endl
+ rsts << ".image ()) == " << opt_ma_get->translate ("obj") <<
+ ")" << endl
<< "return true;"
<< endl;
}
@@ -2272,7 +2416,7 @@ traverse_object (type& c)
<< "const id_type& id," << endl
<< "discriminator_type* pd";
- if (optimistic != 0)
+ if (opt != 0)
os << "," << endl
<< "version_type* pv";
@@ -2291,7 +2435,7 @@ traverse_object (type& c)
<< "if (idi.version != sts.discriminator_id_image_version () ||" << endl
<< "idb.version == 0)"
<< "{"
- << "bind (idb.bind, idi" << (optimistic != 0 ? ", false" : "") << ");"
+ << "bind (idb.bind, idi" << (opt != 0 ? ", false" : "") << ");"
<< "sts.discriminator_id_image_version (idi.version);"
<< "idb.version++;"
<< "}";
@@ -2313,11 +2457,11 @@ traverse_object (type& c)
bind_discriminator_member_->traverse (*discriminator);
os << "}";
- if (optimistic != 0)
+ if (opt != 0)
{
os << "n++;" // For now discriminator is a simple value.
<< "{";
- bind_version_member_->traverse (*optimistic);
+ bind_version_member_->traverse (*opt);
os << "}";
}
@@ -2334,7 +2478,7 @@ traverse_object (type& c)
<< "if (r == select_statement::no_data)"
<< "{";
- if (optimistic != 0)
+ if (opt != 0)
os << "if (pv != 0)" << endl
<< "throw object_changed ();"
<< "else" << endl;
@@ -2342,9 +2486,9 @@ traverse_object (type& c)
os << "throw object_not_persistent ();"
<< "}";
- if (generate_grow && (
- context::grow (*discriminator) ||
- (optimistic != 0 && context::grow (*optimistic))))
+ if (generate_grow &&
+ (context::grow (*discriminator) ||
+ (opt != 0 && context::grow (*opt))))
{
os << "else if (r == select_statement::truncated)"
<< "{";
@@ -2358,8 +2502,8 @@ traverse_object (type& c)
index_ = 0;
grow_discriminator_member_->traverse (*discriminator);
- if (optimistic != 0)
- grow_version_member_->traverse (*optimistic);
+ if (opt != 0)
+ grow_version_member_->traverse (*opt);
os << "if (grew)" << endl
<< "i.version++;"
@@ -2375,11 +2519,11 @@ traverse_object (type& c)
bind_discriminator_member_->traverse (*discriminator);
os << "}";
- if (optimistic != 0)
+ if (opt != 0)
{
os << "n++;" // For now discriminator is a simple value.
<< "{";
- bind_version_member_->traverse (*optimistic);
+ bind_version_member_->traverse (*opt);
os << "}";
}
@@ -2404,12 +2548,12 @@ traverse_object (type& c)
init_named_discriminator_value_member_->traverse (*discriminator);
os << "}";
- if (optimistic != 0)
+ if (opt != 0)
{
os << "if (pv != 0)"
<< "{"
<< "version_type& v (*pv);";
- init_named_version_value_member_->traverse (*optimistic);
+ init_named_version_value_member_->traverse (*opt);
os << "}";
}
@@ -2618,10 +2762,7 @@ traverse_view (type& c)
else
// Output the pragma location for easier error tracking.
//
- os << "// From " <<
- location_file (vq.loc).leaf () << ":" <<
- location_line (vq.loc) << ":" <<
- location_column (vq.loc) << endl
+ os << "// From " << location_string (vq.loc, true) << endl
<< translate_expression (
c, vq.expr, vq.scope, vq.loc, "query", &ph).value;
@@ -2707,13 +2848,10 @@ traverse_view (type& c)
l += " ON";
+ // Output the pragma location for easier error tracking.
+ //
os << "r += " << strlit (l) << ";"
- // Output the pragma location for easier error tracking.
- //
- << "// From " <<
- location_file (i->loc).leaf () << ":" <<
- location_line (i->loc) << ":" <<
- location_column (i->loc) << endl
+ << "// From " << location_string (i->loc, true) << endl
<< "r += " << e.value << ";"
<< endl;
@@ -2775,13 +2913,10 @@ traverse_view (type& c)
l += " ON";
+ // Output the pragma location for easier error tracking.
+ //
os << "r += " << strlit (l) << ";"
- // Output the pragma location for easier error tracking.
- //
- << "// From " <<
- location_file (i->loc).leaf () << ":" <<
- location_line (i->loc) << ":" <<
- location_column (i->loc) << endl
+ << "// From " << location_string (i->loc, true) << endl
<< "r += " << e.value << ";";
if (poly_depth != 1)
@@ -3210,10 +3345,7 @@ traverse_view (type& c)
{
// Output the pragma location for easier error tracking.
//
- os << "// From " <<
- location_file (vq.loc).leaf () << ":" <<
- location_line (vq.loc) << ":" <<
- location_column (vq.loc) << endl
+ os << "// From " << location_string (vq.loc, true) << endl
<< translate_expression (
c, vq.expr, vq.scope, vq.loc, "query", &ph).value;
@@ -3596,6 +3728,9 @@ namespace relational
string const& prag,
bool* placeholder)
{
+ // This code is similar to translate() from context.cxx.
+ //
+
// The overall idea is as folows: read in tokens and add them
// to the string. If a token starts a name, try to resolve it
// to an object member (taking into account aliases). If this
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index a1f1190..38bb80f 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -1127,6 +1127,9 @@ namespace relational
virtual void
set_null (member_info&) = 0;
+ virtual void
+ check_accessor (member_info&, member_access&) {}
+
virtual bool
pre (member_info& mi)
{
@@ -1142,8 +1145,13 @@ namespace relational
if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
return false;
+ bool comp (composite (mi.t));
+
if (!member_override_.empty ())
+ {
member = member_override_;
+ os << "{";
+ }
else
{
// If we are generating standard init() and this member
@@ -1158,9 +1166,7 @@ namespace relational
if (!insert_send_auto_id && id (mi.m) && auto_ (mi.m))
return false;
- string const& name (mi.m.name ());
-
- os << "// " << name << endl
+ os << "// " << mi.m.name () << endl
<< "//" << endl;
// If the whole class is readonly, then we will never be
@@ -1176,13 +1182,37 @@ namespace relational
os << "if (sk == statement_insert)";
}
+ os << "{";
+
if (discriminator (mi.m))
member = "di.discriminator";
else
- member = "o." + name;
- }
+ {
+ // Get the member using the accessor expression.
+ //
+ member_access& ma (mi.m.template get<member_access> ("get"));
- bool comp (composite (mi.t));
+ // Make sure this kind of member can be accessed with this
+ // kind of accessor (database-specific, e.g., streaming).
+ //
+ if (!comp)
+ check_accessor (mi, ma);
+
+ // If this is not a synthesized expression, then output
+ // its location for easier error tracking.
+ //
+ if (ma.loc != 0)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // Use the original type to form the const reference.
+ //
+ os << member_ref_type (mi.m, true, "v") << " (" << endl
+ << ma.translate ("o") << ");"
+ << endl;
+
+ member = "v";
+ }
+ }
// If this is a wrapped composite value, then we need to "unwrap"
// it. If this is a NULL wrapper, then we also need to handle that.
@@ -1205,14 +1235,13 @@ namespace relational
member << "))" << endl
<< "composite_value_traits< " + mi.fq_type () + " >::" <<
"set_null (i." << mi.var << "value, sk);"
- << "else";
+ << "else"
+ << "{";
}
member = "wrapper_traits< " + wt + " >::get_ref (" + member + ")";
}
- os << "{";
-
if (discriminator (mi.m))
os << "const info_type& di (map->find (typeid (o)));"
<< endl;
@@ -1301,6 +1330,13 @@ namespace relational
set_null (mi);
}
+ if (mi.wrapper != 0 && composite (mi.t))
+ {
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ os << "}";
+ }
+
os << "}";
}
@@ -1430,6 +1466,9 @@ namespace relational
virtual void
get_null (member_info&) = 0;
+ virtual void
+ check_modifier (member_info&, member_access&) {}
+
virtual bool
pre (member_info& mi)
{
@@ -1447,25 +1486,66 @@ namespace relational
if (ignore_implicit_discriminator_ && discriminator (mi.m))
return false;
+ bool comp (composite (mi.t));
+
if (!member_override_.empty ())
+ {
+ os << "{";
member = member_override_;
+ }
else
{
- string const& name (mi.m.name ());
- member = "o." + name;
+ os << "// " << mi.m.name () << endl
+ << "//" << endl
+ << "{";
+
+ // Get the member using the accessor expression.
+ //
+ member_access& ma (mi.m.template get<member_access> ("set"));
+
+ // Make sure this kind of member can be modified with this
+ // kind of accessor (database-specific, e.g., streaming).
+ //
+ if (!comp)
+ check_modifier (mi, ma);
- if (mi.cq)
+ // If this is not a synthesized expression, then output
+ // its location for easier error tracking.
+ //
+ if (ma.loc != 0)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // See if we are modifying via a reference or proper modifier.
+ //
+ if (ma.placeholder ())
+ os << member_val_type (mi.m, false, "v") << ";"
+ << endl;
+ else
{
- string t (mi.ptr == 0 ? mi.fq_type (false) : mi.ptr_fq_type ());
- member = "const_cast< " + t + "& > (" + member + ")";
+ // Use the original type to form the reference.
+ //
+ os << member_ref_type (mi.m, false, "v") << " (" << endl;
+
+ // If this member is const and we have a synthesized access,
+ // then cast away constness. Otherwise, we assume that the
+ // user-provided expression handles this.
+ //
+ if (mi.cq && ma.loc == 0)
+ os << "const_cast< " << member_ref_type (mi.m, false) <<
+ " > (" << endl;
+
+ os << ma.translate ("o");
+
+ if (mi.cq && ma.loc == 0)
+ os << ")";
+
+ os << ");"
+ << endl;
}
- os << "// " << name << endl
- << "//" << endl;
+ member = "v";
}
- bool comp (composite (mi.t));
-
// If this is a wrapped composite value, then we need to "unwrap" it.
// If this is a NULL wrapper, then we also need to handle that. For
// simple values this is taken care of by the value_traits
@@ -1498,8 +1578,7 @@ namespace relational
// Handle NULL pointers and extract the id.
//
- os << "{"
- << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
+ os << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
" > obj_traits;"
<< "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
" > ptr_traits;"
@@ -1548,16 +1627,9 @@ namespace relational
{
if (mi.ptr != 0)
{
- if (!member_override_.empty ())
- member = member_override_;
- else
- {
- member = "o." + mi.m.name ();
-
- if (mi.cq)
- member = "const_cast< " + mi.ptr_fq_type () +
- "& > (" + member + ")";
- }
+ // Restore the member variable name.
+ //
+ member = member_override_.empty () ? "v" : member_override_;
// When handling a pointer, mi.t is the id type of the referenced
// object.
@@ -1592,9 +1664,28 @@ namespace relational
}
}
- os << "}"
- << "}";
+ os << "}";
+ }
+
+ // Call the modifier if we are using a proper one.
+ //
+ if (member_override_.empty ())
+ {
+ member_access& ma (mi.m.template get<member_access> ("set"));
+
+ if (ma.placeholder ())
+ {
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (ma.loc != 0)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("o", "v") << ";";
+ }
}
+
+ os << "}";
}
virtual void
@@ -2867,9 +2958,10 @@ namespace relational
};
container_calls (call_type call)
- : object_members_base (true, false, false),
+ : object_members_base (true, false, true),
call_ (call),
- obj_prefix_ ("obj.")
+ obj_prefix_ ("obj"),
+ modifier_ (0)
{
}
@@ -2878,17 +2970,56 @@ namespace relational
semantics::class_& c,
semantics::type* w)
{
- if (m == 0)
+ if (m == 0 || call_ == erase_call || modifier_ != 0)
{
object_members_base::traverse_composite (m, c);
return;
}
- string old (obj_prefix_);
- obj_prefix_ += m->name ();
+ // Get this member using the accessor expression.
+ //
+ member_access& ma (
+ m->get<member_access> (call_ == load_call ? "set" : "get"));
+
+ // We don't support by-value modifiers for composite values
+ // with containers. However, at this point we don't know
+ // whether this composite value has any containers. So we
+ // are just going to set a flag that can be checked in
+ // traverse_container() below.
+ //
+ if (ma.placeholder ())
+ {
+ modifier_ = &ma;
+ object_members_base::traverse_composite (m, c);
+ modifier_ = 0;
+ return;
+ }
+
+ string old_op (obj_prefix_);
+ string old_f (from_);
+ obj_prefix_.clear ();
- // If this is a wrapped composite value, then we need to
- // "unwrap" it.
+ // If this member is const and we have a synthesized access,
+ // then cast away constness. Otherwise, we assume that the
+ // user-provided expression handles this.
+ //
+ if (call_ == load_call && ma.loc == 0 && const_type (m->type ()))
+ obj_prefix_ = "const_cast< " + member_ref_type (*m, false) +
+ " > (\n";
+
+ obj_prefix_ += ma.translate (old_op);
+
+ if (call_ == load_call && ma.loc == 0 && const_type (m->type ()))
+ obj_prefix_ += ")";
+
+ // If this is not a synthesized expression, then store its
+ // location which we will output later for easier error
+ // tracking.
+ //
+ if (ma.loc != 0)
+ from_ += "// From " + location_string (ma.loc, true) + "\n";
+
+ // If this is a wrapped composite value, then we need to "unwrap" it.
//
if (w != 0)
{
@@ -2900,26 +3031,14 @@ namespace relational
//
assert (&t == w);
- string const& type (t.fq_name (hint));
-
- if (call_ == load_call && const_type (m->type ()))
- obj_prefix_ = "const_cast< " + type + "& > (\n" +
- obj_prefix_ + ")";
-
- obj_prefix_ = "wrapper_traits< " + type + " >::" +
+ obj_prefix_ = "wrapper_traits< " + t.fq_name (hint) + " >::" +
(call_ == load_call ? "set_ref" : "get_ref") +
" (\n" + obj_prefix_ + ")";
}
- else if (call_ == load_call && const_type (m->type ()))
- {
- obj_prefix_ = "const_cast< " + class_fq_name (c) + "& > (\n" +
- obj_prefix_ + ")";
- }
-
- obj_prefix_ += '.';
object_members_base::traverse_composite (m, c);
- obj_prefix_ = old;
+ from_ = old_f;
+ obj_prefix_ = old_op;
}
virtual void
@@ -2929,87 +3048,155 @@ namespace relational
bool inverse (context::inverse (m, "value"));
+ // In certain cases we don't need to do anything.
+ //
+ if ((call_ != load_call && inverse) ||
+ (call_ == update_call && readonly (member_path_, member_scope_)))
+ return;
+
string const& name (m.name ());
- string obj_name (obj_prefix_ + name);
string sts_name (flat_prefix_ + name);
string traits (flat_prefix_ + public_name (m) + "_traits");
- if (call_ == load_call && const_type (m.type ()))
- {
-
- }
+ os << "// " << member_prefix_ << m.name () << endl
+ << "//" << endl;
- semantics::names* hint;
- semantics::type& t (utype (m, hint));
+ // Get this member using the accessor expression.
+ //
+ string var;
+ member_access& ma (
+ m.get<member_access> (call_ == load_call ? "set" : "get"));
- // If this is a wrapped container, then we need to "unwrap" it.
+ // We don't support by-value modifiers for composite values
+ // with containers.
//
- if (wrapper (t))
+ if (call_ == load_call && modifier_ != 0)
+ {
+ error (modifier_->loc) << "by-value modification of a composite "
+ << "value with container is not supported"
+ << endl;
+ info (m.location ()) << "container member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ if (call_ != erase_call)
{
- string const& type (t.fq_name (hint));
+ os << "{";
- // We cannot use traits::container_type here.
+ // Output stored locations, if any.
//
- if (call_ == load_call && const_type (m.type ()))
- obj_name = "const_cast< " + type + "& > (\n" + obj_name + ")";
+ if (!ma.placeholder ())
+ os << from_;
- obj_name = "wrapper_traits< " + type + " >::" +
- (call_ == load_call ? "set_ref" : "get_ref") +
- " (\n" + obj_name + ")";
- }
- else if (call_ == load_call && const_type (m.type ()))
- {
- obj_name = "const_cast< " + traits + "::container_type& > (\n" +
- obj_name + ")";
- }
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (ma.loc != 0)
+ os << "// From " << location_string (ma.loc, true) << endl;
+ // See if we are modifying via a reference or proper modifier.
+ //
+ if (ma.placeholder ())
+ os << member_val_type (m, false, "v") << ";"
+ << endl;
+ else
+ {
+ os << member_ref_type (m, call_ != load_call, "v") << " (" << endl;
+
+ // If this member is const and we have a synthesized access,
+ // then cast away constness. Otherwise, we assume that the
+ // user-provided expression handles this.
+ //
+ if (call_ == load_call && ma.loc == 0 && const_type (m.type ()))
+ os << "const_cast< " << member_ref_type (m, false) <<
+ " > (" << endl;
+
+ os << ma.translate (obj_prefix_);
+
+ if (call_ == load_call && ma.loc == 0 && const_type (m.type ()))
+ os << ")";
+
+ os << ");"
+ << endl;
+ }
+
+ var = "v";
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ // If this is a wrapped container, then we need to "unwrap" it.
+ //
+ if (wrapper (t))
+ {
+ var = "wrapper_traits< " + t.fq_name (hint) + " >::" +
+ (call_ == load_call ? "set_ref" : "get_ref") + " (" + var + ")";
+ }
+ }
switch (call_)
{
case persist_call:
{
- if (!inverse)
- os << traits << "::persist (" << endl
- << obj_name << "," << endl
- << "idb," << endl
- << "sts.container_statment_cache ()." << sts_name << ");"
- << endl;
+ os << traits << "::persist (" << endl
+ << var << "," << endl
+ << "idb," << endl
+ << "sts.container_statment_cache ()." << sts_name << ");";
break;
}
case load_call:
{
os << traits << "::load (" << endl
- << obj_name << "," << endl
+ << var << "," << endl
<< "idb," << endl
- << "sts.container_statment_cache ()." << sts_name << ");"
- << endl;
+ << "sts.container_statment_cache ()." << sts_name << ");";
break;
}
case update_call:
{
- if (!(inverse || readonly (member_path_, member_scope_)))
- os << traits << "::update (" << endl
- << obj_name << "," << endl
- << "idb," << endl
- << "sts.container_statment_cache ()." << sts_name << ");"
- << endl;
+ os << traits << "::update (" << endl
+ << var << "," << endl
+ << "idb," << endl
+ << "sts.container_statment_cache ()." << sts_name << ");";
break;
}
case erase_call:
{
- if (!inverse)
- os << traits << "::erase (" << endl
- << "idb," << endl
- << "sts.container_statment_cache ()." << sts_name << ");"
- << endl;
+ os << traits << "::erase (" << endl
+ << "idb," << endl
+ << "sts.container_statment_cache ()." << sts_name << ");"
+ << endl;
break;
}
}
+
+ if (call_ != erase_call)
+ {
+ // Call the modifier if we are using a proper one.
+ //
+ if (ma.placeholder ())
+ {
+ os << endl
+ << from_;
+
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (ma.loc != 0)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate (obj_prefix_, "v") << ";";
+ }
+
+ os << "}";
+ }
}
protected:
call_type call_;
string obj_prefix_;
+ string from_;
+ member_access* modifier_;
};
// Output a list of parameters for the persist statement.
diff --git a/odb/semantics/derived.cxx b/odb/semantics/derived.cxx
index 562d62e..3a08c3f 100644
--- a/odb/semantics/derived.cxx
+++ b/odb/semantics/derived.cxx
@@ -2,9 +2,13 @@
// copyright : Copyright (c) 2009-2012 Code Synthesis Tools CC
// license : GNU GPL v3; see accompanying LICENSE file
+#include <sstream>
+
#include <cutl/compiler/type-info.hxx>
#include <odb/semantics/derived.hxx>
+using namespace std;
+
namespace semantics
{
qualifies::
@@ -25,8 +29,42 @@ namespace semantics
{
}
+ string qualifier::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string'. Instead it prints 'const string'.
+ //
+ type& bt (base_type ());
+
+ // Use the trailing qualifier syntax so that we don't get bogged down
+ // in stuff like 'const const foo*'. We also have to handle arrays in
+ // a special way since char[16] const is not a legal syntax.
+ //
+ string q;
+ if (c_)
+ q += " const";
+
+ if (v_)
+ q += " volatile";
+
+ if (r_)
+ q += " __restrict";
+
+ hint = qualifies ().hint ();
+
+ if (array* a = dynamic_cast<array*> (&bt))
+ return a->fq_name (hint, q);
+ else
+ return bt.fq_name (hint) + q;
+ }
+
points::
points ()
+ : hint_ (0)
{
}
@@ -36,8 +74,23 @@ namespace semantics
{
}
+ string pointer::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string*'. Instead it prints 'const string*'.
+ //
+ string r (base_type ().fq_name (points ().hint ()));
+ r += '*';
+ return r;
+ }
+
references::
references ()
+ : hint_ (0)
{
}
@@ -47,8 +100,23 @@ namespace semantics
{
}
+ string reference::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string&'. Instead it prints 'const string&'.
+ //
+ string r (base_type ().fq_name (points ().hint ()));
+ r += '&';
+ return r;
+ }
+
contains::
contains ()
+ : hint_ (0)
{
}
@@ -62,6 +130,44 @@ namespace semantics
{
}
+ string array::
+ fq_name (names* hint) const
+ {
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string[123]'. Instead it prints 'const string[123]'.
+ //
+ string t;
+ return fq_name (hint, t);
+ }
+
+ string array::
+ fq_name (names* hint, string& t) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint) + t;
+
+ t += '[';
+ ostringstream ostr;
+ ostr << size ();
+ t += ostr.str ();
+
+ if (size () > 0xFFFFFFFF)
+ t += "ULL";
+ else if (size () > 2147483647)
+ t += "U";
+
+ t += ']';
+
+ type& bt (base_type ());
+ hint = contains ().hint ();
+ array* a;
+
+ if (hint != 0 || (a = dynamic_cast<array*> (&bt)) == 0)
+ return bt.fq_name (hint) + t;
+ else
+ return a->fq_name (0, t);
+ }
+
// type info
//
namespace
diff --git a/odb/semantics/derived.hxx b/odb/semantics/derived.hxx
index 7551cd4..4300290 100644
--- a/odb/semantics/derived.hxx
+++ b/odb/semantics/derived.hxx
@@ -114,6 +114,10 @@ namespace semantics
}
public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
qualifier (path const&,
size_t line,
size_t column,
@@ -155,6 +159,21 @@ namespace semantics
return *pointer_;
}
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
public:
points ();
@@ -173,6 +192,7 @@ namespace semantics
protected:
type_type* type_;
pointer_type* pointer_;
+ names* hint_;
};
class pointer: public derived_type
@@ -193,6 +213,10 @@ namespace semantics
}
public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
pointer (path const&, size_t line, size_t column, tree);
void
@@ -228,6 +252,21 @@ namespace semantics
return *reference_;
}
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
public:
references ();
@@ -246,6 +285,7 @@ namespace semantics
protected:
type_type* type_;
reference_type* reference_;
+ names* hint_;
};
class reference: public derived_type
@@ -266,6 +306,10 @@ namespace semantics
}
public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
reference (path const&, size_t line, size_t column, tree);
void
@@ -301,6 +345,21 @@ namespace semantics
return *array_;
}
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
public:
contains ();
@@ -319,6 +378,7 @@ namespace semantics
protected:
type_type* type_;
array_type* array_;
+ names* hint_;
};
class array: public derived_type
@@ -348,6 +408,16 @@ namespace semantics
}
public:
+ virtual string
+ fq_name (names*) const;
+
+ private:
+ friend class qualifier;
+
+ string
+ fq_name (names*, string& trailer) const;
+
+ public:
array (path const&,
size_t line,
size_t column,
diff --git a/odb/semantics/elements.hxx b/odb/semantics/elements.hxx
index ac62ff9..c9aa00e 100644
--- a/odb/semantics/elements.hxx
+++ b/odb/semantics/elements.hxx
@@ -495,7 +495,7 @@ namespace semantics
string
fq_name_ (scope_entry const*) const;
- private:
+ protected:
defines* defined_;
names_list named_;
};