user298182
user298182

Reputation: 33

c++ templated map, common interface for different type of parameters

I have parameter class, which contains different types of parameters.

  std::map<std::string, std::string >        _params_string;
  std::map<std::string, int    >             _params_int;
  std::map<std::string, double      >        _params_double;
  std::map<std::string, bool        >        _params_bool;

I would like to have one method, where user can set parameters as:

  template<class type>
  inline void set(const std::string &key, const type &value)
  {
      _params[key] = value;
  }

but, since I have different storage for different types of params, it is becoming mess.

Does anybody have any suggestion for common interface/ storage for different type of parameters?

Upvotes: 1

Views: 506

Answers (3)

Vitaliy Baldeev
Vitaliy Baldeev

Reputation: 11

You can try to use it with any type like this std::map<std::string,std::experimental::any>

Link: c++ 17 any type

Upvotes: 0

Barry
Barry

Reputation: 302757

You could use a discriminated union like Boost.Variant to reduce all your maps to just:

using Parameter = boost::variant<int, double, bool, std::string>;
std::map<std::string, Parameter> _params;

Now that you only have one map, you (obviously) only need one set():

template <class T>
void set(std::string const& key, T const& value) {
    _params[key] = value;
}

Each parameter will know what type it is holding. This additionally resolves an issue with having the same key exist in multiple maps - since that makes little sense.

Upvotes: 1

md5i
md5i

Reputation: 3083

I can see several ways to do this. But the simplest one I can think of requires C++ 14 support.

Tuples, C++ 14

template <typename T>
using stringmap = std::map<std::string, T>;

std::tuple<stringmap<std::string>, stringmap<std::int>, stringmap<std::double>, stringmap<std::bool>> _params;

template <typename T>
explicit void set(const std::string & key, const T & value) {
    std::get<stringmap<T>>(_params)[key] = value;
}

You can do a similar dance with get, and you can always get a reference to the map of choice using std::get<stringmap<T>>(_params) for a particular type.

Tuples, C++ 11

You can do this in an identical manner, as long as you are willing to write a get routine on tuples that can be indexed by type. Here is one I wrote called tget.

template <std::size_t Pos, std::size_t Count, typename T, typename... Rest>
struct _tget_tuple_helper {
    static constexpr auto pos = Pos;
    static constexpr auto count = Count;
};

template <std::size_t Pos, std::size_t Count, typename T, typename S, typename... Rest>
struct _tget_tuple_helper<Pos, Count, T, S, Rest...> {
    static constexpr auto match = std::is_same<T, typename std::decay<S>::type>::value;
    using parent = _tget_tuple_helper<Pos + 1, match ? Count + 1 : Count, T, Rest...>;
    static constexpr auto pos = match ? Pos : parent::pos;
    static constexpr auto count = parent::count;
};

template <typename T, typename... Types>
using tget_tuple_helper = _tget_tuple_helper<0, 0, typename std::decay<T>::type, Types...>;

template <typename T, typename... Types>
constexpr
typename std::enable_if<tget_tuple_helper<T, Types...>::count == 1, T &>::type
tget(std::tuple<Types...> & t) {
    return std::get<tget_tuple_helper<T, Types...>::pos>(t);
}
template <typename T, typename... Types>
constexpr
typename std::enable_if<tget_tuple_helper<T, Types...>::count == 1, const T &>::type
tget(const std::tuple<Types...> & t) {
    return std::get<tget_tuple_helper<T, Types...>::pos>(t);
}

With this, you can replace std::get with tget in the C++ 14 example. Everything else should work just the same.

Upvotes: 0

Related Questions