Nested initializer lists

127 Views Asked by At

I am making an attempt in creating dynamic Python-like dicts in C++. One possible approach to implementation (which has its drawbacks, for sure) is

#include <iostream>
#include <variant>
#include <map>

using namespace std;

class dict;
using Atom = variant<bool, int, double, string, dict>;
class dict {
  map<string, Atom> data_;
  
public:
  template<typename T>
  bool put(const string& k, T&& v) {
    data_[k] = std::forward<T&&>(v);
    return true;
  }
  
  template<typename T>
  T& get(const string& k) {
    return std::get<T>(data_[k]);
  }
  
  template<class T>
  bool contains(T&& k) {
    return data_.find(std::forward<T&&>(k)) != data_.end();
  }
  
  dict(initializer_list<pair<string, Atom>> kvs) {
    for (auto kv : kvs) (this->put(kv.first, kv.second));
  }
};

Now consider this last variadic initializer. The thing is, it is not exactly as flexible as I would want it to be:

int main() {
  // This works:
  auto u = dict{
    {"a", 9},
    {"b", 1.2},
    {"c", true},
    {"d", dict{
      {"e", "f"}
    }}
  };
  // This does not:
  auto v = dict{
    {"a", 9},
    {"b", 1.2},
    {"c", true},
    {"d", {
      {"e", "f"}
    }}
  };
  
  return 0;
}

The question is, how does one rewrite this so that the necessary initializer lists would be automatically cast to pair<string, Atom>?

1

There are 1 best solutions below

0
On

Thanks to @Jarod42, I have managed to come up with this:

#include <iostream>
#include <variant>
#include <map>
#include <cassert>

using namespace std;

class dict;
class Atom;

class dict {
  map<string, Atom> data_;
  
public:
  template<typename T>
  bool put(const string& k, T&& v) {
    data_[k] = std::forward<T&&>(v);
    return true;
  }
  
  template<typename T>
  T& get(const string& k) {
    return std::get<T>(data_[k]);
  }
  
  template<class T>
  bool contains(T&& k) {
    return data_.find(std::forward<T&&>(k)) != data_.end();
  }
  
  dict(std::initializer_list<pair<string, Atom>> kvs);
};

using AtomBase = variant<bool, long, double, string, dict>;

class Atom : public AtomBase {
  using AtomBase::AtomBase;
  
public:
  Atom(initializer_list<pair<string, Atom>> l) {
    *this = dict(l);
  }
};

dict::dict(std::initializer_list<pair<string, Atom>> kvs) {
  for (auto kv : kvs) {
    this->put(kv.first, kv.second);
  }
}

int main() {
  [[maybe_unused]]
  auto u = dict{
    {"a", 9},
    {"b", 1.2},
    {"c", true},
    {"d", {
      {"e", "f"}
    }}
  };
  
  return 0;
}