aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-06-15 12:36:53 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-06-15 12:36:53 +0200
commite78db08d98d5adb4dee3006eea8c3569e383c562 (patch)
tree4df1473c158d69ee7b53c75cc61f1a282b72da99
parentbbc39ffe31c67506b4c03fc56fa3adcb925b6325 (diff)
Add points_to pragma
Currently does not support containers.
-rw-r--r--odb/common.cxx11
-rw-r--r--odb/common.hxx3
-rw-r--r--odb/context.hxx6
-rw-r--r--odb/pragma.cxx68
-rw-r--r--odb/processor.cxx109
-rw-r--r--odb/relational/model.hxx96
-rw-r--r--odb/relational/validator.cxx4
-rw-r--r--odb/validator.cxx82
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<semantics::class_*> ("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(<fq-name>)
+ //
+
+ 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<class_*> ("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<tree> ("points-to"));
+ location_t l (m.get<location_t> ("points-to-location"));
+
+ class_* c (dynamic_cast<class_*> (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<sema_rel::column> ())
+ 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<sema_rel::column> ())
+ 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<column> ())
- 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<column*> (&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_;
};
}