Я пытаюсь придумать легкий класс, который бы делегировал функции static/member, функторы, замыкания и т.д. В моем проекте необходимо передать такие сущности, как аргументы функции, и, кроме того, это используется в некоторых других случаев.
В первый раз я решил использовать шаблоны и наследование и получил что-то вроде этого:
template<class ReturnType, class ...Args>
class callback {
private:
//
struct abstract_invoker {
virtual ReturnType invoke(Args...) = 0;
};
//
template<class Function>
class sf_invoker : public abstract_invoker {
public:
sf_invoker(Function function) : _function(function) { };
ReturnType invoke(Args ...args) override {
return _function(args...);
}
private:
Function _function;
};
//
template<class Class>
class mf_invoker : public abstract_invoker {
using Function = ReturnType (Class::*)(Args...);
public:
mf_invoker(Class& target, Function function) : _target(target), _function(function) { };
ReturnType invoke(Args ...args) override {
return (_target.*_function)(args...);
}
private:
Class& _target;
Function _function;
};
// --------------------------------------
public:
template<class Function>
callback(Function function) {
_invoker = new sf_invoker<Function>(function);
}
template<class Class>
callback(Class& object, ReturnType(Class::*function)(Args...)) {
_invoker = new mf_invoker<Class>(object, function);
}
~callback() {
delete _invoker;
}
ReturnType operator() (Args ...args) {
return _invoker->invoke(args...);
}
private:
abstract_invoker* _invoker;
};
На самом деле, этот метод решает мою проблему, но он работает немного медленно. Я пытался избежать виртуальных функций и эмпирически обнаружил, что приведенный ниже код работает (VS 2014):
template<class ReturnType, class ...Args>
class callback {
using Function = ReturnType (*)(Args...);
public:
template<class _Function>
callback(_Function function) {
auto invoker = [function] (Args ...args) -> ReturnType {
return function(args...);
};
_invoker = &invoker;
}
template<class _Class>
callback(_Class& object, ReturnType (_Class::*function)(Args...)) {
auto invoker = [function, &object] (Args ...args) -> ReturnType {
return (object.*function)(args...);
};
_invoker = &invoker;
}
ReturnType operator()(Args ...args) {
return (*(Function*) _invoker)(args...);
}
private:
void* _invoker;
};
Очевидно, что эта реализация работает быстрее. Такой метод небезопасен, но я не очень хорошо знаю этот стандарт, поэтому я не знаю требований к компилятору для лямбда-выражений. Поэтому мой вопрос: это волшебство? Или он будет работать на большинстве компиляторов?
В вашем коде есть проблема жизни: вам нужно подумать о области действия объектов (и после этого они мертвы).
template<class Func>
callback(Func function) {
auto invoker = [function] (Args ...args) -> ReturnType {
return function(args...);
};
_invoker = &invoker;
}
invoker
захватывает состояние, поэтому не может быть преобразован в указатель на функцию. Если _invoker
был введен как Function
компилятор, вероятно, предупредил бы вас.
Замечание одинаково для второй перегрузки.
Итак, если это работает, это случайно.