From 3417fc7c0df3b1b01750874587c4f3bb2ef02f45 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 4 Dec 2013 11:30:33 +0200 Subject: Implement on_delete pragma for object pointers Translates to the ON DELETE SQL clause. --- NEWS | 6 + doc/manual.xhtml | 310 ++++++++++++++++++++++--------- odb/pragma.cxx | 42 +++++ odb/relational/mssql/schema.cxx | 19 ++ odb/relational/mysql/schema.cxx | 18 ++ odb/relational/schema.hxx | 10 +- odb/relational/validator.cxx | 38 ++++ odb/semantics/relational/foreign-key.cxx | 4 +- odb/semantics/relational/foreign-key.hxx | 3 +- odb/validator.cxx | 25 ++- 10 files changed, 382 insertions(+), 93 deletions(-) diff --git a/NEWS b/NEWS index b38239e..5396abb 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,9 @@ +Version 2.4.0 + + * New pragma, on_delete, allows the specification of an on-delete semantics + (translated to the ON DELETE SQL clause) for an object pointer. For more + information, refer to Section 14.4.15, "on_delete" in the ODB manual. + Version 2.3.0 * Support for database schema evolution, including schema migration, data diff --git a/doc/manual.xhtml b/doc/manual.xhtml index a87fa6a..06d7c82 100644 --- a/doc/manual.xhtml +++ b/doc/manual.xhtml @@ -596,27 +596,28 @@ for consistency. 14.4.12readonly 14.4.13virtual 14.4.14inverse - 14.4.15version - 14.4.16index - 14.4.17unique - 14.4.18unordered - 14.4.19table - 14.4.20load/update - 14.4.21section - 14.4.22added - 14.4.23deleted - 14.4.24index_type - 14.4.25key_type - 14.4.26value_type - 14.4.27value_null/value_not_null - 14.4.28id_options - 14.4.29index_options - 14.4.30key_options - 14.4.31value_options - 14.4.32id_column - 14.4.33index_column - 14.4.34key_column - 14.4.35value_column + 14.4.15on_delete + 14.4.16version + 14.4.17index + 14.4.18unique + 14.4.19unordered + 14.4.20table + 14.4.21load/update + 14.4.22section + 14.4.23added + 14.4.24deleted + 14.4.25index_type + 14.4.26key_type + 14.4.27value_type + 14.4.28value_null/value_not_null + 14.4.29id_options + 14.4.30index_options + 14.4.31key_options + 14.4.32value_options + 14.4.33id_column + 14.4.34index_column + 14.4.35key_column + 14.4.36value_column @@ -5783,7 +5784,7 @@ private: of person's nicknames is probably not important. To instruct the ODB compiler to ignore the order in ordered containers we can use the db unordered pragma (Section 14.3.9, - "unordered", Section 14.4.18, + "unordered", Section 14.4.19, "unordered"). For example:

@@ -6089,8 +6090,8 @@ for (;;)
 
   

odb::vector incurs 2-bit per element overhead in order to store the change state. It cannot - be stored unordered in the database (Section - 14.4.18 "unordered") but can be used as an inverse + be stored unordered in the database (Section + 14.4.19 "unordered") but can be used as an inverse side of a relationship (6.2 "Bidirectional Relationships"). In this case, no change tracking is performed since no state for such a container is stored in the database.

@@ -6349,8 +6350,8 @@ class employee use the not_null pragma (Section 14.4.6, "null/not_null") for single object pointers and the value_not_null pragma - (Section - 14.4.27, "value_null/value_not_null") + (Section + 14.4.28, "value_null/value_not_null") for containers of object pointers. For example:

@@ -6481,7 +6482,12 @@ result r (db.query<employee> (query::employer.is_null ()));
      or updated. Rather, only a reference to the object (in the form of the
      object id) is stored for the pointed-to object in the database.
      The pointed-to object itself is a separate entity and should
-     be made persistent or updated independently.

+ be made persistent or updated independently. By default, the + same principle also applies to erasing pointed-to objects. That + is, we have to make sure all the pointing objects are updated + accordingly. However, in the case of erase, we can specify an + alternative on-delete semantic as discussed in + Section 14.4.15, "on_delete".

