首页 > 解决方案 > 基于 CRTP 的类的工厂

问题描述

我将 CRTP 用于一组类:

#include <iostream>
#include <memory>
#include <string>
#include <typeinfo>

template<typename T, class Derived>
struct signal
{
    T t;   // Used elsewhere in the code...

    static std::string name() {
        return Derived::name_sub();
    }

private:
    signal() = default;
    friend Derived;
};

struct signal_boolean : signal<bool, signal_boolean>
{
    static std::string name_sub() {
        return "boolean";
    }
};

struct signal_integer : signal<int, signal_integer>
{
    static std::string name_sub() {
        return "integer";
    }
};

int main() {
    signal_boolean sb;
    signal_integer si;
    std::cout << sb.name() << std::endl;
    std::cout << si.name() << std::endl;

    return 0;
}

到目前为止一切都很好。现在我想signal用一个序列化接口来扩展这个类。对于这个问题,序列化的样子并不重要。我们假设container序列化程序为类的(反)序列化提供了一个:

// Serialization container
class container;

template<typename T, class Derived>
struct signal
{
    static std::string name() {
        return Derived::name_sub();
    }

    container to_container() const {
        container c;

        c.add_string("name", name());

        return c;
    }

private:
    signal() = default;
    friend Derived;
};

到目前为止一切都还好。我可以调用signal_boolean::to_container()来序列化一个signal_boolean对象。

但从这里开始,事情变得有点棘手:为了反序列化,我需要一个接受 acontainer并返回适当派生signal类的对象的工厂。

我遇到了这个宝石,它教会了我如何创建一个不错的可注册工厂。但是,我不知道如何正确地将其调整到我的代码中以处理我的signal类。

我首先创建了signal_factory实现可注册工厂模式的类:

    static std::string demangle(const char* name) {
        int status;
        std::unique_ptr<char, void (*)(void *)> res{
            abi::__cxa_demangle(name, NULL, NULL, &status), std::free};
        return (status == 0) ? res.get() : name;
    }

    template <class base, class ...Args>
    class factory
    {
    public:
        template <class ...T>
        static std::unique_ptr<base> make(const std::string &s, T&&... args) {
            return data().at(s)(std::forward<T>(args)...);
        }

        template <class T>
        struct registrar : base
        {
            friend T;

            static bool registerT() {
                const auto name = demangle(typeid(T).name());
                factory::data()[name] = [](Args... args) -> std::unique_ptr<base> {
                    return std::make_unique<T>(std::forward<Args>(args)...);
                };
                return true;
            }
            static bool registered;

        private:
            registrar() : base(key{}) {
                (void)registered;
            }
        };

        friend base;

    private:
        class key
        {
            key() {};

            template<class T>
            friend struct registrar;
        };

        using func_type = std::unique_ptr<base> (*)(Args...);

        factory() = default;

        static auto& data() {
            static std::unordered_map<std::string, func_type> s;
            return s;
        }
    };

    template <class base, class ...Args>
    template <class T>
    bool factory<base, Args...>::registrar<T>::registered = factory<base, Args...>::registrar<T>::registerT();

但现在我被困住了。我会让signal类继承factory::registrar自如上面链接的网站示例所示。但是,我的基类(在他们的示例中是Animal类)是一个类模板。

我怎样才能使这项工作?我是否需要扩展factory::registrar结构模板以接受模板模板参数?我试图这样做,但我很快迷失在模板语法中。

这甚至是正确的方法吗?我想要一种方法来根据我在运行时从反序列化获得的信息创建signal派生类对象container

标签: c++factorycrtp

解决方案


推荐阅读