From 67d06f9697793f987afa08bc1c82a9d2670c9917 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 17 Jan 2014 12:49:09 +0200 Subject: Handle query column exporting for composite value types A composite value is represented in query_columns as a nested struct. Even though the query_columns template instantiation is exported, VC++ for some reason doesn't appear to also export the nested structs. To work around this, nested structs have to have the export macro in the declaration. But that's not it: we also have to declare the nested structs extern, just like the outer template instantiation itself. --- odb/common-query.cxx | 166 +++++++++++++++++++++++++++++++++++++-------------- odb/common-query.hxx | 18 ++++-- odb/context.cxx | 4 +- odb/context.hxx | 2 +- 4 files changed, 139 insertions(+), 51 deletions(-) diff --git a/odb/common-query.cxx b/odb/common-query.cxx index 4f5a43f..2d8ad98 100644 --- a/odb/common-query.cxx +++ b/odb/common-query.cxx @@ -6,6 +6,105 @@ using namespace std; +// query_utils +// + +// Collect nested (composite) types as generated by query_columns. +// +struct query_nested_types: object_columns_base, virtual context +{ + query_nested_types (bool ptr): ptr_ (ptr), in_ptr_ (false) {} + + virtual void + traverse_object (semantics::class_& c) + { + // We don't want to traverse bases. + // + names (c); + } + + virtual void + traverse_composite (semantics::data_member* m, semantics::class_& c) + { + if (m != 0) + { + string name (prefix_ + public_name (*m)); + name += in_ptr_ ? "_column_type_" : "_type_"; + types.push_back (name); + + string p (prefix_); + prefix_ += name + "::"; + object_columns_base::traverse_composite (m, c); + prefix_ = p; + } + else + object_columns_base::traverse_composite (m, c); // Base + } + + virtual void + traverse_pointer (semantics::data_member& m, semantics::class_& c) + { + // The same logic as in query_columns. + // + if (m.count ("polymorphic-ref") || inverse (m, key_prefix_)) + return; + + if (composite_wrapper (utype (*id_member (c)))) + { + if (ptr_) + object_columns_base::traverse_pointer (m, c); + else + { + in_ptr_ = true; + object_columns_base::traverse_pointer (m, c); + in_ptr_ = false; + } + } + } + +public: + strings types; + +protected: + bool ptr_; + bool in_ptr_; // True while we are "inside" an object pointer. + string prefix_; +}; + +void query_utils:: +inst_query_columns (bool decl, + bool ptr, + string const& type, + string const& alias, + semantics::class_& c) +{ + inst_header (decl); + os << (ptr ? "pointer_" : "") << "query_columns<" << endl + << " " << type << "," << endl + << " id_" << db << "," << endl + << " " << alias << " >;" + << endl; + + // If we are generating extern declarations, we also have to generate + // them for all the nested (composite) structs. That's what VC++ needs. + // + if (decl) + { + query_nested_types t (ptr); + t.traverse (c); + + for (strings::iterator i (t.types.begin ()); i != t.types.end (); ++i) + { + inst_header (decl, true); // Omit export, GCC doesn't like it. + os << (ptr ? "pointer_" : "") << "query_columns<" << endl + << " " << type << "," << endl + << " id_" << db << "," << endl + << " " << alias << " >::" << *i << ";" + << endl; + } + } +} + // query_tags // @@ -350,13 +449,11 @@ generate_inst (semantics::data_member& m, semantics::class_& c) // pointer_query_columns just derives from query_columns and // that's what we need to instantiate. // - inst_header (inst_); - os << (has_a (c, test_pointer | include_base) ? "pointer_" : "") << - "query_columns<" << endl - << " " << fq_name << "," << endl - << " id_" << db << "," << endl - << " " << alias << " >;" - << endl; + inst_query_columns (inst_, + has_a (c, test_pointer | include_base), + fq_name, + alias, + c); } // query_columns @@ -398,9 +495,13 @@ traverse_composite (semantics::data_member* m, semantics::class_& c) if (decl_) { + // For some bizarre reason VC++ needs the export directive for + // a type nested in an (exported) template. This appears not + // to cause any problems for GCC. + // os << "// " << name << endl << "//" << endl - << "struct " << name << suffix; + << "struct " << exp << name << suffix; // Derive from the base in query_columns_base. It contains columns // data for the pointer members. @@ -733,23 +834,14 @@ traverse (type& c) // inherits (c, inherits_); - inst_header (decl_); - os << (test_ptr_ && ptr ? "pointer_query_columns" : "query_columns") << - "<" << endl - << " " << class_fq_name (c) << "," << endl - << " id_" << db << "," << endl - << " " << alias_ << " >;" - << endl; + inst_query_columns (decl_, + test_ptr_ && ptr, + class_fq_name (c), + alias_, + c); if (!test_ptr_ && ptr) - { - inst_header (decl_); - os << "pointer_query_columns<" << endl - << " " << class_fq_name (c) << "," << endl - << " id_" << db << "," << endl - << " " << alias_ << " >;" - << endl; - } + inst_query_columns (decl_, true, class_fq_name (c), alias_, c); if (poly) alias_ = old_alias; @@ -996,22 +1088,10 @@ generate_inst (type& c) // 3 // - inst_header (inst_); - os << "query_columns<" << endl - << " " << type << "," << endl - << " id_" << db << "," << endl - << " " << alias << " >;" - << endl; + inst_query_columns (inst_, false, type, alias, c); if (has_a (c, test_pointer | exclude_base)) - { - inst_header (inst_); - os << "pointer_query_columns<" << endl - << " " << type << "," << endl - << " id_" << db << "," << endl - << " " << alias << " >;" - << endl; - } + inst_query_columns (inst_, true, type, alias, c); } // view_query_columns_type @@ -1250,13 +1330,11 @@ generate_inst (type& c) // pointer_query_columns just derives from query_columns and // that's what we need to instantiate. // - inst_header (decl_); - os << (has_a (o, test_pointer | include_base) ? "pointer_" : "") << - "query_columns<" << endl - << " " << otype << "," << endl - << " id_" << db << "," << endl - << " " << alias << " >;" - << endl; + inst_query_columns (decl_, + has_a (o, test_pointer | include_base), + otype, + alias, + o); } } } diff --git a/odb/common-query.hxx b/odb/common-query.hxx index b340b9f..5b0f71b 100644 --- a/odb/common-query.hxx +++ b/odb/common-query.hxx @@ -12,6 +12,16 @@ // Query-related generators. // +struct query_utils: virtual context +{ + void + inst_query_columns (bool decl, // Extern declaration or instanatiation. + bool ptr, // pointer_query_columns or query_columns + string const& type, // Object fq-type. + string const& alias, // Table alias. + semantics::class_&); // Traverse for nested structs. +}; + // Generate query tags for pointers in this objects. // struct query_tags: object_columns_base, virtual context @@ -75,7 +85,7 @@ protected: // Generate query columns in the query_columns_base class. // -struct query_columns_base: object_columns_base, virtual context +struct query_columns_base: object_columns_base, query_utils { typedef query_columns_base base; @@ -179,7 +189,7 @@ private: // Generate explicit instantiations of base classes. // -struct query_columns_base_insts: traversal::class_, virtual context +struct query_columns_base_insts: traversal::class_, query_utils { typedef query_columns_base_insts base; @@ -203,7 +213,7 @@ private: // Generate the query_columns_base/query_columns or pointer_query_columns // classes for objects. // -struct query_columns_type: traversal::class_, virtual context +struct query_columns_type: traversal::class_, query_utils { typedef query_columns_type base; @@ -237,7 +247,7 @@ public: // Generate the query_columns class for views. // -struct view_query_columns_type: traversal::class_, virtual context +struct view_query_columns_type: traversal::class_, query_utils { typedef view_query_columns_type base; diff --git a/odb/context.cxx b/odb/context.cxx index 352cb51..ef6ef9c 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -2345,14 +2345,14 @@ strlit (string const& str) } void context:: -inst_header (bool decl) +inst_header (bool decl, bool omit_exp) { if (decl && !ext.empty ()) os << ext << " "; os << "template struct"; - if (!exp.empty ()) + if (!omit_exp && !exp.empty ()) { // If we are generating an explicit instantiation directive rather // than the extern template declaration, then omit the export symbol diff --git a/odb/context.hxx b/odb/context.hxx index e0238dd..41428d0 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -1227,7 +1227,7 @@ public: // extern and export symbols. // void - inst_header (bool decl); + inst_header (bool decl, bool omit_exp = false); // Counts and other information. // -- cgit v1.1