Ну, MyClass
должен знать что-то о Foo
, а именно подписи, какой бы метод вы планируете использовать в качестве обратного вызова; в противном случае, как он узнает, что передать в качестве параметров, или какой тип ожидается получить в качестве вывода? Если обратный вызов подпись известна и зафиксирована, например, int(int,int,int)
как у вас выше, вы можете использовать конструкцию, как это:
class MyClass {
std::function<int(int,int,int)> callback;
public:
int run() {
return callback(1,2,3); // or whatever
}
template <typename Class>
void SetCallback (Class& o, int (Class::*m) (int,int,int)) {
callback = [&o,m] (int a, int b, int c) { return (o.*m)(a,b,c); };
}
template <typename Class>
void SetCallback (Class const& o, int (Class::*m) (int,int,int) const) {
callback = [&o,m] (int a, int b, int c) { return (o.*m)(a,b,c); };
}
};
выше реализацию MyClass
работает следующим образом: callback
функция объект, изначально не определен, который принимает три int
s и возвращает int
. SetCallback
принимает два параметра: объект o
, на котором должен выполняться обратный вызов, и метод m
на том объекте, который соответствует сигнатуре callback
. Не волнует, что такое тип o
; благодаря стиранию типа, MyClass
никогда не должен знать, что он на самом деле звонит.
Особого внимания заслуживают две версии SetCallback
- по одному для const
и не для const
объектов. В действительности вы должны писать перегрузки для volatile
и const volatile
, но они сравнительно реже, чем const
. В будущем, когда спецификации исключений и транзакции станут частью системы типов, нам также нужно будет заботиться о noexcept
и синхронизации, и в результате комбинаторного взрыва типов будет сложно эффективно справиться без какой-либо очень умной поддержки языка. Но этот пример показывает вам, как этот код будет написан, и, вероятно, он достаточно хорош для ваших целей.
Реализация выглядит уродливой, но на самом деле она обеспечивает очень чистый интерфейс; учитывая Foo
, как вы написали выше, вы бы использовать функциональные возможности обратного вызова MyClass
«s, как это:
MyClass test;
Foo foo;
foo.d = 4;
test.SetCallback (foo, &Foo::the_func);
int result = test.run(); // result = 10
Приведенный выше код будет работать с любым типом, который имеет метод с подписью int(int,int,int)
. Обратите внимание, что вы должны позвонить SetCallback
, прежде чем позвонить run
, иначе вы получите исключение std::bad_function_call
, потому что обратный вызов еще не определен.
Я создал обходное решение, создав виртуальный класс. MyClass знает об этом, Foo реализует его. Но я бы хотел, чтобы ответ был ближе к тому, что я спросил –
Теперь у нас есть 'std :: function' и' std :: bind'. –