When persisting or updating an object containing a pointer to another object, the pointed-to object must have a valid object id. This, @@ -6808,7 +6814,7 @@ CREATE TABLE employee ( pointer. Also note that an ordered container (Section 5.1, "Ordered Containers") of pointers that is an inverse side of a bidirectional relationship is always treated as unordered - (Section 14.4.18, "unordered") + (Section 14.4.19, "unordered") because the contents of such a container are implicitly built from the direct side of the relationship which does not contain the element order (index).

@@ -7895,9 +7901,9 @@ CREATE TABLE person (

The same principle applies when a composite value type is used as an element of a container, except that instead of db column, either the db value_column - (Section 14.4.35, "value_column") or + (Section 14.4.36, "value_column") or db key_column - (Section 14.4.34, "key_column") + (Section 14.4.35, "key_column") pragmas are used to specify the column prefix.

When a composite value type contains a container, an extra table @@ -7941,8 +7947,8 @@ CREATE TABLE person (

To customize the container table name we can use the - db table pragma (Section - 14.4.19, "table"), for example:

+ db table pragma (Section + 14.4.20, "table"), for example:

 #pragma db value
@@ -11147,7 +11153,7 @@ p.age (age);
   

To declare a persistent class with the optimistic concurrency model we use the optimistic pragma (Section 14.1.5, "optimistic"). We also use the version - pragma (Section 14.4.15, "version") + pragma (Section 14.4.16, "version") to specify which data member will store the object version. For example:

@@ -13826,7 +13832,7 @@ class person has the optimistic concurrency model. A class with the optimistic concurrency model must also specify the data member that is used to store the object version using the version pragma - (Section 14.4.15, "version"). + (Section 14.4.16, "version"). For example:

@@ -15004,7 +15010,7 @@ typedef std::vector<std::string> nicknames;
 
   

The semantics of the id_options specifier for a container type are similar to those of the id_options specifier for - a container data member (Section 14.4.28, + a container data member (Section 14.4.29, "id_options").

@@ -15021,7 +15027,7 @@ typedef std::vector<std::string> nicknames;

The semantics of the index_options specifier for a container type are similar to those of the index_options specifier for - a container data member (Section 14.4.29, + a container data member (Section 14.4.30, "index_options").

@@ -15038,7 +15044,7 @@ typedef std::map<std::string, std::string> properties;

The semantics of the key_options specifier for a container type are similar to those of the key_options specifier for - a container data member (Section 14.4.30, + a container data member (Section 14.4.31, "key_options").

@@ -15055,7 +15061,7 @@ typedef std::set<std::string> nicknames;

The semantics of the value_options specifier for a container type are similar to those of the value_options specifier for - a container data member (Section 14.4.31, + a container data member (Section 14.4.32, "value_options").

@@ -15218,129 +15224,135 @@ typedef std::map<unsigned short, float> age_weight_map; + on_delete + ON DELETE clause for object pointer member + 14.4.15 + + + version member stores object version - 14.4.15 + 14.4.16 index define database index for a member - 14.4.16 + 14.4.17 unique define unique database index for a member - 14.4.17 + 14.4.18 unordered ordered container should be stored unordered - 14.4.18 + 14.4.19 table table name for a container - 14.4.19 + 14.4.20 load/update loading/updating behavior for a section - 14.4.20 + 14.4.21 section member belongs to a section - 14.4.21 + 14.4.22 added member is soft-added - 14.4.22 + 14.4.23 deleted member is soft-deleted - 14.4.23 + 14.4.24 index_type database type for a container's index type - 14.4.24 + 14.4.25 key_type database type for a container's key type - 14.4.25 + 14.4.26 value_type database type for a container's value type - 14.4.26 + 14.4.27 value_null/value_not_null container's value can/cannot be NULL - 14.4.27 + 14.4.28 id_options database options for a container's id column - 14.4.28 + 14.4.29 index_options database options for a container's index column - 14.4.29 + 14.4.30 key_options database options for a container's key column - 14.4.30 + 14.4.31 value_options database options for a container's value column - 14.4.31 + 14.4.32 id_column column name for a container's object id - 14.4.32 + 14.4.33 index_column column name for a container's index - 14.4.33 + 14.4.34 key_column column name for a container's key - 14.4.34 + 14.4.35 value_column column name for a container's value - 14.4.35 + 14.4.36 @@ -16341,12 +16353,136 @@ class person relationship information. Only ordered and set containers can be used for inverse members. If an inverse member is of an ordered container type, it is automatically marked as unordered - (Section 14.4.18, "unordered").

+ (Section 14.4.19, "unordered").

For a more detailed discussion of inverse members, refer to Section 6.2, "Bidirectional Relationships".

-

14.4.15 version

+

14.4.15 on_delete

+ +

The on_delete specifier specifies the on-delete semantics + for a data member of an object pointer or a container of object + pointers type. The single required argument to this specifier must + be either cascade or set_null.

+ +

The on_delete specifier is translated directly to the + corresponding ON DELETE SQL clause. That is, if + cascade is specified, then when a pointed-to object + is erased from the database, the database state of the pointing + object is automatically erased as well. If set_null is + specified, then when a pointed-to object is erased from the database, + the database state of the pointing object is automatically updated + to set the pointer column to NULL. For example:

+ +
+#pragma db object
+class employer
+{
+  ...
+
+  #pragma db id auto
+  unsigned long id_;
+};
+
+#pragma db object
+class person
+{
+  ...
+
+  #pragma db on_delete(cascade)
+  employer* employer_;
+};
+
+unsigned long id;
+
+{
+  employer e;
+  person p;
+  p.employer_ = &e;
+
+  transaction t (db.begin ());
+
+  id = db.persist (e);
+  db.persist (p);
+
+  t.commit ();
+}
+
+{
+  transaction t (db.begin ());
+
+  // Database state of the person object is erased as well.
+  //
+  db.erase<employer> (id);
+
+  t.commit ();
+}
+  
+ + +

Note that this is a database-level functionality and care must be + taken in order not to end up with inconsistent object states in the + application's memory and database. The following example illustrates + the kind of problems one may encounter:

+ +
+#pragma db object
+class employer
+{
+  ...
+};
+
+#pragma db object
+class person
+{
+  ...
+
+  #pragma db on_delete(set_null)
+  employer* employer_;
+};
+
+employer e;
+person p;
+p.employer_ = &e;
+
+{
+  transaction t (db.begin ());
+  db.persist (e);
+  db.persist (p);
+  t.commit ();
+}
+
+{
+  transaction t (db.begin ());
+
+  // The employer column is set to NULL in the database but
+  // not the p.employer_ data member in the application.
+  //
+  db.erase (e);
+  t.commit ();
+}
+
+{
+  transaction t (db.begin ());
+
+  // Override the employer column with an invalid pointer.
+  //
+  db.update (p);
+
+  t.commit ();
+}
+  
+ +

Note that even optimistic concurrency will not resolve such + issues unless you are using database-level support for optimistic + concurrency as well (for example, ROWVERSION in SQL + Server).

+ +

The on_delete specifier is only valid for non-inverse + object pointer data members. If the set_null semantics + is used, then the pointer must allow the NULL value.

+ +

14.4.16 version

The version specifier specifies that the data member stores the object version used to support optimistic concurrency. If a class @@ -16376,7 +16512,7 @@ class person

For a more detailed discussion of optimistic concurrency, refer to Chapter 12, "Optimistic Concurrency".

-

14.4.16 index

+

14.4.17 index

The index specifier instructs the ODB compiler to define a database index for the data member. For example:

@@ -16395,7 +16531,7 @@ class person

For more information on defining database indexes, refer to Section 14.7, "Index Definition Pragmas".

-

14.4.17 unique

+

14.4.18 unique

The index specifier instructs the ODB compiler to define a unique database index for the data member. For example:

@@ -16414,7 +16550,7 @@ class person

For more information on defining database indexes, refer to Section 14.7, "Index Definition Pragmas".

-

14.4.18 unordered

+

14.4.19 unordered

The unordered specifier specifies that the member of an ordered container type should be stored unordered in the database. @@ -16437,7 +16573,7 @@ class person storage in the database, refer to Section 5.1, "Ordered Containers".

-

14.4.19 table

+

14.4.20 table

The table specifier specifies the table name that should be used to store the contents of the container member. For example:

@@ -16485,7 +16621,7 @@ class person qualified names, refer to Section 14.1.8, "schema".

-

14.4.20 load/update

+

14.4.21 load/update

The load and update specifiers specify the loading and updating behavior for an object section, respectively. @@ -16495,7 +16631,7 @@ class person change, and manual. For more information on object sections, refer to Chapter 9, "Sections".

-

14.4.21 section

+

14.4.22 section

The section specifier indicates that a data member of a persistent class belongs to an object section. The single @@ -16504,7 +16640,7 @@ class person members of a persistent class. For more information on object sections, refer to Chapter 9, "Sections".

-

14.4.22 added

+

14.4.23 added

The added specifier marks the data member as soft-added. The single required argument to this specifier is @@ -16512,7 +16648,7 @@ class person refer to Section 13.4, "Soft Object Model Changes".

-

14.4.23 deleted

+

14.4.24 deleted

The deleted specifier marks the data member as soft-deleted. The single required argument to this specifier is @@ -16520,7 +16656,7 @@ class person refer to Section 13.4, "Soft Object Model Changes".

-

14.4.24 index_type

+

14.4.25 index_type

The index_type specifier specifies the native database type that should be used for an ordered container's @@ -16540,7 +16676,7 @@ class person };

-

14.4.25 key_type

+

14.4.26 key_type

The key_type specifier specifies the native database type that should be used for a map container's @@ -16560,7 +16696,7 @@ class person };

-

14.4.26 value_type

+

14.4.27 value_type

The value_type specifier specifies the native database type that should be used for a container's @@ -16581,12 +16717,12 @@ class person

The value_null and value_not_null - (Section 14.4.27, + (Section 14.4.28, "value_null/value_not_null") specifiers can be used to control the NULL semantics of a value column.

-

14.4.27 value_null/value_not_null

+

14.4.28 value_null/value_not_null

The value_null and value_not_null specifiers specify that a container's element value for the data member can or @@ -16619,7 +16755,7 @@ class account Multiset Containers") the element value is automatically treated as not allowing a NULL value.

-

14.4.28 id_options

+

14.4.29 id_options

The id_options specifier specifies additional column definition options that should be used for a container's @@ -16643,7 +16779,7 @@ class person of the options specifier (Section 14.4.8, "options").

-

14.4.29 index_options

+

14.4.30 index_options

The index_options specifier specifies additional column definition options that should be used for a container's @@ -16664,7 +16800,7 @@ class person of the options specifier (Section 14.4.8, "options").

-

14.4.30 key_options

+

14.4.31 key_options

The key_options specifier specifies additional column definition options that should be used for a container's @@ -16685,7 +16821,7 @@ class person of the options specifier (Section 14.4.8, "options").

-

14.4.31 value_options

+

14.4.32 value_options

The value_options specifier specifies additional column definition options that should be used for a container's @@ -16706,7 +16842,7 @@ class person of the options specifier (Section 14.4.8, "options").

-

14.4.32 id_column

+

14.4.33 id_column

The id_column specifier specifies the column name that should be used to store the object id in a @@ -16730,7 +16866,7 @@ class person

If the column name is not specified, then object_id is used by default.

-

14.4.33 index_column

+

14.4.34 index_column

The index_column specifier specifies the column name that should be used to store the element index in an @@ -16754,7 +16890,7 @@ class person

If the column name is not specified, then index is used by default.

-

14.4.34 key_column

+

14.4.35 key_column

The key_column specifier specifies the column name that should be used to store the key in a map @@ -16778,7 +16914,7 @@ class person

If the column name is not specified, then key is used by default.

-

14.4.35 value_column

+

14.4.36 value_column

The value_column specifier specifies the column name that should be used to store the element value in a @@ -17138,9 +17274,9 @@ class object

ODB also offers a shortcut for defining an index with the default method and options for a single data member. Such an index can - be defined using the index (Section - 14.4.16, "index") or unique - (Section 14.4.17, "unique") + be defined using the index (Section + 14.4.17, "index") or unique + (Section 14.4.18, "unique") member specifier. For example:

@@ -24524,8 +24660,8 @@ class Person
 
   

QOdbList incurs 2-bit per element overhead in order to store the change state. It cannot - be stored unordered in the database (Section - 14.4.18 "unordered") but can be used as an inverse + be stored unordered in the database (Section + 14.4.19 "unordered") but can be used as an inverse side of a relationship (6.2 "Bidirectional Relationships"). In this case, no change tracking is performed since no state for such a container is stored in the database.

diff --git a/odb/pragma.cxx b/odb/pragma.cxx index 0c8881f..89cfd50 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -386,6 +386,7 @@ check_spec_decl_type (declaration const& d, p == "auto" || p == "column" || p == "inverse" || + p == "on_delete" || p == "section" || p == "load" || p == "update" || @@ -2151,6 +2152,39 @@ handle_pragma (cxx_lexer& l, tt = l.next (tl, &tn); } + else if (p == "on_delete") + { + // on_delete (cascade|set_null) + // + + // Make sure we've got the correct declaration type. + // + if (decl && !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; + } + + if (l.next (tl, &tn) != CPP_NAME || (tl != "cascade" && tl != "set_null")) + { + error (l) << "cascade or set_null expected after '('" << endl; + return; + } + + using semantics::relational::foreign_key; + val = (tl == "cascade" ? foreign_key::cascade : foreign_key::set_null); + + if (l.next (tl, &tn) != 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) @@ -3255,6 +3289,7 @@ handle_pragma_qualifier (cxx_lexer& l, string p) p == "load" || p == "update" || p == "inverse" || + p == "on_delete" || p == "unordered" || p == "readonly" || p == "transient" || @@ -3625,6 +3660,12 @@ handle_pragma_db_inverse (cpp_reader* r) } extern "C" void +handle_pragma_db_on_delete (cpp_reader* r) +{ + handle_pragma_qualifier (r, "on_delete"); +} + +extern "C" void handle_pragma_db_unordered (cpp_reader* r) { handle_pragma_qualifier (r, "unordered"); @@ -3738,6 +3779,7 @@ register_odb_pragmas (void*, void*) c_register_pragma_with_expansion ("db", "load", handle_pragma_db_load); 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", "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/relational/mssql/schema.cxx b/odb/relational/mssql/schema.cxx index 2480e75..7b6c7ca 100644 --- a/odb/relational/mssql/schema.cxx +++ b/odb/relational/mssql/schema.cxx @@ -237,6 +237,20 @@ namespace relational { create_foreign_key (base const& x): base (x) {} + void + diagnose (sema_rel::foreign_key& fk) + { + if (fk.on_delete () != sema_rel::foreign_key::no_action) + { + cerr << "warning: foreign key '" << fk.name () << "' has " << + "ON DELETE clause but is disabled in SQL Server due to lack " + "of deferrable constraint support" << endl; + + cerr << "info: consider using non-deferrable foreign keys (" << + "--fkeys-deferrable-mode)" << endl; + } + } + virtual void traverse_create (sema_rel::foreign_key& fk) { @@ -248,6 +262,8 @@ namespace relational base::traverse_create (fk); else { + diagnose (fk); + // Don't bloat C++ code with comment strings if we are // generating embedded schema. // @@ -268,6 +284,9 @@ namespace relational { bool c (!fk.not_deferrable () && !in_comment); + if (c) + diagnose (fk); + if (c && format_ != schema_format::sql) return; diff --git a/odb/relational/mysql/schema.cxx b/odb/relational/mysql/schema.cxx index f06914a..473e746 100644 --- a/odb/relational/mysql/schema.cxx +++ b/odb/relational/mysql/schema.cxx @@ -145,6 +145,20 @@ namespace relational { create_foreign_key (base const& x): base (x) {} + void + diagnose (sema_rel::foreign_key& fk) + { + if (fk.on_delete () != sema_rel::foreign_key::no_action) + { + cerr << "warning: foreign key '" << fk.name () << "' has " << + "ON DELETE clause but is disabled in MySQL due to lack " + "of deferrable constraint support" << endl; + + cerr << "info: consider using non-deferrable foreign keys (" << + "--fkeys-deferrable-mode)" << endl; + } + } + virtual void traverse_create (sema_rel::foreign_key& fk) { @@ -156,6 +170,8 @@ namespace relational base::traverse_create (fk); else { + diagnose (fk); + // Don't bloat C++ code with comment strings if we are // generating embedded schema. // @@ -178,6 +194,8 @@ namespace relational base::traverse_add (fk); else { + diagnose (fk); + if (format_ != schema_format::sql) return; diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx index 0de7036..4903e3a 100644 --- a/odb/relational/schema.hxx +++ b/odb/relational/schema.hxx @@ -857,14 +857,20 @@ namespace relational switch (a) { + case foreign_key::no_action: + break; case foreign_key::cascade: { os << endl << " ON DELETE CASCADE"; break; } - case foreign_key::no_action: - break; + case foreign_key::set_null: + { + os << endl + << " ON DELETE SET NULL"; + break; + } } } diff --git a/odb/relational/validator.cxx b/odb/relational/validator.cxx index c620a58..3218b57 100644 --- a/odb/relational/validator.cxx +++ b/odb/relational/validator.cxx @@ -51,6 +51,44 @@ namespace relational } } } + + // Check on-delete. + // + if (m.count ("on-delete")) + { + const char* kp (container (m) ? "value" : ""); + location l (m.location ()); + + // Make sure it is a pointer. + // + if (!object_pointer (member_utype (m, kp))) + { + error (l) << "on_delete specified for non-object pointer" << endl; + valid_ = false; + } + + // Make sure it is not inverse. + // + if (inverse (m, kp)) + { + error (l) << "on_delete specified for inverse object " << + "pointer" << endl; + valid_ = false; + } + + // Make sure the pointer is nullable if asked to set it to NULL. + // + using sema_rel::foreign_key; + + if (m.get ("on-delete") == + foreign_key::set_null && + !null (m, kp)) + { + error (l) << "set_null specified for non-nullable object " + "pointer" << endl; + valid_ = false; + } + } } bool& valid_; diff --git a/odb/semantics/relational/foreign-key.cxx b/odb/semantics/relational/foreign-key.cxx index 8188e1f..59b401b 100644 --- a/odb/semantics/relational/foreign-key.cxx +++ b/odb/semantics/relational/foreign-key.cxx @@ -15,7 +15,7 @@ namespace semantics { namespace relational { - static const char* action_str[] = {"NO ACTION", "CASCADE"}; + static const char* action_str[] = {"NO ACTION", "CASCADE", "SET NULL"}; ostream& operator<< (ostream& os, foreign_key::action_type v) @@ -38,6 +38,8 @@ namespace semantics v = foreign_key::no_action; else if (s == "CASCADE") v = foreign_key::cascade; + else if (s == "SET NULL") + v = foreign_key::set_null; else is.setstate (istream::failbit); } diff --git a/odb/semantics/relational/foreign-key.hxx b/odb/semantics/relational/foreign-key.hxx index 458683f..859b940 100644 --- a/odb/semantics/relational/foreign-key.hxx +++ b/odb/semantics/relational/foreign-key.hxx @@ -53,7 +53,8 @@ namespace semantics enum action_type { no_action, - cascade + cascade, + set_null }; action_type diff --git a/odb/validator.cxx b/odb/validator.cxx index 172ca6d..f8f7408 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -1027,6 +1027,18 @@ namespace // Pass 2. // + struct data_member2: traversal::data_member, context + { + data_member2 (bool& valid): valid_ (valid) {} + + virtual void + traverse (type&) + { + } + + bool& valid_; + }; + // Make sure soft-delete versions make sense for dependent entities. // We don't seem to need anything for soft-add since if an entity is // not added (e.g., an object), then we cannot reference it in the @@ -1191,7 +1203,7 @@ namespace struct class2: traversal::class_, context { class2 (bool& valid) - : valid_ (valid), has_lt_operator_ (0), typedefs_ (true) + : valid_ (valid), has_lt_operator_ (0), typedefs_ (true), member_ (valid) { // Find the has_lt_operator function template. // @@ -1232,6 +1244,8 @@ namespace *this >> defines_ >> *this; *this >> typedefs_ >> *this; + + names_member_ >> member_; } virtual void @@ -1249,9 +1263,13 @@ namespace case class_object: traverse_object (c); break; case class_view: traverse_view (c); break; case class_composite: traverse_composite (c); break; - default: break; + default: return; } + // Check members. + // + names (c, names_member_); + // Check version dependencies. // { @@ -1440,6 +1458,9 @@ namespace traversal::defines defines_; typedefs typedefs_; + + data_member1 member_; + traversal::names names_member_; }; } -- cgit v1.1