Есть ли какая-то библиотека, которая позволяет мне легко и удобно создавать объектно-ориентированные обратные вызовы в c ++?
Например, в языке Eiffel есть понятие «агенты», которые более или менее работают следующим образом:
class Foo{
public:
Bar* bar;
Foo(){
bar = new Bar();
bar->publisher.extend(agent say(?,"Hi from Foo!", ?));
bar->invokeCallback();
}
say(string strA, string strB, int number){
print(strA + " " + strB + " " + number.out);
}
}
class Bar{
public:
ActionSequence<string, int> publisher;
Bar(){}
invokeCallback(){
publisher.call("Hi from Bar!", 3);
}
}
] вывод будет: Привет из бара! 3 Привет от Foo!
Итак, агент позволяет заключить функцию-член в объект, передать ей некоторые предопределенные параметры вызова (Привет из Foo), указать открытые параметры (?) И передать их другому объекту. который затем может вызвать его позже.
Поскольку c ++ не позволяет создавать указатели функций на нестатических функциях-членах, кажется, что не так просто реализовать что-то столь же простое в использовании в c ++. Я нашел несколько статей в Google по объектно-ориентированным обратным вызовам в C ++, но на самом деле я ищу некоторые библиотеки или заголовочные файлы, которые я могу просто импортировать, что позволило бы мне использовать такой же элегантный синтаксис.
У кого-нибудь есть какие-нибудь советы для меня?
Спасибо!
В C ++ разрешены указатели функций на объекты-члены.
См. здесь для получения дополнительной информации.
Вы также можете использовать boost.signals или boost.signals2 (в зависимости от того, является ли ваша программа многопоточной или нет).
Существуют различные библиотеки, которые позволяют вам делать тот. Проверьте boost :: function.
Или попробуйте свою собственную простую реализацию:
template <typename ClassType, typename Result>
class Functor
{
typedef typename Result (ClassType::*FunctionType)();
ClassType* obj;
FunctionType fn;
public:
Functor(ClassType& object, FunctionType method): obj(&object), fn(method) {}
Result Invoke()
{
return (*obj.*fn)();
}
Result operator()()
{
return Invoke();
}
};
Использование:
class A
{
int value;
public:
A(int v): value(v) {}
int getValue() { return value; }
};
int main()
{
A a(2);
Functor<A, int> fn(a, &A::getValue);
cout << fn();
}
Присоединяюсь к идее функторов - используйте std::tr1::function и boost::bind для встраивания аргументов в функцию перед ее регистрацией.
Люди обычно используют один из нескольких шаблонов:
Наследование. То есть вы определяете абстрактный класс, который содержит обратный вызов. Затем вы берете на него указатель / ссылку. Это означает, что любой может унаследовать и предоставить этот обратный вызов.
class Foo {
virtual void MyCallback(...) = 0;
virtual ~Foo();
};
class Base {
std::auto_ptr<Foo> ptr;
void something(...) {
ptr->MyCallback(...);
}
Base& SetCallback(Foo* newfoo) { ptr = newfoo; return *this; }
Foo* GetCallback() { return ptr; }
};
Снова наследование. То есть ваш корневой класс является абстрактным, и пользователь наследует его и определяет обратные вызовы, а не имеет конкретный класс и выделенные объекты обратного вызова.
class Foo {
virtual void MyCallback(...) = 0;
...
};
class RealFoo : Foo {
virtual void MyCallback(...) { ... }
};
Еще более статичное наследование. Таким образом, вы можете использовать шаблоны для изменения поведения объекта. Он похож на второй вариант, но работает во время компиляции, а не во время выполнения, что может дать различные преимущества и недостатки в зависимости от контекста.
template<typename T> class Foo {
void MyCallback(...) {
T::MyCallback(...);
}
};
class RealFoo : Foo<RealFoo> {
void MyCallback(...) {
...
}
};
Вы можете взять и использовать указатели на функции-члены или обычные указатели на функции
class Foo {
void (*callback)(...);
void something(...) { callback(...); }
Foo& SetCallback( void(*newcallback)(...) ) { callback = newcallback; return *this; }
void (*)(...) GetCallback() { return callback; }
};
Есть объекты функций - они перегружают operator (). Вы захотите использовать или написать функциональную оболочку, которая в настоящее время предоставляется в std :: / boost :: function, но здесь я также продемонстрирую простую. Он похож на первую концепцию, но скрывает реализацию и принимает множество других решений. Я лично обычно использую этот метод обратного вызова.
class Foo {
virtual ... Call(...) = 0;
virtual ~Foo();
};
class Base {
std::auto_ptr<Foo> callback;
template<typename T> Base& SetCallback(T t) {
struct NewFoo : Foo {
T t;
NewFoo(T newt) : t(newt) {}
... Call(...) { return t(...); }
};
callback = new NewFoo<T>(t);
return this;
}
Foo* GetCallback() { return callback; }
void dosomething() { callback->Call(...); }
};
Правильное решение в основном зависит от контекста. Если вам нужно предоставить API в стиле C, тогда указатели на функции - единственный выход (помните void * для пользовательских аргументов). Если вам нужно изменить во время выполнения (например, выставить код в предварительно скомпилированной библиотеке), то здесь нельзя использовать статическое наследование.
Небольшое примечание: я вручную создал этот код, поэтому он не будет идеальным (например, модификаторы доступа для функций и т. Д.) И может содержать пару ошибок. Это пример.
Наиболее ОО способ использования обратных вызовов в C++ - это вызов функции интерфейса, а затем передача реализации этого интерфейса.
#include <iostream>
class Interface
{
public:
virtual void callback() = 0;
};
class Impl : public Interface
{
public:
virtual void callback() { std::cout << "Hi from Impl\n"; }
};
class User
{
public:
User(Interface& newCallback) : myCallback(newCallback) { }
void DoSomething() { myCallback.callback(); }
private:
Interface& myCallback;
};
int main()
{
Impl cb;
User user(cb);
user.DoSomething();
}
В C++ есть много возможностей, вопрос обычно заключается в синтаксисе.
boost::bind
для еще более... интересного... (*)()
, например void Functor:: operator()(int a) const;
, поскольку это объект, он имеет состояние и может происходить от общего интерфейсаstd::function
+ лямбда-функции - это действительно потрясающе, когда дело доходит до выразительности. Буду признателен за обзор синтаксиса лямбда-функций ;)
Foo foo;
std::function<void(std::string const&,int)> func =
[&foo](std::string const& s, int i) {
return foo.say(s,"Hi from Foo",i);
};
func("Hi from Bar", 2);
func("Hi from FooBar", 3);
Конечно, func
жизнеспособен только пока foo
жизнеспособен (проблема области видимости), вы можете скопировать foo
, используя [=foo]
, чтобы указать передачу по значению вместо передачи по ссылке.