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/relational/processor.cxx | 432 +++++++++++++++++++++++++++++++++---------- 1 file changed, 330 insertions(+), 102 deletions(-) (limited to 'odb/relational/processor.cxx') 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)); } } } -- cgit v1.1