2016-05-02 3 views
0

Мой C++ ржавый. Я бы хотел, чтобы MyClass ничего не знал о Foo и имел обратный вызов для запуска функции в foo. Я не знаю, как определить callback_pair или как позвонить SetCallbackОбратный вызов неизвестного класса + функции

class MyClass{ 
    tuple<cb, ptr> callback_pair 

    int run() { 
     auto that=callback_pair<1>(); 
     auto cb = callback_pair<0>(); 
     int a=1, b=2, c=3; 
     auto result = cb(that, a, b, c); //this calls foo.the_function 
     return result; 
    } 
    void SetCallback(tuple<cb, ptr> cb) { callback_pair=cb;) 
}; 

class Foo { 
    int d; 
    int the_func(int a, b, c) { 
     return a+b+c+d 
    } 
} 
//myclass.SetCallback(what_do_I_write_here) 
+0

Я создал обходное решение, создав виртуальный класс. MyClass знает об этом, Foo реализует его. Но я бы хотел, чтобы ответ был ближе к тому, что я спросил –

+0

Теперь у нас есть 'std :: function' и' std :: bind'. –

ответ

1

Ну, 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, потому что обратный вызов еще не определен.

Смежные вопросы