Object -Ориентированные обратные вызовы для C ++?

Есть ли какая-то библиотека, которая позволяет мне легко и удобно создавать объектно-ориентированные обратные вызовы в 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 ++, но на самом деле я ищу некоторые библиотеки или заголовочные файлы, которые я могу просто импортировать, что позволило бы мне использовать такой же элегантный синтаксис.

У кого-нибудь есть какие-нибудь советы для меня?

Спасибо!

8
задан Mat 19 August 2010 в 09:01
поделиться

6 ответов

В C ++ разрешены указатели функций на объекты-члены.
См. здесь для получения дополнительной информации.
Вы также можете использовать boost.signals или boost.signals2 (в зависимости от того, является ли ваша программа многопоточной или нет).

2
ответ дан 5 December 2019 в 08:22
поделиться

Существуют различные библиотеки, которые позволяют вам делать тот. Проверьте 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();
}
1
ответ дан 5 December 2019 в 08:22
поделиться

Присоединяюсь к идее функторов - используйте std::tr1::function и boost::bind для встраивания аргументов в функцию перед ее регистрацией.

1
ответ дан 5 December 2019 в 08:22
поделиться

Люди обычно используют один из нескольких шаблонов:

Наследование. То есть вы определяете абстрактный класс, который содержит обратный вызов. Затем вы берете на него указатель / ссылку. Это означает, что любой может унаследовать и предоставить этот обратный вызов.

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 * для пользовательских аргументов). Если вам нужно изменить во время выполнения (например, выставить код в предварительно скомпилированной библиотеке), то здесь нельзя использовать статическое наследование.

Небольшое примечание: я вручную создал этот код, поэтому он не будет идеальным (например, модификаторы доступа для функций и т. Д.) И может содержать пару ошибок. Это пример.

5
ответ дан 5 December 2019 в 08:22
поделиться

Наиболее ОО способ использования обратных вызовов в 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();
}
11
ответ дан 5 December 2019 в 08:22
поделиться

В C++ есть много возможностей, вопрос обычно заключается в синтаксисе.

  • Вы можете использовать указатели на функции, если вам не требуется состояние, но синтаксис при этом просто ужасен. Это можно объединить с boost::bind для еще более... интересного... (*)
  • Я исправляю ваше ложное предположение, действительно возможно иметь указатель на функцию-член, просто синтаксис настолько неудобен, что вы убежите (*)
  • Вы можете использовать объекты Functor, в основном Functor - это объект, который перегружает оператор (), например void Functor:: operator()(int a) const;, поскольку это объект, он имеет состояние и может происходить от общего интерфейса
  • Вы можете просто создать свою собственную иерархию, с более красивым именем для функции обратного вызова, если не хотите идти по пути перегрузки операторов
  • Наконец, вы можете воспользоваться возможностями C++0x: 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], чтобы указать передачу по значению вместо передачи по ссылке.

(*) Обязательный учебник по указателям функций

0
ответ дан 5 December 2019 в 08:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: