【问题标题】:Design pattern for static base constructor that calls static method in final class在最终类中调用静态方法的静态基构造函数的设计模式
【发布时间】:2014-11-15 14:48:50
【问题描述】:

如何使用 C++11 实现以下设计:

class Consumer : Base<Consumer>
{
    // different consumers will have different methods (of the same signature)
    void Foo(){...}
    void Bar(){...}
    : // etc.

    static void override
    register_all () {
        register_method<&Consumer::Foo>("foo");
        register_method<&Consumer::Bar>("bar");
        : // etc.
    }
    :
}

template<typename T>
class Base
{
    static 
    Base() { 
        register(); 
    }

    virtual static void
    register_all(){ };

    using F = void(T::*)();

    template<F f>
    static void 
    register_method(std::string name) {
        ...
    }
}

...?

请注意,我在做两件违法的事情:

  • 我在基类上使用静态构造函数(不允许)
  • 我正在使用虚拟静态函数(也不允许)

注意:方法的注册只需要发生一次,在访问第一个实例之前(它将填充静态 C 函数指针表)。

最后,我可以使用什么更好的技术,以某种方式标记或标记需要注册的方法,并省去消费者必须在单独的函数中手动注册它们的麻烦?

【问题讨论】:

  • register 是 C++ 中的关键字。
  • 我不明白你在这里做什么。有太多想象的结构在起作用,很难理解整体观点。
  • 您要注册的方法的名称是已知的并且是全局固定的,还是每个派生类自己决定注册什么?
  • 谢谢,问题已编辑!

标签: c++ design-patterns c++11 static virtual-functions


【解决方案1】:

我相信普通的CRTP 可以正常工作。这消除了virtualstatic。将std::once_flagstd::call_once 结合使用将允许您只调用该函数一次——模仿“静态构造函数”的效果。它只需要一点点混乱。

完整代码:

#include <iostream>
#include <mutex>

template<typename T>
struct Base {
    Base() {
        static_cast<T*>(this)->register_all();
    }

    using F = void(T::*)();

    template<F f>
    void register_method(const char* str) {
        // ...
        std::cout << "registered " << str << '\n';
    }
};

struct Derived : Base<Derived> {
private:
    static std::once_flag flag_;
    void implementation() {
        register_method<&Derived::foo>("foo");
        register_method<&Derived::bar>("bar");
    }
public:
    void foo() {}
    void bar() {}

    void register_all() {
        std::call_once(flag_, [this]{ implementation(); });
    }
};

std::once_flag Derived::flag_;

int main() {
    Derived x;
    Derived y;
}

Live Demo

【讨论】:

  • 这并没有解决“一次性注册”的问题——我已经在问题中添加了注释以使其更清楚。
  • @Pi 你可以用std::call_once 做一些诡计,因为建议的语法是不可能的(不能有依赖于非静态成员函数的静态成员函数)。跨度>
  • 抱歉,我在最初的伪代码中犯了很多错误:/ 我已经添加了必要的 static-s。顺便说一句,CRTP 很好地解决了静态覆盖问题——谢谢!
  • @Pi 我已经更新了答案以展示我对 std::call_once 的意思。
【解决方案2】:

我修改了 Rapptz 的答案,将所有机器移回基类:

http://coliru.stacked-crooked.com/a/52fd723e905333c6

#include <iostream>
#include <mutex>

template<typename T>
struct Base {
    Base() {
        std::call_once(flag_, T::register_all );
    }

    using F = void(T::*)();

    template<F f>
    static void register_method(const char* str) {
        // ...
        std::cout << "registered " << str << '\n';
    }
public:
    static std::once_flag flag_;
};

// have to initialise static-s outside class...
template<typename T>
std::once_flag Base<T>::flag_{};

...

struct Derived1 : Base<Derived1> {
public:
    static void register_all() {
        std::cout << "D1" << '\n';
        register_method<&Derived1::foo>("foo");
        register_method<&Derived1::bar>("bar");
    }
public:
    void foo() {}
    void bar() {}
};

struct Derived2 : Base<Derived2> {
public:
    static void register_all() {
        std::cout << "D2" << '\n';
        register_method<&Derived2::woot>("woot");
    }
public:
    void woot() {}
};

...

int main() {
    Derived1 x;
    Derived1 y;

    Derived2 z;
}

编辑:奇怪的是我需要用 {} 初始化我的标志,但 Rapptz 没有。这是为什么呢?

EDIT2:此代码在 ideone 上失败,因为 ideone 不使用 -pthread 编译器标志。使用更简单的 'static bool first=true; if(first){first = false;...} 构造可以解决问题,因为 c++11 规定静态变量的初始化是线程安全的。

【讨论】:

  • "编辑:奇怪的是我需要用 {} 初始化我的标志,但 Rapptz 没有。为什么会这样?" 因为 GCC 错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-06
  • 2018-09-25
  • 1970-01-01
相关资源
最近更新 更多