2

I'm loading various data from database, and have already implemented a template-method:

template <typename R>
bool
loadMap(map<const string, R> &store, const char *query) {
{
...
      for (auto row : rows(query)) {
          store.insert(make_pair(row[0], R(row[1], row[2], ....));
      }
}

The method sends the query to the DB-server and populates the store with the results. The first column becomes the key of the map, and the rest of the fields form the value structure. That works.

However, in a couple of cases, the queries returns just one column -- which is stored in a set (or unordered_set) -- without any values associated with it.

Currently those cases are handled by methods of their own, and I'd like to extend my loadMap-function to these other types of containers -- perhaps, renaming it to loadContainer.

How would this new loadContainer's declaration look, and how would it invoke the store.insert() with just the key, no values?

(I'm aware of the dodge of turning the set into a map with all values being empty, but I'd like to avoid that.)

Actually, I cannot even think of a way to handle map and unordered_map -- though the insert would be exactly the same, how would I declare such a function? The two map-types don't seem to have a common ancestor (nor "interface", to use a Java-term)...

Mikhail T.
  • 3,043
  • 3
  • 29
  • 46
  • You could use `map>` – bloody Dec 22 '20 at 19:42
  • I was just about to put that comment in, but was beaten to it. Use `std::optional`. – PaulMcKenzie Dec 22 '20 at 19:44
  • Thank you both, but it'd still be a `map`, would not it? Not a `set` -- nor even an `unordered_map`... – Mikhail T. Dec 22 '20 at 19:46
  • At my work (with Java), we faced this problem and ended up with a core `template void queryAll(Container&)` that did the real work, and then three helper wrappers: `List queryAll()`, `T queryOneOrThrow()`, and `Optional queryOneOrNone()` overloads. – Mooing Duck Dec 22 '20 at 20:22
  • @MooingDuck, Java has run-time type information (RTTI)! You can create new _classes_ on the fly, at run-time, and then create objects of these newly-created types for each SQL-query -- based on the names/types of the columns returned. In C++ there is no such (expensive) luxury, though... – Mikhail T. Dec 22 '20 at 20:30
  • @MikhailT.: Right, but we don't like RTTI, which is why we have the caller pass in the container in the core method I showed. What we did is 100% doable in C++. Then again, this is C++, so maybe the entire concept would be better as an iterator? – Mooing Duck Dec 22 '20 at 20:36

2 Answers2

2

You might have overloads

template <typename R>
bool loadContainer(std::map<string, R> &store, const char *query) {
{
    // ...
    for (auto row : rows(query)) {
        store.insert(std::make_pair(row[0], R(row[1], row[2] /*, ...*/));
    }
    // ...
}

bool loadContainer(std::set<string> &store, const char *query) {
{
    // ...
    for (auto row : rows(query)) {
        store.insert(row[0]);
    }
    // ...
}

Alternatively, to avoid overload, you might do (C++17)

template <typename Container>
bool loadContainer(Container& store, const char *query) {
{
    // ...
    for (auto row : rows(query)) {
        if constexpr (is_map<Container>::value) {
            using R = typename Container::mapped_type;
            store.insert(std::make_pair(row[0], R(row[1], row[2] /*, ...*/));
        } else {
            store.insert(row[0]);
        }
    }
    // ...
}

with appropriate traits as for example

template <typename T, typename Enabler = void>
struct is_map : std::false_type {};

template <typename T>
struct is_map<T, std::void_t<typename T::mapped_type>> : std::true_type {};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

Having solved the problem of distinguishing between maps and sets, I was able to implement different insert-functions for different container-types.

My loadMap templatized function is simply calling the insert(store, key, value) -- and the correct insert implementation is picked at compile time depending on the type of the store. (For sets, the value is ignored.)

Mikhail T.
  • 3,043
  • 3
  • 29
  • 46