From eacf52a9a4f3832274fdefc909ab23c13413e128 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 15 Aug 2012 11:46:00 +0200 Subject: Add support for member accessors/modifiers New pragmas: get, set, access. New test: common/access. --- odb/context.cxx | 386 ++++++++++++++++++++++++++++++++++ odb/context.hxx | 66 +++++- odb/cxx-token.hxx | 6 +- odb/diagnostics.cxx | 42 +++- odb/diagnostics.hxx | 18 ++ odb/parser.cxx | 48 ++++- odb/pragma.cxx | 62 +++++- odb/relational/inline.hxx | 18 +- odb/relational/mssql/source.cxx | 35 ++++ odb/relational/oracle/source.cxx | 39 ++++ odb/relational/processor.cxx | 432 ++++++++++++++++++++++++++++++--------- odb/relational/source.cxx | 339 +++++++++++++++++++++--------- odb/relational/source.hxx | 379 +++++++++++++++++++++++++--------- odb/semantics/derived.cxx | 106 ++++++++++ odb/semantics/derived.hxx | 70 +++++++ odb/semantics/elements.hxx | 2 +- 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 // std::toupper #include +#include #include #include #include +#include #include #include @@ -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 (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 (&utype (t))) + { + semantics::type& bt (a->base_type ()); + hint = a->contains ().hint (); + + if (bt.is_a ()) + { + // 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 (&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 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 + +#include + #include #include 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 +#include #include #include @@ -65,6 +66,23 @@ warn (cxx_lexer&); std::ostream& info (cxx_lexer&); +// Location as a string in the "::" 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 (file, line, clmn, t, size)); unit_->insert (t, a); - unit_->new_edge (a, bt); + contains& edge (unit_->new_edge (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 (file, line, clmn, t)); unit_->insert (t, ref); - unit_->new_edge (ref, bt); + references& edge (unit_->new_edge (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 (file, line, clmn, t)); unit_->insert (t, p); - unit_->new_edge (p, bt); + points& edge (unit_->new_edge (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 ().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 #include #include @@ -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 ("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 ("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 ("type"); + string type, id_type; - if (id_type.empty ()) - id_type = type; - } + if (m.count ("id-type")) + id_type = m.get ("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 ("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 ("wrapper-hint"); - wt = &utype (*idt.get ("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 ("wrapper-hint"); + wt = &utype (*idt.get ("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 ("id-type"); + + if (type.empty () && id.count ("type")) + type = id.get ("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 ("id-type"); + + if (type.empty () && wt != 0 && wt->count ("id-type")) + type = wt->get ("id-type"); + + if (type.empty () && idt.count ("type")) + type = idt.get ("type"); + + if (type.empty () && wt != 0 && wt->count ("type")) + type = wt->get ("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 ("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 ("id-type"); - if (type.empty () && id.count ("id-type")) - type = id.get ("id-type"); + if (type.empty () && t.count ("type")) + type = t.get ("type"); - if (type.empty () && id.count ("type")) - type = id.get ("type"); + if (type.empty () && wt != 0 && wt->count ("type")) + type = wt->get ("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 ("id-type"); + if (id_type.empty ()) + id_type = type; - if (type.empty () && wt != 0 && wt->count ("id-type")) - type = wt->get ("id-type"); + if (id_type.empty ()) + id_type = database_type (t, hint, true); - if (type.empty () && idt.count ("type")) - type = idt.get ("type"); + if (type.empty ()) + type = database_type (t, hint, false); - if (type.empty () && wt != 0 && wt->count ("type")) - type = wt->get ("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 ("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 ("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 ("type"); + throw operation_failed (); + } - if (type.empty () && wt != 0 && wt->count ("type")) - type = wt->get ("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 (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 (&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 (&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 ("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 ("get") : 0); + member_access* opt_ma_set (opt ? &opt->get ("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 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 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 ("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 ());" << 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 (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 ("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 ("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 ("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 ("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 (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 (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 + #include #include +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 (&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 (&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_; }; -- cgit v1.1