再谈自动注册的工厂 | 知行一

再谈自动注册的工厂

关于自动注册的工厂,我之前写过了两篇文章,之前的一篇文章在这里。这个自动注册的工厂通过宏实现自动注册,同时也可以支持变参。

下面是通过宏实现自动注册的例子。

之前提到的自动注册的工厂

问题

通过宏实现的自动注册工厂的主要问题就是需要借助宏,宏有两个问题,第一个问题是不能调试,第二个问题是让代码变得晦涩。这些自动注册的宏散落在各处,也增加了维护负担。如果能不借助宏实现自动注册就可以消除这些问题了。

改进目标

之前的自动注册工厂还需要改进,改进的目标就是消除宏。

实现

实现代码很简单,主要思路是借助crtp和静态变量初始化顺序来实现的,因为静态变量的实例化是早于main函数的,因此我们可以利用静态变量实例化的时候实现自动注册。

创建的对象需要派生于AutoMsgFactory,并实现一个静态的id函数,这个id就是创建该类型对象需要的一个唯一的id。需要注意的一个细节是:在id函数中需要使用一下基类AutoMsgFactory中的静态变量registered_,用来保证静态变量实例化。

assert(registered_);

上面这行代码在这里有两个作用,第一个作用是调用了registered_保证当前类会被自动注册,第二个作用是避免重复注册,出现重复注册时会触发断言错误。

另外通过编译期限定派生类必须定义一个Id函数可以保证派生类不会忘记定义创建该类需要的唯一id,这个id是自定义的,可以是枚举类型也可以是整形。

总结

这个实现消除了宏,在使用上也比较方便,不过也有个缺点是需要调用一下静态变量。综合来看,个人感觉比通过宏实现自动注册更好。

更新 支持变参

《再谈自动注册的工厂》有11个想法

  1. 改造的一个代码见https://wandbox.org/permlink/2xIH3LSQXPbyMyhn
    不带参数是可以编译的,带参数就不行了!
    下面的静态变量初始化,没有办法将实参u传进去。
    template
    bool AutoMsgFactory::registered_ = MsgFactory::Register(T::Id(),
    [](U &&… u) { return std::make_unique( std::forward(u)…); });

  2. 前面一个评论可以删除了!
    最新进展是实现了带参数创建对象,见http://rextester.com/KZNW65382。
    新问题是,该代码在gcc/clang都能编译通过,vc2017却编译报错,错误是在静态变量registered_外部初始化时,error C3520: ‘u': parameter pack must be expanded in this context,详细编译情况见http://rextester.com/OIFB93092。
    我在本机用最新的vc2017 v15.7.4版本,也是一样。
    请问vc2017有什么折中解决办法么?

    1. vs要把参数变成tuple才行,像这样。

  3. 有个问题
    因为继承了AutoMsgFactory所以msg有静态变量registered_。
    但是registered_的值是由MsgFactory::Register的返回值决定的,Register的一个参数是Id(),里面又出现assert(registered_)

    这样一来assert的结果是不是有点问题呢

发表评论