Reputation: 652
I want to write a Settings
class to load and store settings of various types:
template <typename T>
class Setting {
std::string m_name;
T m_defaultValue;
T load() {
if(backend.contains(m_name))
return backend.load<T>(m_name);
return m_defaultValue;
}
void save(const T &value) {
backend.save(m_name, value);
}
}
The backend
has implementaions for some basic types, but not for my own. So I need template specializations for my types. My idea is to create a Serializable
class and derive my classes from it:
class Serializable {
public:
virtual std::string serialize() const = 0;
virtual void deserialize(const std::string &data) = 0;
}
Now I need a template specialization for load
and save
in the Setting class to use the serialize
and deserialize
functions in Serializable:
template<>
void Setting<Serializable>::load()
{
if(backend.contains(m_name)) {
Serializable s;
s.deserialize(backend.load<std::string>(m_name);
return s;
}
return m_defaultValue;
}
template<>
void Setting<Serializable>::save(const Serializable &value)
{
backend.save(m_name, value.serialize());
}
Obviously this doesn't work because in Setting<Serializable>::load()
I cannot instantiate Serializable s
. But even if I leave out load
for the time being, it does not work because it cannot declare field ‘Setting<Serializable>::m_defaultValue’ to be of abstract type ‘Serializable’
.
Is there a way to write a different implementation for all subtypes of Serializable
but still use a template parameter T
?
Upvotes: 2
Views: 346
Reputation: 10700
You could do this by creating a specialized version of Setting that is only available if the type of the setting inherits from Serializable.
By using partial template specialization with std::enable_if
you can create a separate Settings class that will only be used if the Setting type implements Serializable
.
e.g.:
// Normal Setting class, for the primitive types your backend supports
template <typename T, typename _ = void>
class Setting {
std::string m_name;
T m_defaultValue;
public:
T load() {
if(backend.contains(m_name))
return backend.load<T>(m_name);
return m_defaultValue;
}
void save(const T &value) {
backend.save(m_name, value);
}
};
// specialized Setting class that will apply to all classes that implement Serializable
template<typename T>
class Setting<T, std::enable_if_t<std::is_base_of_v<Serializable, T>>> {
std::string m_name;
T m_defaultValue;
public:
T load() {
if(backend.contains(m_name)) {
T s;
s.deserialize(backend.load<std::string>(m_name));
return s;
}
return m_defaultValue;
}
void save(const T &value) {
backend.save(m_name, value.serialize());
}
};
Upvotes: 1