Шаблон проектирования для статического базового конструктора, который вызывает статический метод в конечном классе

0

Как я могу достичь следующего проекта с использованием С++ 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).

Наконец, есть ли какая-либо лучшая техника, которую я мог бы использовать, каким-то образом меток или меток маркировки, которые необходимо зарегистрировать, и избавить пользователя от необходимости вручную регистрировать их в отдельной функции?

  • 1
    register - это ключевое слово в C ++.
  • 1
    Я не понимаю, что ты здесь делаешь. В работе слишком много воображаемых конструкций, которые затрудняют понимание общей цели.
Показать ещё 2 комментария
Теги:
c++11
design-patterns
static
virtual-functions

2 ответа

2
Лучший ответ

Я считаю, что регулярный CRTP будет работать нормально. Это избавляет от virtual и static. Использование std::once_flag в сочетании с std::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;
}

Демо-версия

  • 0
    Это не решает проблему «одноразовой регистрации» - я поставил примечание в вопросе, чтобы сделать его более понятным.
  • 0
    @Pi Возможно, вам удастся проделать некоторую хитрость с std::call_once поскольку предлагаемый синтаксис невозможен (не может быть статических функций-членов, которые полагаются на нестатические функции-члены).
Показать ещё 2 комментария
0

Я изменил ответ 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;
}

EDIT: странно мне нужно инициализировать мой флаг с помощью {}, но Rapptz этого не сделал. Почему это?

EDIT2: этот код не работает на ideone, потому что ideone не использует -pthread флаг компилятора. Использование более простого 'static bool first = true; if (first) {first = false;...} construct будет делать трюк, поскольку С++ 11 предусматривает, что инициализация статических переменных будет потокобезопасной.

  • 1
    « РЕДАКТИРОВАТЬ: странно, мне нужно инициализировать свой флаг с помощью {}, но Раппц этого не сделал. Почему это так? » Из-за ошибки в GCC.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню