Использование обратных вызовов в C ++ увеличивает связь? - PullRequest
3 голосов
/ 13 ноября 2009

Q1. Почему используются функции обратного вызова?

Q2. Обратные вызовы - это зло? Веселье для тех кто знает, для других кошмар.

Q3. Есть ли альтернатива обратному вызову?

Ответы [ 8 ]

26 голосов
/ 13 ноября 2009

Обратные вызовы уменьшают связь - вызываемой стороне передается какой-то указатель, и она не знает, что за этим стоит. Обратные вызовы - это такое удачное решение, что они очень распространены.

Например, посмотрите на sqlite3_exec () . Вы даете ему запрос и, возможно, обратный вызов. Он выполняет запрос и вызывает обратный вызов для каждой строки при ее получении. Теперь задача SQLite - выполнить запрос быстро и с низким потреблением ресурсов, и только ваша задача - обработать полученные результаты так, как вам нравится. Вы можете добавить их в контейнер и обработать все позже, или вы можете обрабатывать их немедленно, один за другим, или вы можете отправить их по факсу куда-нибудь и ожидать, что другая сторона отправит их по факсу - SQLite не волнует, он полностью абстрагирован и может делать свою работу.

4 голосов
/ 13 ноября 2009

Независимо от "использования обратных вызовов в C ++ для повышения связи" или нет, я предлагаю использовать стиль обработчика событий, особенно для обработки событий, подобных событиям. Например, конкретизируйте шаблон проектирования вместо концепции обратного вызова, как показано ниже:

class MyClass
{
public:
    virtual bool OnClick(...) = 0;
    virtual bool OnKey(...) = 0;
    virtual bool OnTimer(...) = 0;
    virtual bool OnSorting(...) = 0
    ...
};

Вы все еще можете считать вышеупомянутые функции обратными вызовами, но, рассматривая их как известный шаблон проектирования, вы не запутаетесь, потому что выполняете OO и пишете C ++.

Effo UPD @ 2009nov13 - Типичные случаи: Framework, Система событий или Модель параллельного программирования и т. Д. Следующие примеры должны быть полезны.

Framework контролирует весь процесс, как гласит Голливудский принцип: «Не звоните нам, мы вам позвоним». (это именно то, что означает «обратный вызов»), в то время как для обычной функции или lib, вызывающая сторона контролирует поток.

Хорошо известной средой C является ядро ​​Linux, и писатель драйвера Linux знает, что он / она реализует в нем «struct file_operations», «read ()» означает OnRead (), а «write ()» означает OnWrite ( ) и т. д.

struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
        ...
};

Но самый простой пример фреймворка должен быть:

    The Framework           |     A developer to do
----------------------------+--------------------------------------
                            |    class MyClass : public Actor
                            |    {     
                            |    public:
pApplication->Init(...);    |        virtual bool OnInit(...) {}
pApplication->Run(...);     |        virtual int OnRun(...) {}  
pApplication->Quit(...);    |        virtual void OnQuit(...) {}
                            |        ...
                            |    };

и pApplication-> Init () вызовут pActor-> OnInit, а pApplication-> Run () вызовет pActor-> OnRun () и т. Д. Внутри. Большинство разработчиков графических интерфейсов Windows испытали реализацию OnMouseClick () или OnButtonPress () и т. Д.

Я согласен с другими ответами этого потока, что они дают правильное объяснение на основе соответствующей точки зрения, например, обработчики в многоуровневом подходе, обобщенный обратный вызов или асинхронные операции и так далее. Вам решать, какие идеи подойдут для вас.

3 голосов
/ 13 ноября 2009

Q3. Есть ли альтернатива обратному вызову?

Я предпочитаю функторную форму обратного вызова. например такого рода вещи:

class WidgetContainerOrProcessorOfSomeSort
{
public:
  struct IWidgetFunctor
  {
    virtual bool operator()( const Widget& thisWidget )=0;
  };
  ....
  bool ProcessWidgets( IWidgetFunctor& callback )
  {
     .....
     bool continueIteration = callback( widget[ idxWidget ] );
     if ( !continueIteration ) return false;
     .....
  }
};

struct ShowWidgets : public WidgetContainerOrProcessorOfSomeSort::IWidgetFunctor
{
  virtual bool operator(){ const Widget& thisWidget }
  {
     thisWidget.print();
     return true;
  }
};

WidgetContainterOrProcessorOfSomeSort wc;
wc.ProcessWidgets( ShowWidgets() );

Для простых примеров это всегда кажется немного многословным, но в реальном мире мне гораздо проще, чем пытаться точно вспомнить, как создавать объявления указателей сложных функций:

