From c9531e4140c08dcb45f05698c5ff6d201f319e8f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 1 Jul 2015 19:20:25 +0200 Subject: C++ type mapping support for data members --- odb/context.cxx | 254 +++++++++++++++++++++++++++++++++++++++++++--- odb/context.hxx | 29 ++++-- odb/pragma.cxx | 1 + odb/processor.cxx | 42 +++++--- odb/relational/common.hxx | 3 + odb/relational/common.txx | 12 ++- odb/relational/source.hxx | 117 ++++++++++++--------- 7 files changed, 376 insertions(+), 82 deletions(-) (limited to 'odb') diff --git a/odb/context.cxx b/odb/context.cxx index 1ff906f..5ce084a 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -22,6 +22,239 @@ using namespace std; +static inline void +add_space (string& s) +{ + string::size_type n (s.size ()); + if (n != 0 && s[n - 1] != ' ') + s += ' '; +} + +// +// custom_cxx_type +// +string custom_cxx_type:: +translate (string const& val, const cxx_tokens& expr) +{ + // Similar to member_access::translate() and a few other places. + // + 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 + { + 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); + + r += 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; +} + + // // view_object // @@ -59,14 +292,6 @@ placeholder () const 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, string const& db) const { @@ -1163,7 +1388,10 @@ utype (semantics::type& t, semantics::names*& hint) } semantics::type& context:: -utype (semantics::data_member& m, semantics::names*& hint, string const& kp) +utype (semantics::data_member& m, + semantics::names*& hint, + string const& kp, + const custom_cxx_type** translation) { semantics::type* t (0); @@ -1200,6 +1428,9 @@ utype (semantics::data_member& m, semantics::names*& hint, string const& kp) // // @@ Need to cache the result on the member. // + if (translation != 0) + *translation = 0; + for (semantics::scope* s (&m.scope ());; s = &s->scope_ ()) { using semantics::namespace_; @@ -1219,12 +1450,11 @@ utype (semantics::data_member& m, semantics::names*& hint, string const& kp) if (i != m.end ()) { - cerr << "mapping " << t->fq_name (hint) << " to "; - hint = i->second->as_hint; t = i->second->as; - cerr << t->fq_name (hint) << endl; + if (translation != 0) + *translation = i->second; // Currently we only support one level of mapping, but I am // sure someone will want multiple levels. diff --git a/odb/context.hxx b/odb/context.hxx index 6646e93..6e1d733 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -118,6 +118,12 @@ struct custom_cxx_type { custom_cxx_type (): type_node (0), as_node (0) {} + std::string + translate_to (std::string const& v) const {return translate (v, to);} + + std::string + translate_from (std::string const& v) const {return translate (v, from);} + tree type_node; std::string type_name; semantics::type* type; @@ -128,12 +134,18 @@ struct custom_cxx_type semantics::type* as; semantics::names* as_hint; - // Empty expression means the values are implicitly convertible. - // cxx_tokens to; + bool to_move; // Single (?), so can move. + cxx_tokens from; + bool from_move; // Single (?), so can move. location_t loc; + tree scope; // Scope for which this mapping is defined. + +private: + static std::string + translate (std::string const&, const cxx_tokens&); }; typedef std::vector custom_cxx_types; @@ -617,17 +629,19 @@ public: // The same for a member's type but also do custom C++ type translation. // static semantics::type& - utype (semantics::data_member& m) + utype (semantics::data_member& m, const custom_cxx_type** translation = 0) { semantics::names* hint (0); - return utype (m, hint); + return utype (m, hint, string (), translation); } static semantics::type& - utype (semantics::data_member& m, string const& key_prefix) + utype (semantics::data_member& m, + string const& key_prefix, + const custom_cxx_type** translation = 0) { semantics::names* hint (0); - return utype (m, hint, key_prefix); + return utype (m, hint, key_prefix, translation); } // In addition to the unqualified type, this version also returns the @@ -638,7 +652,8 @@ public: static semantics::type& utype (semantics::data_member&, semantics::names*& hint, - string const& key_prefix = string ()); + string const& key_prefix = string (), + const custom_cxx_type** translation = 0); // For arrays this function returns true if the (innermost) element // type is const. diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 733d19c..549a0f6 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -3117,6 +3117,7 @@ handle_pragma_qualifier (cxx_lexer& l, string p) { custom_cxx_type ct; ct.loc = loc; + ct.scope = current_scope (); val = ct; name = "custom-cxx-types"; adder = &accumulate; diff --git a/odb/processor.cxx b/odb/processor.cxx index c281706..facbbb4 100644 --- a/odb/processor.cxx +++ b/odb/processor.cxx @@ -3002,12 +3002,12 @@ namespace tree access_; // odb::access node. }; - static void + static bool check_to_from (const cxx_tokens& ex, const char* c, location_t l) { // Make sure we have one and only one placeholder (?). // - bool r (false); + bool r (false), m (true); for (cxx_tokens::const_iterator i (ex.begin ()), e (ex.end ()); i != e;) { @@ -3018,11 +3018,7 @@ namespace if (++i != e && i->type == CPP_CLOSE_PAREN) { if (r) - { - error (l) << "multiple '(?)' expressions in the '" << c << "' " - << "clause of db pragma map" << endl; - throw operation_failed (); - } + m = false; // Multiple (?), can't move. else r = true; } @@ -3039,6 +3035,8 @@ namespace throw operation_failed (); } + + return m; } } @@ -3055,8 +3053,6 @@ process1 (semantics::unit& u) u.set ("custom-cxx-types", custom_cxx_types ()); custom_cxx_types & cts (u.get ("custom-cxx-types")); - custom_cxx_type_map& ctm (u.set ("custom-cxx-type-map", - custom_cxx_type_map ())); for (custom_cxx_types::iterator i (cts.begin ()); i != cts.end (); ++i) { @@ -3096,12 +3092,13 @@ process1 (semantics::unit& u) e.push_back (cxx_token (0, CPP_OPEN_PAREN)); e.push_back (cxx_token (0, CPP_QUERY)); e.push_back (cxx_token (0, CPP_CLOSE_PAREN)); + ct.to_move = true; } else - check_to_from (e, "to", ct.loc); + ct.to_move = check_to_from (e, "to", ct.loc); } - // to + // from // { cxx_tokens& e (ct.from); @@ -3111,14 +3108,33 @@ process1 (semantics::unit& u) e.push_back (cxx_token (0, CPP_OPEN_PAREN)); e.push_back (cxx_token (0, CPP_QUERY)); e.push_back (cxx_token (0, CPP_CLOSE_PAREN)); + ct.from_move = true; } else - check_to_from (e, "from", ct.loc); + ct.from_move = check_to_from (e, "from", ct.loc); + } + + // Resolve mapping scope. + // + semantics::scope* s (dynamic_cast (u.find (ct.scope))); + if (s == 0) + { + error (ct.loc) << "unable to resolve db pragma map scope" << endl; + throw operation_failed (); + } + + if (semantics::namespace_* ns = dynamic_cast (s)) + { + if (ns->extension ()) + s = &ns->original (); } // Enter into the map. // - ctm[ct.type] = &ct; + if (!s->count ("custom-cxx-type-map")) + s->set ("custom-cxx-type-map", custom_cxx_type_map ()); + + s->get ("custom-cxx-type-map")[ct.type] = &ct; } } diff --git a/odb/relational/common.hxx b/odb/relational/common.hxx index c117be5..bc7e034 100644 --- a/odb/relational/common.hxx +++ b/odb/relational/common.hxx @@ -78,6 +78,7 @@ namespace relational semantics::data_member& m; // Member. semantics::type& t; // Cvr-unqualified member C++ type, note // that m.type () may not be the same as t. + const custom_cxx_type* ct; // Translation used for t, if any. semantics::class_* ptr; // Pointed-to object if m is an object // pointer. In this case t is the id type // while fq_type_ is the pointer fq-type. @@ -146,12 +147,14 @@ namespace relational member_info (semantics::data_member& m_, semantics::type& t_, + const custom_cxx_type* ct_, semantics::type* wrapper_, bool cq_, string& var_, string const& fq_type) : m (m_), t (t_), + ct (ct_), ptr (0), wrapper (wrapper_), cq (cq_), diff --git a/odb/relational/common.txx b/odb/relational/common.txx index 7a3adad..893e16c 100644 --- a/odb/relational/common.txx +++ b/odb/relational/common.txx @@ -26,7 +26,10 @@ namespace relational } bool cq (type_override_ != 0 ? false : const_member (m)); - semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m)); + const custom_cxx_type* ct (0); + semantics::type& t (type_override_ != 0 + ? *type_override_ + : utype (m, &ct)); semantics::type* cont; if (semantics::class_* c = object_pointer (t)) @@ -34,11 +37,12 @@ namespace relational // A pointer in view might point to an object without id. // semantics::data_member* idm (id_member (*c)); - semantics::type& t (utype (idm != 0 ? *idm : m)); + semantics::type& t (utype (idm != 0 ? *idm : m, &ct)); semantics::class_* comp (idm != 0 ? composite_wrapper (t) : 0); member_info mi (m, (comp != 0 ? *comp : t), + ct, (comp != 0 && wrapper (t) ? &t : 0), cq, var, @@ -64,6 +68,7 @@ namespace relational // member_info mi (m, *c, + ct, (wrapper (t) ? &t : 0), cq, var, @@ -82,6 +87,7 @@ namespace relational // member_info mi (m, *cont, + 0, // Cannot be mapped. (wrapper (t) ? &t : 0), cq, var, @@ -94,7 +100,7 @@ namespace relational } else { - member_info mi (m, t, 0, cq, var, fq_type_override_); + member_info mi (m, t, ct, 0, cq, var, fq_type_override_); mi.st = &member_sql_type (m); if (pre (mi)) diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index a160672..81e182b 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -2154,6 +2154,19 @@ namespace relational } } + // Translate. + // + if (mi.ct != 0) + { + os << "// From " << location_string (mi.ct->loc, true) << endl + << type_ref_type (*mi.ct->as, mi.ct->as_hint, true, "vt") << + " =" << endl + << " " << mi.ct->translate_to (member) << ";" + << endl; + + member = "vt"; + } + // 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 @@ -2181,7 +2194,11 @@ namespace relational << "{"; } - member = "wrapper_traits< " + wt + " >::get_ref (" + member + ")"; + os << "const" << mi.fq_type () << "& vw = " << endl + << " wrapper_traits< " + wt + " >::get_ref (" + member + ");" + << endl; + + member = "vw"; } if (discriminator (mi.m)) @@ -2553,7 +2570,7 @@ namespace relational if (mi.ptr != 0 && view_member (mi.m)) return true; // That's enough for the object pointer in view. - // Get the member using the accessor expression. + // Set the member using the modifier expression. // member_access& ma (mi.m.template get ("set")); @@ -2603,6 +2620,17 @@ namespace relational member = "v"; } + // Translate. + // + if (mi.ct != 0) + { + os << type_val_type (*mi.ct->as, mi.ct->as_hint, false, "vt") << ";" + << endl; + + translate_member = member; + member = "vt"; + } + // 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 @@ -2625,10 +2653,16 @@ namespace relational << "i." << mi.var << "value" << (versioned (*comp) ? ", svm" : "") << "))" << endl << "wrapper_traits< " << wt << " >::set_null (" << member + ");" - << "else" << endl; + << "else" + << "{"; } - member = "wrapper_traits< " + wt + " >::set_ref (" + member + ")"; + os << mi.fq_type () << "& vw =" << endl + << " wrapper_traits< " + wt + " >::set_ref (" + member + ");" + << endl; + + wrap_member = member; + member = "vw"; } if (mi.ptr != 0) @@ -2739,6 +2773,30 @@ namespace relational os << "}"; } + // Wrap back (so to speak). + // + if (mi.wrapper != 0 && composite (mi.t) != 0) + { + if (null (mi.m, key_prefix_) && + mi.wrapper->template get ("wrapper-null-handler")) + os << "}"; + + member = wrap_member; + } + + // Untranslate. + // + if (mi.ct != 0) + { + //@@ Use move() in C++11? Or not. + // + os << "// From " << location_string (mi.ct->loc, true) << endl + << translate_member << " = " << + mi.ct->translate_from (member) << ";"; + + member = translate_member; + } + // Call the modifier if we are using a proper one. // if (member_override_.empty ()) @@ -2893,6 +2951,8 @@ namespace relational string db_type_id; string traits; string member; + string translate_member; // Untranslated member. + string wrap_member; // Wrapped member. instance member_database_type_id_; }; @@ -6118,11 +6178,6 @@ namespace relational if (opt != 0) // Not load_opt, we do it in poly-derived as well. { - member_access& ma (opt->get ("get")); - - if (!ma.synthesized) - os << "// From " << location_string (ma.loc, true) << endl; - os << "if ("; if (poly_derived) @@ -6137,7 +6192,8 @@ namespace relational else os << "version (im)"; - os << " != " << ma.translate ("obj") << ")" << endl + os << " != " << (poly_derived ? "root_traits::" : "") << + "version (obj))" << endl << "throw object_changed ();" << endl; } @@ -6341,49 +6397,16 @@ namespace relational // if (s.optimistic ()) // Note: not update_opt. { - member_access& ma_get (opt->get ("get")); - member_access& ma_set (opt->get ("set")); - // Object is passed as const reference so we need to cast away // constness. // - string obj ("const_cast< object_type& > (obj)"); + const char* obj ("const_cast (obj)"); string inc (optimistic_version_increment (*opt)); - if (!ma_set.synthesized) - os << "// From " << location_string (ma_set.loc, true) << endl; - - if (ma_set.placeholder ()) - { - if (!ma_get.synthesized) - os << "// From " << location_string (ma_get.loc, true) << endl; - - if (inc == "1") - os << ma_set.translate ( - obj, ma_get.translate ("obj") + " + 1") << ";"; - else - os << ma_set.translate (obj, inc) << ";"; - } + if (inc == "1") + inc_member (*opt, obj, "obj", "version_type"); else - { - // If this member is const and we have a synthesized direct - // access, then cast away constness. Otherwise, we assume - // that the user-provided expression handles this. - // - bool cast (ma_set.direct () && const_member (*opt)); - if (cast) - os << "const_cast< version_type& > (" << endl; - - os << ma_set.translate (obj); - - if (cast) - os << ")"; - - if (inc == "1") - os << "++;"; - else - os << " = " << inc << ";"; - } + set_member (*opt, obj, inc, "", "version_type"); } os << "}"; -- cgit v1.1