Mr. Clear
Mr. Clear

Reputation: 652

C++ template specialisation for abstract base class

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

Answers (1)

Turtlefight
Turtlefight

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());
    }
};

(heres an example godbolt)

Upvotes: 1

Related Questions