1 голос
/ 14 ноября 2009
  1. Обратные вызовы уменьшают связность, так как позволяют писать код, который вызывает функцию, которую можно изменить
  2. Обратные вызовы не являются злом, просто если вы используете необработанные указатели на функции, все может очень быстро запутаться.
  3. Нет альтернативы обратным вызовам как таковым, но есть альтернативы использованию необработанных указателей на функции.

Пост, указывающий на Boost, был сделан ранее для Boost.Function. Если вы ищете более общее решение для обратных вызовов, например, несколько функций, прикрепленных к одному и тому же обратному вызову или что-то подобное, рассмотрите возможность использования Boost.Signals . Название происходит от сигналов и слотов, так в наши дни некоторые люди обращаются к обратным вызовам, особенно для графических интерфейсов.

0 голосов
/ 13 ноября 2009

В редких сценариях с обратными вызовами в стиле Win32 следите за проблемами "неясного повторного входа" (следующее не имеет значения, если под обратным вызовом вы просто подразумеваете очень простую передачу функции в виде аргумента в другую функцию, где проблемы параллелизма невообразимы).
Отличный указатель к «Параллельному программированию в Windows» Джо Даффи перечисляет 5 тем в разделе «Вступление», которые, как правило, являются специализациями концепции «Вокруг сарая Робин-Гуда« ОДНА ЖЕ НИТИ »- другими словами, из топ-рейтинга -ответ "вызванной стороне передан какой-то указатель, и она не имеет ни малейшего представления, что за этим стоит", "иногда ни одна идея" не может обойти сарай Робин-Гуда.
Ничто из того, что я только что сказал, не относится к обратным вызовам, но если вызываемая сторона наталкивается на один из сценариев Даффи, тогда может произойти «неясное возвращение». Иными словами, в понятии «обратный вызов» подразумевается, что вы, похоже, будете вызываться обратно в одном потоке, как это происходит, как синхронизируется.
Если вы введете в Google название книги Даффи и добавите слово «Обратный вызов» в свой поиск, вы получите более 10 страниц просмотров.

0 голосов
/ 13 ноября 2009

Если мы в контексте C ++, рассмотрим использование (например, обобщенные обратные вызовы ).

Основная идея поведения заключается в том, что обратный вызов (метод класса) может иметь любое имя, и обратный вызов не требуется для получения от некоторого класса, известного исполнителю обратного вызова.

Единственное ограничение на обратный вызов - это входные аргументы и возвращаемое значение. Так что это сводит пару к нулю ...:)

UDP:

Ответ @EffoStaff Effo - это пример, где обратный вызов должен быть определенного класса (производного) и иметь имя исправления. Все эти «ограничения» отсутствуют в контексте обобщенных обратных вызовов.

0 голосов
/ 13 ноября 2009

Q1. Обратные вызовы необходимы, если вы используете многоуровневый подход, при котором вызовы более высоких уровней снижаются, а обратные вызовы получают с помощью обратных вызовов.
Q2. При соблюдении некоторых мер предосторожности они не хуже, чем, например. исключения. В чем-то они похожи. Q3. Больше связи: низшие влюбленные знают выше.

Примечания:
- простой способ (1 обработчик обратного вызова на обратный вызов): регистрация объектов CallbackHandler через интерфейсы
- Используйте сигналы (QT, boost, ...) и убедитесь, что для каждого обратного вызова используются уникальные сигналы для улучшения отслеживаемости

Редактировать: пример:

Пользователь вызывает ProtocolHandler для отправки сообщения, а ProtocolHandler вызывает пользователя для отправки ответа: взаимозависимость.

Layered: пользователь более высокого уровня, ProtocolHandler более низкого уровня. При запуске он регистрирует обратный вызов для ответа и вызывает ProtocolHandler для отправки сообщения. ProtocolHandler использует обратный вызов для отправки ответа: только ProtocolHandler зависит только от пользователя.

0 голосов
/ 13 ноября 2009
  1. Обратные вызовы используются, например, для вызова асинхронных операций, то есть кода, который выполняется в другом потоке, в то время как вызывающая сторона следует своим собственным способом. Вам нужен какой-то механизм, чтобы знать, когда асинхронная операция завершилась.

  2. Почему они были бы злыми? Как и с любым другим программным ресурсом, они полезны при разумном использовании. Например, Windows API и .NET Framework широко используют обратные вызовы.

  3. Не знаю о C ++, но в мире .NET объекты и события синхронизации являются альтернативами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...