代码之家  ›  专栏  ›  技术社区  ›  Moia

CRTP自动注册工厂(静态初始化命令失败)

  •  1
  • Moia  · 技术社区  · 7 年前

    我正在尝试自动注册CRTP工厂类。 我的目标是 不使用宏 注册类类型,而不显式调用register方法。

    我的尝试基于 this answer :

    工厂,H

    template<typename Base>
    struct Factory{
        static Base* create(const QString& name){
            auto callable = map.value(name);
            return callable();
        }
    
        template<typename Derived>
        static void subscribe(QString name){
            // insert already-exist-check here
            qDebug() << "registered: " << name;
            map.insert(std::move(name), [](){return new Derived{};});
        }
    
        static QMap< QString, std::function<Base*(void)> > map;
    };
    
    template<typename Base>
    QMap< QString, std::function<Base*(void)> > Factory<Base>::map;
    
    
    template<typename Base, typename Derived>
    class Registrable{
    protected:
        virtual QString registerName() const = 0;
    
        Registrable(){
            isRegistered;
        }
        ~Registrable() = default;
    
        static bool init(){
            Derived t;
            Factory<Base>::template subscribe<Derived>(t.registerName());
            return true;
        }
    
    private:
        static bool isRegistered;
    };
    
    template<typename Base, typename Derived>
    bool Registrable<Base, Derived>::isRegistered = Registrable<Base, Derived>::init();
    

    主CPP

    struct MyFactoryBase{
        virtual ~MyFactoryBase() = default;
        virtual void method() const = 0;
    };
    
    struct MyFactory1 : public MyFactoryBase, public Registrable<MyFactoryBase, MyFactory1>{
        QString registerName() const override{
            return "Class1";
        }
        void method() const override{ qDebug() << "yay Class1"; }
    
        MyFactory1() : MyFactoryBase(), Registrable<MyFactoryBase, MyFactory1>(){}
    };
    
    struct MyFactory2 : public MyFactoryBase, public Registrable<MyFactoryBase, MyFactory2>{
        QString registerName() const override{
            return "Class2";
        }
        void method() const override{ qDebug() << "yay Class2"; }
    
        MyFactory2() : MyFactoryBase(), Registrable<MyFactoryBase, MyFactory2>(){}
    };
    
    int main(){
    //    auto* fac = Factory<MyFactoryBase>::create("Class1");
    //    auto* fac2 = Factory<MyFactoryBase>::create("Class2");
    }
    

    在第一次订阅期间尝试使用map.insert()时,它会与segfault崩溃。我错过了什么?

    1 回复  |  直到 7 年前
        1
  •  2
  •   Moia    7 年前

    正如评论中所建议的,这是一个 static initialization order fiasco .

    我按照isocpp的建议解决了它。 How do I prevent the “static initialization order problem” for my static data members?

    在任何情况下,将x::x_uuu静态数据成员更改为静态成员函数都是可移植和安全的:

    所以改变

    static QMap< QString, std::function<Base*(void)> > map;
    

       static QMap< QString, std::function<Base*()> >& map(){
            static QMap< QString, std::function<Base*()> > map;
            return map;
        }
    

    并更新来自的所有呼叫 map 具有 map() 解决这个问题。

    int main(){
        auto* fac = Factory<MyFactoryBase>::create("Class1");
        auto* fac2 = Factory<MyFactoryBase>::create("Class2");
    
        fac->method();
        fac2->method();
    
        return 0;
    }
    

    输出:

    registered: Class1
    registered: Class2
    yay Class1
    yay Class2
    
    推荐文章