Каков наилучший способ выставить API обратного вызова - C ++ - PullRequest
1 голос
/ 27 апреля 2010

У меня есть библиотека C ++, которая должна отображать некоторые системные \ ресурсные вызовы в качестве обратных вызовов из связанного приложения. Например: связующее приложение (которое использует эту библиотеку) может отправлять функции обратного вызова управления сокетом - отправлять, получать, открывать, закрывать и т. Д., И библиотека будет использовать эту реализацию вместо реализации библиотеки. (Этот способ позволяет приложению самостоятельно управлять сокетами, может быть полезным). Эта библиотека должна также предоставлять больше обратных вызовов, как, например, проверка пароля, поэтому мне интересно, есть ли предпочтительный метод для предоставления опции отправки обратного вызова в одном API. Что-то вроде:

int AddCallbackFunc (int functionCallbackType, <generic function prototype>, <generic way to pass some additional arguments>)

Затем в моей библиотеке я назначу обратный вызов соответствующему указателю на функцию в соответствии с параметром functionCallbackType.

Есть ли способ реализовать его универсальным образом, который будет соответствовать ЛЮБОМУ прототипу функции и ЛЮБЫМ дополнительным аргументам?

Ваша помощь будет более чем оценена ... Спасибо!

Ответы [ 4 ]

3 голосов
/ 27 апреля 2010

Почему бы ему не принять аргумент 0 функтор и просто заставить пользователя использовать boost::bind для встраивания аргументов в него перед его регистрацией? В основном пример (звонки вместо магазинов, но вы получаете точку):

#include <tr1/functional>
#include <iostream>

void callback(const std::tr1::function<int()> &f) {
    f();
}

int x() {
    std::cout << "x" << std::endl;
    return 0;
}

int y(int n) {
    std::cout << "y = " << n << std::endl;
    return 0;
}

int main(int argc, char *argv[]) {
    callback(x);
    callback(std::tr1::bind(y, 5));
}

РЕДАКТИРОВАТЬ: Существует опция B, которая в основном реализует то, что bind делает под капотом, со структурами для хранения всей необходимой информации и наследования для полиморфизма ... это очень быстро становится беспорядком. Я не рекомендовал бы это, но это будет работать. Вы также можете спасти от горя, принудительно возвращая тип int, но это только немного экономит.

#include <iostream>

struct func_base {
    virtual int operator()() = 0;
};

// make one of these for each arity function you want to support (boost does this up to 50 for you :-P
struct func0 : public func_base {
    typedef int (*fptr_t)();

    func0(fptr_t f) : fptr(f) {
    }

    virtual int operator()() { return fptr(); }

    fptr_t fptr;
};

// demonstrates an arity of 1, templated so it can take any type of parameter
template <class T1>
struct func1 : public func_base {
    typedef int (*fptr_t)(T1);

    func1(fptr_t f, T1 a) : fptr(f), a1(a) {
    }

    virtual int operator()() { return fptr(a1); }

    fptr_t fptr;
    T1 a1;
};

void callback(func_base *f) {
    (*f)();
}

int x() {
    std::cout << "x" << std::endl;
    return 0;
}

int y(int n) {
    std::cout << "y = " << n << std::endl;
    return 0;
}

int main(int argc, char *argv[]) {
    // NOTE: memory leak here...
    callback(new func0(x));
    callback(new func1<int>(y, 5));
}
1 голос
/ 27 апреля 2010

Это можно сделать, используя комбинацию шаблона и стирания типа.

Идея состоит в том, чтобы взять любой тип и обернуть его в объект с известным интерфейсом.

class CallbackBase
{
public:
  virtual ~CallbackBase();

  virtual void execute();
};

template <class T>
class Callback: public CallbackBase
{
public:
  explicit Callback(T functor): mFunctor(functor) {}

  void execute() { mFunctor(); }

private:
  T mFunctor;
};

А теперь мы можем обернуть это:

template <class Function>
int AddCallbackFunc (int functionCallbackType, Function f)
{
  std::auto_ptr<CallbackBase> c(new Callback<Function>(f));

  // do something with `c`.
}

Я оставляю на усмотрение вас связывать аргументы, нет библиотечного способа создать функтор.

1 голос
/ 27 апреля 2010

Если вы не хотите использовать какой-либо из доступных вариантов C ++; std :: tr1 :: function, функторы, полиморфизм с общим базовым классом и т. д. вместо этого вы можете использовать метод C.

Клиент передает обратный вызов и указатель на свои аргументы как void *, затем обратный вызов преобразует void * в правильный тип при вызове. Вам нужно будет хранить пустоту * рядом с обратным вызовом, и вы должны быть очень осторожны с временем жизни объекта.

int AddCallbackFunc (int type, int(*callback)(void*), void* callbackData)
0 голосов
/ 27 апреля 2010

Звучит так, будто вы ищете Функтор . В основном это класс для каждого типа обратного вызова с аргументами в качестве членов данных и operator() для вызова функциональности.

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