From e78db08d98d5adb4dee3006eea8c3569e383c562 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 15 Jun 2015 12:36:53 +0200 Subject: Add points_to pragma Currently does not support containers. --- odb/common.cxx | 11 ++++- odb/common.hxx | 3 ++ odb/context.hxx | 6 +++ odb/pragma.cxx | 68 +++++++++++++++++++++++++++ odb/processor.cxx | 109 ++++++++++++++----------------------------- odb/relational/model.hxx | 96 +++++++++++++++++++++++++++---------- odb/relational/validator.cxx | 4 +- odb/validator.cxx | 82 +++++++++++++++++++++++++++++++- 8 files changed, 275 insertions(+), 104 deletions(-) diff --git a/odb/common.cxx b/odb/common.cxx index 172e008..8dc995d 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -250,6 +250,12 @@ traverse_pointer (semantics::data_member& m, semantics::class_& c) } void object_columns_base:: +traverse_points_to (semantics::data_member& m, semantics::class_&) +{ + traverse_member (m, utype (m)); +} + +void object_columns_base:: traverse_composite (semantics::data_member*, semantics::class_& c) { inherits (c); @@ -453,10 +459,13 @@ traverse (semantics::data_member& m) if (oc_.section_test (oc_.member_path_)) { + using semantics::class_; semantics::type& t (utype (m)); - if (semantics::class_* c = object_pointer (t)) + if (class_* c = object_pointer (t)) oc_.traverse_pointer (m, *c); + else if (class_* c = points_to (m)) + oc_.traverse_points_to (m, *c); else oc_.traverse_member (m, t); } diff --git a/odb/common.hxx b/odb/common.hxx index b3a71ce..277c0e3 100644 --- a/odb/common.hxx +++ b/odb/common.hxx @@ -189,6 +189,9 @@ struct object_columns_base: traversal::class_, virtual context virtual void traverse_pointer (semantics::data_member&, semantics::class_&); + virtual void + traverse_points_to (semantics::data_member&, semantics::class_&); + // If you override this function, you can call the base to traverse // bases and members. The first argument is the data member and can // be NULL if we are traversing the root type or a base. The second diff --git a/odb/context.hxx b/odb/context.hxx index 351bc61..b32f392 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -769,6 +769,12 @@ public: static semantics::data_member* object_pointer (data_member_path const&); + static semantics::class_* + points_to (semantics::data_member& m) + { + return m.get ("points-to", 0); + } + static bool abstract (semantics::class_& c) { diff --git a/odb/pragma.cxx b/odb/pragma.cxx index c8a3254..ac220b3 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -380,6 +380,7 @@ check_spec_decl_type (declaration const& d, p == "column" || p == "inverse" || p == "on_delete" || + p == "points_to" || p == "section" || p == "load" || p == "update" || @@ -2340,6 +2341,65 @@ handle_pragma (cxx_lexer& l, tt = l.next (tl, &tn); } + else if (p == "points_to") + { + // points_to() + // + + tree type; + string type_name; + + string p (tl); + location_t loc (l.location ()); + + if (l.next (tl, &tn) != CPP_OPEN_PAREN) + { + error (l) << "'(' expected after db pragma " << p << endl; + return; + } + + tt = l.next (tl, &tn); + + if (tt == CPP_NAME || tt == CPP_SCOPE) + { + type = resolve_scoped_name ( + l, tt, tl, tn, current_scope (), type_name, true, p); + + if (type == 0) + return; // Diagnostics has already been issued. + + // Get the actual type if this is a TYPE_DECL. Also get the main + // variant. + // + if (TREE_CODE (type) == TYPE_DECL) + type = TREE_TYPE (type); + + if (TYPE_P (type)) // Can be a template. + type = TYPE_MAIN_VARIANT (type); + + if (TREE_CODE (type) != RECORD_TYPE) + { + error (loc) << "name '" << type_name << "' in db pragma " << p + << " does not refer to a class" << endl; + return; + } + + val = type; + } + else + { + error (l) << "class name expected in db pragma " << p << endl; + return; + } + + if (tt != CPP_CLOSE_PAREN) + { + error (l) << "')' expected at the end of db pragma " << p << endl; + return; + } + + tt = l.next (tl, &tn); + } else if (p == "section") { // section (name) @@ -3445,6 +3505,7 @@ handle_pragma_qualifier (cxx_lexer& l, string p) p == "update" || p == "inverse" || p == "on_delete" || + p == "points_to" || p == "unordered" || p == "readonly" || p == "transient" || @@ -3821,6 +3882,12 @@ handle_pragma_db_on_delete (cpp_reader* r) } extern "C" void +handle_pragma_db_points_to (cpp_reader* r) +{ + handle_pragma_qualifier (r, "points_to"); +} + +extern "C" void handle_pragma_db_unordered (cpp_reader* r) { handle_pragma_qualifier (r, "unordered"); @@ -3935,6 +4002,7 @@ register_odb_pragmas (void*, void*) c_register_pragma_with_expansion ("db", "update", handle_pragma_db_update); c_register_pragma_with_expansion ("db", "inverse", handle_pragma_db_inverse); c_register_pragma_with_expansion ("db", "on_delete", handle_pragma_db_on_delete); + c_register_pragma_with_expansion ("db", "points_to", handle_pragma_db_points_to); c_register_pragma_with_expansion ("db", "unordered", handle_pragma_db_unordered); c_register_pragma_with_expansion ("db", "readonly", handle_pragma_db_readonly); c_register_pragma_with_expansion ("db", "transient", handle_pragma_db_transient); diff --git a/odb/processor.cxx b/odb/processor.cxx index 3a2cb1d..0bb8bc7 100644 --- a/odb/processor.cxx +++ b/odb/processor.cxx @@ -130,6 +130,8 @@ namespace m.set ("readonly", true); } + process_points_to (m); + if (composite_wrapper (t)) return; @@ -1118,15 +1120,15 @@ namespace semantics::type& t, string const& kp = string ()) { - // The overall idea is as follows: try to instantiate the pointer - // traits class template. If we are successeful, then get the - // element type and see if it is an object. - // using semantics::class_; using semantics::data_member; class_* c (0); + // The overall idea is as follows: try to instantiate the pointer + // traits class template. If we are successeful, then get the + // element type and see if it is an object. + // if (t.count ("element-type")) c = t.get ("element-type"); else @@ -1269,76 +1271,6 @@ namespace } } - bool poly (polymorphic (*c)); - bool abst (abstract (*c)); - - // Make sure the pointed-to class is complete. - // - if (!c->complete ()) - { - os << m.file () << ":" << m.line () << ":" << m.column () << ": " - << "error: pointed-to class '" << class_fq_name (*c) << "' " - << "is incomplete" << endl; - - os << c->file () << ":" << c->line () << ":" << c->column () << ": " - << "info: class '" << class_name (*c) << "' is declared here" - << endl; - - os << c->file () << ":" << c->line () << ":" << c->column () << ": " - << "info: consider including its definition with the " - << "--odb-epilogue option" << endl; - - throw operation_failed (); - } - - // Make sure the pointed-to class is not reuse-abstract. - // - if (abst && !poly) - { - os << m.file () << ":" << m.line () << ":" << m.column () << ": " - << "error: pointed-to class '" << class_fq_name (*c) << "' " - << "is abstract" << endl; - - os << c->file () << ":" << c->line () << ":" << c->column () << ": " - << "info: class '" << class_name (*c) << "' is defined here" - << endl; - - throw operation_failed (); - } - - // Make sure the pointed-to class has object id unless it is in a - // view where we can load no-id objects. - // - if (id_member (*c) == 0 && !view_member (m)) - { - os << m.file () << ":" << m.line () << ":" << m.column () << ": " - << "error: pointed-to class '" << class_fq_name (*c) << "' " - << "has no object id" << endl; - - os << c->file () << ":" << c->line () << ":" << c->column () << ": " - << "info: class '" << class_name (*c) << "' is defined here" - << endl; - - throw operation_failed (); - } - - // Make sure the pointed-to class has a default ctor. Since we will - // use database::load() in the generated code, lack of a default ctor - // will lead to uncompilable generated code. Poly-abstract is Ok. - // - if (!c->default_ctor () && !(abst && poly)) - { - os << m.file () << ":" << m.line () << ":" << m.column () << ": " - << "error: pointed-to class '" << class_fq_name (*c) << "' " - << "has no default constructor" << endl; - - os << c->file () << ":" << c->line () << ":" << c->column () << ": " - << "info: class '" << class_name (*c) << "' is defined here" - << endl; - - throw operation_failed (); - } - // See if this is the inverse side of a bidirectional relationship. // If so, then resolve the member and cache it in the context. // @@ -1408,6 +1340,35 @@ namespace } // + // Process points-to pragma. + // + + void + process_points_to (semantics::data_member& m, + string const& /*kp*/ = string ()) + { + if (!m.count ("points-to")) + return; + + using semantics::class_; + + tree t (m.get ("points-to")); + location_t l (m.get ("points-to-location")); + + class_* c (dynamic_cast (unit.find (t))); + + if (c == 0 || !object (*c)) + { + error (l) << "name specified with '#pragma db points_to' does " + << "not refer to an object" << endl; + throw operation_failed (); + } + + m.remove ("points-to"); + m.set (/*kp + (kp.empty () ? "": "-") + */"points-to", c); + } + + // // Process container. // diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx index 94f093a..77631e2 100644 --- a/odb/relational/model.hxx +++ b/odb/relational/model.hxx @@ -237,9 +237,6 @@ namespace relational virtual void traverse_pointer (semantics::data_member& m, semantics::class_& c) { - using sema_rel::column; - using sema_rel::foreign_key; - // Ignore inverse object pointers. // if (inverse (m, key_prefix_)) @@ -254,6 +251,76 @@ namespace relational return; } + // Get the position of the last column. + // + sema_rel::table::names_iterator i (table_.names_end ()); + + while (i != table_.names_begin ()) + { + --i; + if (i->nameable ().is_a ()) + break; + } + + // Traverse the object pointer as columns. + // + object_columns_base::traverse_pointer (m, c); + + // Get to the first column that we have added. + // + if (i != table_.names_end ()) + ++i; // Next column. + else + i = table_.names_begin (); + + foreign_key (m, c, i); + } + + virtual void + traverse_points_to (semantics::data_member& m, semantics::class_& c) + { + if (deleted (member_path_)) + { + // Still traverse it as columns so that we can populate the + // deleted map. + // + object_columns_base::traverse_points_to (m, c); + return; + } + + // Get the position of the last column. + // + sema_rel::table::names_iterator i (table_.names_end ()); + + while (i != table_.names_begin ()) + { + --i; + if (i->nameable ().is_a ()) + break; + } + + // Traverse the data member as columns. + // + object_columns_base::traverse_points_to (m, c); + + // Get to the first column that we have added. + // + if (i != table_.names_end ()) + ++i; // Next column. + else + i = table_.names_begin (); + + foreign_key (m, c, i); + } + + virtual void + foreign_key (semantics::data_member& m, + semantics::class_& c, + sema_rel::table::names_iterator i) + { + using sema_rel::column; + using sema_rel::foreign_key; + string id (id_prefix_ + (key_prefix_.empty () ? m.name () : key_prefix_)); @@ -287,29 +354,8 @@ namespace relational simple = (fk.referenced_columns ().size () == 1); } - // Get the position of the last column. + // Get referencing columns. // - sema_rel::table::names_iterator i (table_.names_end ()); - - while (i != table_.names_begin ()) - { - --i; - - if (i->nameable ().is_a ()) - break; - } - - // Traverse the object pointer as columns. - // - object_columns_base::traverse_pointer (m, c); - - // Add the newly added columns to the foreign key. - // - if (i != table_.names_end ()) - ++i; - else - i = table_.names_begin (); - for (; i != table_.names_end (); ++i) { if (column* c = dynamic_cast (&i->nameable ())) diff --git a/odb/relational/validator.cxx b/odb/relational/validator.cxx index 47f089c..3b867f8 100644 --- a/odb/relational/validator.cxx +++ b/odb/relational/validator.cxx @@ -59,9 +59,9 @@ namespace relational const char* kp (container (m) ? "value" : ""); location l (m.location ()); - // Make sure it is a pointer. + // Make sure it is a pointer or a member with points_to pragma. // - if (!object_pointer (member_utype (m, kp))) + if (!object_pointer (member_utype (m, kp)) && !points_to (m)) { error (l) << "on_delete specified for non-object pointer" << endl; valid_ = false; diff --git a/odb/validator.cxx b/odb/validator.cxx index 938ce7d..071da16 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -1032,8 +1032,86 @@ namespace data_member2 (bool& valid): valid_ (valid) {} virtual void - traverse (type&) + traverse (type& m) { + // Validate pointed-to objects. + // + semantics::class_* c; + if ((c = object_pointer (utype (m))) || (c = points_to (m))) + { + bool poly (polymorphic (*c)); + bool abst (abstract (*c)); + + // Make sure the pointed-to class is complete. + // + if (!c->complete ()) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ": " + << "error: pointed-to class '" << class_fq_name (*c) << "' " + << "is incomplete" << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () << ": " + << "info: class '" << class_name (*c) << "' is declared here" + << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () << ": " + << "info: consider including its definition with the " + << "--odb-epilogue option" << endl; + + valid_ = false; + return; + } + + // Make sure the pointed-to class is not reuse-abstract. + // + if (abst && !poly) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ": " + << "error: pointed-to class '" << class_fq_name (*c) << "' " + << "is abstract" << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () << ": " + << "info: class '" << class_name (*c) << "' is defined here" + << endl; + + throw operation_failed (); + } + + // Make sure the pointed-to class has object id unless it is in a + // view where we can load no-id objects. + // + if (id_member (*c) == 0 && !view_member (m)) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ": " + << "error: pointed-to class '" << class_fq_name (*c) << "' " + << "has no object id" << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () << ": " + << "info: class '" << class_name (*c) << "' is defined here" + << endl; + + valid_ = false; + return; + } + + // Make sure the pointed-to class has a default ctor. Since we will + // use database::load() in the generated code, lack of a default ctor + // will lead to uncompilable generated code. Poly-abstract is Ok. + // + if (!c->default_ctor () && !(abst && poly)) + { + os << m.file () << ":" << m.line () << ":" << m.column () << ": " + << "error: pointed-to class '" << class_fq_name (*c) << "' " + << "has no default constructor" << endl; + + os << c->file () << ":" << c->line () << ":" << c->column () << ": " + << "info: class '" << class_name (*c) << "' is defined here" + << endl; + + valid_ = false; + return; + } + } } bool& valid_; @@ -1466,7 +1544,7 @@ namespace traversal::defines defines_; typedefs typedefs_; - data_member1 member_; + data_member2 member_; traversal::names names_member_; }; } -- cgit v1.1