简体   繁体   中英

Template constructor resolution based on existence of method or free-function

Problem

Motivated by Sean Parent's "Runtime Polymorphism" I implemented a Serializable class that uses type-erasure to dispatch Serializable::serialize(...)obj.serialize(...) , where obj is a wrapped object.

struct Serializable
{
    template <typename T>
    Serializable(T obj)
        : m_self(std::make_unique<Model<T> >(std::move(obj))) {}

    /// Writes itself to a write storage
    void serialize(Storage& outStream)
    { return m_self->serialize(outStream); }  
private:
    struct Concept
    {
        virtual ~Concept() = default;
        virtual void serialize(Storage& outStream) = 0;
    };

    template <typename T>
    class Model final : public Concept
    {
    public:
        Model(T x) : m_data(std::move(x)) {}
    private:
        void serialize(Storage& outStream) override
        { m_data.serialize(outStream); } 
    private:
        T m_data;
    }; 
private:
    std::unique_ptr<Concept> m_self;
};

Now I would like to extend Serializable with another model class that would dispatch Serializable::serialize(...) to a free function with obj as an argument: Serializable::serialize(...)serialize(obj, ...)

Then I would like a template constructor of Serializable to decide which model to use by checking the existence of either T::serialize(...) or serialize(const T&, ...)

Question

Is it possible by any means (eg, SFINAE) to automatically construct Serializable so that it uses a method serialization if possible and free-function serialization otherwise?

Feel free to use any C++ standard up to C++17.

You can devise your own trait to find out whether the class has the correct serialize member. There are several ways to do it, this is one of them:

template <class T, class = void>
struct HasMemberSerialize : std::false_type
{};

template <class T>
struct HasMemberSerialize<T, std::void_t<decltype(std::declval<T>().serialize(std::declval<Storage&>()))>> : std::true_type
{};

[Live example]

Then, add a new template parameter to Model and use the trait to find its argument:

struct Serializable
{
    template <typename T>
    Serializable(T obj)
        : m_self(std::make_unique<Model<T, HasMemberSerialize<T>::value> >(std::move(obj))) {}

    /// Writes itself to a write storage
    void serialize(Storage& outStream)
    { return m_self->serialize(outStream); }  
private:
    struct Concept
    {
        virtual ~Concept() = default;
        virtual void serialize(Storage& outStream) = 0;
    };

    template <typename T, bool Member>
    class Model;
private:
    std::unique_ptr<Concept> m_self;
};

template <typename T>
class Serializable::Model<T, true> final : public Serializable::Concept
{
public:
    Model(T x) : m_data(std::move(x)) {}
private:
    void serialize(Storage& outStream) override
    { m_data.serialize(outStream); } 
private:
    T m_data;
};

template <typename T>
class Serializable::Model<T, false> final : public Serializable::Concept
{
public:
    Model(T x) : m_data(std::move(x)) {}
private:
    void serialize(Storage& outStream) override
    { serialize(m_data, outStream); } 
private:
    T m_data;
}; 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM