From ce696c26d2c9dd5a5813fd865082ab19ac49bcfa Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 6 Nov 2010 18:05:19 +0200 Subject: Add support for container persistence --- odb/type-processor.cxx | 357 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 349 insertions(+), 8 deletions(-) (limited to 'odb/type-processor.cxx') diff --git a/odb/type-processor.cxx b/odb/type-processor.cxx index c8e58a1..ff799b2 100644 --- a/odb/type-processor.cxx +++ b/odb/type-processor.cxx @@ -3,6 +3,8 @@ // copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file +#include + #include namespace @@ -12,10 +14,63 @@ namespace data_member (context& c) : context (c) { + // Find the odb namespace. + // + tree odb = lookup_qualified_name ( + global_namespace, get_identifier ("odb"), false, false); + + if (odb == error_mark_node) + { + //@@ tmp + container_traits_ = 0; + return; + + os << unit.file () << ": error: unable to resolve odb namespace" + << endl; + + throw generation_failed (); + } + + // Find the access class. + // + tree access = lookup_qualified_name ( + odb, get_identifier ("access"), true, false); + + if (access == error_mark_node) + { + //@@ tmp + container_traits_ = 0; + return; + + os << unit.file () << ": error: unable to resolve access class" + << "in the odb namespace" << endl; + + throw generation_failed (); + } + + access = TREE_TYPE (access); + + // Find container_traits. + // + container_traits_ = lookup_qualified_name ( + access, get_identifier ("container_traits"), true, false); + + if (container_traits_ == error_mark_node || + !DECL_CLASS_TEMPLATE_P (container_traits_)) + { + //@@ tmp + container_traits_ = 0; + return; + + os << unit.file () << ": error: unable to resolve container_traits " + << "in the odb namespace" << endl; + + throw generation_failed (); + } } virtual void - traverse (type& m) + traverse (semantics::data_member& m) { if (m.count ("transient")) return; @@ -27,7 +82,15 @@ namespace if (comp_value (t)) return; - string const& type (column_type_impl (m)); + string type; + + if (m.count ("type")) + type = m.get ("type"); + + if (type.empty () && t.count ("type")) + type = t.get ("type"); + + type = column_type_impl (t, type, &m); if (!type.empty ()) { @@ -37,21 +100,299 @@ namespace // See if this is a container type. // + if (process_container (m)) + return; // If it is none of the above then we have an error. // string const& fq_type (t.fq_name (m.belongs ().hint ())); - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " error: unable to map C++ type '" << fq_type << "' used in " - << "data member '" << m.name () << "' to a database type" << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: unable to map C++ type '" << fq_type << "' used in " + << "data member '" << m.name () << "' to a database type" << endl; - cerr << m.file () << ":" << m.line () << ":" << m.column () << ":" - << " info: use '#pragma db type' to specify the database type" - << endl; + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " info: use '#pragma db type' to specify the database type" + << endl; + + throw generation_failed (); + } + + void + process_container_value (semantics::type& t, + string const& prefix, + semantics::data_member& m) + { + if (comp_value (t)) + return; + + string type; + + // Custom mapping can come from these places (listed in the order + // of priority): member, container type, value type. + // + if (m.count (prefix + "-type")) + type = m.get (prefix + "-type"); + + semantics::type& ct (m.type ()); + + if (type.empty () && ct.count (prefix + "-type")) + type = ct.get (prefix + "-type"); + + if (type.empty () && t.count ("type")) + type = t.get ("type"); + + type = column_type_impl (t, type, 0); + + if (!type.empty ()) + { + m.set (prefix + "-column-type", type); + return; + } + + // We do not support nested containers so skip that test. + // + + // If it is none of the above then we have an error. + // + string fq_type (t.fq_anonymous () ? "" : t.fq_name ()); + + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " error: unable to map C++ type '" << fq_type << "' used in " + << "data member '" << m.name () << "' to a database type" << endl; + + os << m.file () << ":" << m.line () << ":" << m.column () << ":" + << " info: use '#pragma db " << prefix << "_type' to specify the " + << "database type" << endl; throw generation_failed (); } + + bool + process_container (semantics::data_member& m) + { + // @@ tmp + // + if (container_traits_ == 0) + return false; + + // The overall idea is as follows: try to instantiate the container + // traits class template. If we are successeful, then this is a + // container type and we can extract the various information from + // the instantiation. Otherwise, this is not a container. + // + + semantics::type& t (m.type ()); + + container_kind_type ck; + semantics::type* vt (0); + semantics::type* it (0); + semantics::type* kt (0); + + if (t.count ("container")) + { + ck = t.get ("container-kind"); + vt = t.get ("tree-value-type"); + + if (ck == ck_ordered) + it = t.get ("tree-index-type"); + + if (ck == ck_map || ck == ck_multimap) + kt = t.get ("tree-key-type"); + } + else + { + tree args (make_tree_vec (1)); + TREE_VEC_ELT (args, 0) = t.tree_node (); + + // This step should succeed regardles of whether there is a + // container traits specialization for this type. + // + tree inst ( + lookup_template_class ( + container_traits_, args, 0, 0, 0, tf_warning_or_error)); + + if (inst == error_mark_node) + { + // Diagnostics has already been issued by lookup_template_class. + // + throw generation_failed (); + } + + inst = TYPE_MAIN_VARIANT (inst); + + // The instantiation may already be complete if it matches a + // (complete) specialization. + // + if (!COMPLETE_TYPE_P (inst)) + inst = instantiate_class_template (inst); + + // If we cannot instantiate this type, assume there is no suitable + // container traits specialization for it. + // + if (inst == error_mark_node || !COMPLETE_TYPE_P (inst)) + return false; + + // @@ This points to the primary template, not the specialization. + // + tree decl (TYPE_NAME (inst)); + + string f (DECL_SOURCE_FILE (decl)); + size_t l (DECL_SOURCE_LINE (decl)); + size_t c (DECL_SOURCE_COLUMN (decl)); + + + // Determine the container kind. + // + try + { + tree kind ( + lookup_qualified_name ( + inst, get_identifier ("kind"), false, false)); + + if (kind == error_mark_node || TREE_CODE (kind) != VAR_DECL) + throw generation_failed (); + + + // Instantiate this decalaration so that we can get its value. + // + if (DECL_TEMPLATE_INSTANTIATION (kind) && + !DECL_TEMPLATE_INSTANTIATED (kind) && + !DECL_EXPLICIT_INSTANTIATION (kind)) + instantiate_decl (kind, false, false); + + tree init (DECL_INITIAL (kind)); + + if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST) + throw generation_failed (); + + unsigned long long e; + + { + HOST_WIDE_INT hwl (TREE_INT_CST_LOW (init)); + HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (init)); + + unsigned long long l (hwl); + unsigned long long h (hwh); + unsigned short width (HOST_BITS_PER_WIDE_INT); + + e = (h << width) + l; + } + + ck = static_cast (e); + } + catch (generation_failed const&) + { + os << f << ":" << l << ":" << c << ": error: " + << "odb::container_traits specialization does not define the " + << "container kind constant" << endl; + + throw; + } + + t.set ("container-kind", ck); + + // Get the value type. + // + try + { + tree decl ( + lookup_qualified_name ( + inst, get_identifier ("value_type"), true, false)); + + if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL) + throw generation_failed (); + + tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); + + vt = &dynamic_cast (*unit.find (type)); + } + catch (generation_failed const&) + { + os << f << ":" << l << ":" << c << ": error: " + << "odb::container_traits specialization does not define the " + << "value_type type" << endl; + + throw; + } + + t.set ("tree-value-type", vt); + + + // Get the index type for ordered containers. + // + if (ck == ck_ordered) + { + try + { + tree decl ( + lookup_qualified_name ( + inst, get_identifier ("index_type"), true, false)); + + if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL) + throw generation_failed (); + + tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); + + it = &dynamic_cast (*unit.find (type)); + } + catch (generation_failed const&) + { + os << f << ":" << l << ":" << c << ": error: " + << "odb::container_traits specialization does not define the " + << "index_type type" << endl; + + throw; + } + + t.set ("tree-index-type", it); + } + + // Get the key type for maps. + // + if (ck == ck_map || ck == ck_multimap) + { + try + { + tree decl ( + lookup_qualified_name ( + inst, get_identifier ("key_type"), true, false)); + + if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL) + throw generation_failed (); + + tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); + + kt = &dynamic_cast (*unit.find (type)); + } + catch (generation_failed const&) + { + os << f << ":" << l << ":" << c << ": error: " + << "odb::container_traits specialization does not define the " + << "key_type type" << endl; + + throw; + } + + t.set ("tree-key-type", kt); + } + } + + // Process member data. + // + process_container_value (*vt, "value", m); + + if (it != 0) + process_container_value (*it, "index", m); + + if (kt != 0) + process_container_value (*kt, "key", m); + + return true; + } + + private: + tree container_traits_; }; struct class_: traversal::class_, context -- cgit v1.1