// file : odb/nested-container.hxx // license : GNU GPL v2; see accompanying LICENSE file #ifndef ODB_NESTED_CONTAINER_HXX #define ODB_NESTED_CONTAINER_HXX #include #include // size_t #include #include // ODB_CXX11 #ifndef ODB_CXX11 # error nested container support is only available in C++11 #endif namespace odb { // Nested container emulation support for ODB. // // In a nutshell, the idea is to represent a nested container, for example, // vector>, as map where nested_key is a composite // key consisting of the outer and inner container indexes. // // Note that the outer key in the inner container should strictly speaking // be a foreign key pointing to the key of the outer container. The only way // to achieve this currently is to manually add the constraint via ALTER // TABLE ADD CONSTRAINT. Note, however, that as long as we only modify these // tables via the ODB container interface, not having the foreign key (and // not having ON DELETE CASCADE) should be harmless (since we have a foreign // key pointing to the object id). // Map key that is used to emulate 1-level nested container mapping (for // example, vector>). Template parameter IC is a tag that allows // us to distinguish keys for unrelated containers in order to assign column // names, etc. Use the inner container type (for example, vector) for IC. // template struct nested_key { using outer_type = O; using inner_type = I; outer_type outer; inner_type inner; nested_key () = default; nested_key (outer_type o, inner_type i): outer (o), inner (i) {} bool operator< (const nested_key& v) const { return outer < v.outer || (outer == v.outer && inner < v.inner); } }; // Map key that is used to emulate 2-level nested container mapping (for // example, vector>>>). Use the middle container type for // MC (for example, vector>). // template struct nested2_key { using outer_type = O; using middle_type = M; using inner_type = I; outer_type outer; middle_type middle; inner_type inner; nested2_key () = default; nested2_key (outer_type o, middle_type m, inner_type i) : outer (o), middle (m), inner (i) {} bool operator< (const nested2_key& v) const { return outer != v.outer ? outer < v.outer : middle != v.middle ? middle < v.middle : inner < v.inner ; } }; } #include #include // size_t #include // move(), declval() #include #include // remove_reference namespace odb { template struct nested1_type: std::remove_reference ()[0])> {}; template struct nested2_type: std::remove_reference ()[0][0])> {}; template struct nested3_type: std::remove_reference ()[0][0][0])> {}; // 1-level nesting. // template // For example, OC = vector>. std::map::type>, typename nested2_type::type> nested_get (const OC& oc) { using namespace std; using IC = typename nested1_type::type; using V = typename nested2_type::type; using K = nested_key; map r; for (size_t o (0); o != oc.size (); ++o) { const IC& ic (oc[o]); for (size_t i (0); i != ic.size (); ++i) r.emplace (K (o, i), ic[i]); } return r; } template void nested_set (OC& oc, std::map&& r) { using namespace std; for (auto& p: r) { size_t o (p.first.outer); size_t i (p.first.inner); V& v (p.second); assert (o < oc.size ()); assert (i == oc[o].size ()); oc[o].push_back (move (v)); } } // 2-level nesting. // template // For example, OC = vector>>. std::map::type>, typename nested3_type::type> nested2_get (const OC& oc) { using namespace std; using MC = typename nested1_type::type; using V = typename nested3_type::type; using K = nested2_key; map r; for (size_t o (0); o != oc.size (); ++o) { const auto& mc (oc[o]); for (size_t m (0); m != mc.size (); ++m) { const auto& ic (mc[m]); for (size_t i (0); i != ic.size (); ++i) r.emplace (K (o, m, i), ic[i]); } } return r; } template void nested2_set (OC& oc, std::map&& r) { using namespace std; for (auto& p: r) { size_t o (p.first.outer); size_t m (p.first.middle); size_t i (p.first.inner); V& v (p.second); assert (o < oc.size ()); auto& mc (oc[o]); if (m >= mc.size ()) mc.resize (m + 1); assert (i == mc[m].size ()); mc[m].push_back (move (v)); } } } #include #endif // ODB_NESTED_CONTAINER_HXX