Reputation: 33
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
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
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
Reputation: 3083
I can see several ways to do this. But the simplest one I can think of requires C++ 14 support.
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.
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