Как я могу достичь следующего проекта с использованием С++ 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).
Наконец, есть ли какая-либо лучшая техника, которую я мог бы использовать, каким-то образом меток или меток маркировки, которые необходимо зарегистрировать, и избавить пользователя от необходимости вручную регистрировать их в отдельной функции?
Я считаю, что регулярный 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;
}
std::call_once
поскольку предлагаемый синтаксис невозможен (не может быть статических функций-членов, которые полагаются на нестатические функции-члены).
Я изменил ответ 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 предусматривает, что инициализация статических переменных будет потокобезопасной.
register
- это ключевое слово в C ++.