Reputation: 155
I'm still trying to update my older programs to modern C++, and I have another question related to templates. I'm using libconfig++ to handle program configuration, writing plain text data to a configuration file. You can choose the type of data you want to write to the file, and the library uses an enum to do this (ie Setting::TypeString, Setting::TypeBoolean, etc.)
At first, I used plain overloading as you can see below :
void Config::write_value(Setting& root, const string& key, const string& value) {
if (!root.exists(key.c_str())) root.add(key.c_str(), Setting::TypeString) = value;
else {
Setting& s = root[key.c_str()];
s = value;
}
}
void Config::write_value(Setting& root, const string& key, const bool value) {
if (!root.exists(key.c_str())) root.add(key.c_str(), Setting::TypeBoolean) = value;
else {
Setting& s = root[key.c_str()];
s = value;
}
}
But these functions are candidates for a generic treatment, as just one parameter changes, and the type used by the library can be deduced from the parameter.
The question is : how can I do it ? I tried using conditional_t and is_same, but the compiler isn't happy about what I tried, something's wrong and I think it's related to the enum ...
What's the right way to map the enum to the parameter type ?
Here's one (of numerous) thing I tried :
template<typename T>
using Type = std::conditional_t<std::is_same_v<T, bool>, Setting::TypeBoolean, void>;
template<typename T>
void write_value(libconfig::Setting& root, const std::string& key, const T& value)
{
if (!root.exists(key.c_str())) root.add(key.c_str(), Type<T>) = value;
else {
Setting& s = root[key.c_str()];
s = value;
}
}
// Compiler error :
error C2275: 'Config::Type<bool>' : illegal use of this type as an expression
Thanks for reading me :)
Edit Setting::* is an enum, defined as Setting::Type actually.
enum Type
{
TypeNone = 0,
// scalar types
TypeInt,
TypeInt64,
TypeFloat,
TypeString,
TypeBoolean,
// aggregate types
TypeGroup,
TypeArray,
TypeList
};
Upvotes: 1
Views: 419
Reputation: 155
After some research based on @gem-taylor answer, I ended up with the following version which is working as I wanted. It still needs some tweaking to include std::string version, but it's not essential.
template <class T> struct ToMySetting;
template <> struct ToMySetting<bool> { static constexpr libconfig::Setting::Type index = libconfig::Setting::TypeBoolean; };
template <> struct ToMySetting<uint32_t> { static constexpr libconfig::Setting::Type index = libconfig::Setting::TypeInt; };
template <> struct ToMySetting<uint64_t> { static constexpr libconfig::Setting::Type index = libconfig::Setting::TypeInt64; };
template <> struct ToMySetting<float> { static constexpr libconfig::Setting::Type index = libconfig::Setting::TypeFloat; };
template <> struct ToMySetting<const char*> { static constexpr libconfig::Setting::Type index = libconfig::Setting::TypeString; };
template<class T>
void write_value(libconfig::Setting& root, const std::string& key, const T& value)
{
if (!root.exists(key.c_str())) root.add(key.c_str(), ToMySetting<T>::index) = value;
else {
libconfig::Setting& s = root[key.c_str()];
s = value;
}
}
Edit: for completeness, here are the 2 specializations for char array and std::string :
void write_value(libconfig::Setting& root, const std::string& key, const std::string& value)
{
if (!root.exists(key.c_str())) root.add(key.c_str(), libconfig::Setting::TypeString) = value.c_str();
else {
libconfig::Setting& s = root[key.c_str()];
s = value.c_str();
}
}
template<size_t N>
void write_value(libconfig::Setting& root, const std::string& key, const char (&value)[N])
{
if (!root.exists(key.c_str())) root.add(key.c_str(), libconfig::Setting::TypeString) = value;
else {
libconfig::Setting& s = root[key.c_str()];
s = value;
}
}
Upvotes: 0
Reputation: 5613
You don't explain here how the object root.add() is able to assign from an arbitrary type, but that does not appear to be the problem you are currently asking us to solve...
The problem you are currently tackling is specifically translating the template type to the appropriate enum index, and for that in c++11 I would simply define a translator class:
template <class T> class ToMySetting {};
template <> ToMySetting<int> { static Setting index = {Setting::TypeInt}; };
template <> ToMySetting<char*> { static Setting index = {Setting::TypeCStr}; };
template <> ToMySetting<bool> { static Setting index = {Setting::TypeBool}; };
You can then use that from your write_value
function as ToMySetting<T>::index
In newer versions (c++17) I would tend to use if constexpr
syntax to define the ToMySetting translator class body just once.
Upvotes: 2