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

#c #oop #callback #member-function-pointers #eiffel

#c #ооп #Обратный вызов #указатель на элемент #eiffel

Вопрос:

Существует ли какая-нибудь библиотека, которая позволяет мне легко и удобно создавать объектно-ориентированные обратные вызовы на c ?

в языке Eiffel, например, есть концепция «агентов», которые более или менее работают следующим образом:

 class Foo{
public:
    Bar* bar;

    Foo(){
        bar = new Bar();
        bar-&&t;publisher.extend(a&ent say(?,"Hi from Foo!", ?));
        bar-&&t;invokeCallback();
    }

    say(strin& strA, strin& strB, int number){
        print(strA   " "   strB   " "   number.out);
    }

}

class Bar{
public:
    ActionSequence<strin&, int&&t; publisher;

    Bar(){}

    invokeCallback(){
        publisher.call("Hi from Bar!", 3);
    }
}
  

вывод будет:
Привет от Bar! 3 Привет от Foo!

Итак, агент позволяет вложить функцию member в объект, передать ей некоторые предопределенные параметры вызова (привет от Foo), указать открытые параметры (?) и передать ее какому-либо другому объекту, который затем может вызвать ее позже.

Поскольку c не позволяет создавать указатели на функции для нестатических функций-членов, кажется не таким уж тривиальным реализовать что-то столь же простое в использовании на c . я нашел в Goo&le несколько статей об объектно-ориентированных обратных вызовах в c , однако на самом деле я ищу какую-нибудь библиотеку или заголовочные файлы, которые я просто могу импортировать, которые позволяют мне использовать какой-нибудь похожий элегантный синтаксис.

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

Спасибо!

Комментарии:

1. Я не уверен, что полностью понимаю ваш синтаксис, но boost::bind его можно использовать для упаковки как функций, так и функций-членов с соответствующим интерфейсом в объект. boost.or&/doc/libs/1_44_0/libs/bind /…

2. привет! да, я думаю, это то, что я ищу. к сожалению, я не могу скомпилировать boost для iPhone (разрабатываемый на iPhone). я читал, что это также можно сделать с помощью stl. может быть, кто-нибудь объяснит, как это сделать?

Ответ №1:

Наиболее простой способ использования обратных вызовов в C — это вызвать функцию интерфейса, а затем передать реализацию этого интерфейса.

 #include <iostream&&t;

class Interface
{
  public:
  virtual void callback() = 0;
};

class Impl : public Interface
{
  public:
  virtual void callback() { std::cout << "Hi from Impln"; }
};

class User
{
  public:
  User(Interfaceamp; newCallback) : myCallback(newCallback) { }

  void DoSomethin&() { myCallback.callback(); }

  private:
  Interfaceamp; myCallback;
};

int main()
{
  Impl cb;
  User user(cb);
  user.DoSomethin&();
}
  

Ответ №2:

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

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

 class Foo {
    virtual void MyCallback(...) = 0;
    virtual ~Foo();
};
class Base {
    std::auto_ptr<Foo&&t; ptr;
    void somethin&(...) {
        ptr-&&t;MyCallback(...);
    }
    Baseamp; 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&&t; class Foo {
    void MyCallback(...) {
        T::MyCallback(...);
    }
};
class RealFoo : Foo<RealFoo&&t; {
    void MyCallback(...) {
        ...
    }
};
  

Вы можете брать и использовать указатели на функции-члены или обычные указатели на функции

 class Foo {
    void (*callback)(...);
    void somethin&(...) { callback(...); }
    Fooamp; 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&&t; callback;
    template<typename T&&t; Baseamp; SetCallback(T t) {
        struct NewFoo : Foo {
             T t;
             NewFoo(T newt) : t(newt) {}
             ... Call(...) { return t(...); }
        };
        callback = new NewFoo<T&&t;(t);
        return this;
    }
    Foo* GetCallback() { return callback; }
    void dosomethin&() { callback-&&t;Call(...); }
};
  

Правильное решение в основном зависит от контекста. Если вам нужно предоставить API в стиле C, то единственный способ — использовать указатели на функции (помните void * для пользовательских аргументов). Если вам нужно внести изменения во время выполнения (например, предоставить код в предварительно скомпилированной библиотеке), то статическое наследование здесь использовать нельзя.

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

Ответ №3:

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

Комментарии:

1. да, но только статические функции-члены. но я хочу, чтобы сам объект был уведомлен

2. Нет, указателем на функцию-член может быть любая функция-член.

3. @Mat Я настаиваю, потому что Mat, похоже, не понимает этого: C ДОПУСКАЕТ нестатические указатели на функции-члены. Однако самый умный способ сделать то, что вы хотите, — это функтор (на самом деле это просто определение оператора круглых скобок, чтобы ваши объекты могли вести себя как функции), как мне кажется, ответил.

4. @Mat вот небольшой пример: класс A{ public: void Foo(){};}; typedef (void) (A::*)() PFuncNonStatic; A a; PFuncNonStatic func = A::Foo; a.(*func)(); Возможно, я допустил некоторые ошибки, я не пытался компилировать, но это более или менее соответствует духу синтаксиса указателей на нестатические функции.

Ответ №4:

Существуют различные библиотеки, которые позволяют вам это делать. Проверьте boost::function.

Или попробуйте свою собственную простую реализацию:

 template <typename ClassType, typename Result&&t;
class Functor
{
 typedef typename Result (ClassType::*FunctionType)();
 ClassType* obj;
 FunctionType fn;
public:
 Functor(ClassTypeamp; object, FunctionType method): obj(amp;object), fn(method) {}

 Result Invoke()
 {
  return (*obj.*fn)();
 }

 Result operator()()
 {
  return Invoke();
 }
};
  

Использование:

 class A
{
 int value;
public:
 A(int v): value(v) {}

 int &etValue() { return value; }
};


int main()
{
 A a(2);

 Functor<A, int&&t; fn(a, amp;A::&etValue);

 cout << fn();
}
  

Ответ №5:

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

Ответ №6:

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

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

Я был бы признателен за обзор синтаксиса lambda 😉

 Foo foo;
std::function<void(std::strin& constamp;,int)&&t; func =
  [amp;foo](std::strin& constamp; 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] для указания передачи по значению вместо передачи по ссылке.

(*) Обязательное руководство по указателям на функции