Как я могу сохранить и переслать слоты, используя boost :: signal2? - PullRequest
5 голосов
/ 20 января 2012

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

Моя основная проблема заключается в том, что у меня есть процесс, который загружает файл обновления в отдельный потокотправить сигнал прогресса любому, кто заинтересован.По сути, это сигнал:

typedef boost::signals2::signal<void (double)> DownloadProgress;

Предположим, что реализация функции progress, упомянутой ниже, соответствует этому;сама природа сигнала не очень важна (хотя я по большей части использую функторы).

Сигнал установлен, и код называется примерно так:

Updater updater;
updater.onDownloadProgress(&progress);
updater.runDownloadTask();

Когда вы звоните updater.runDownloadTask(), он запускает UpdaterDownloadTask, который начинает HTTPRequest и возвращает HTTPResponse.HTTPResponse - это элемент, который взаимодействует с сетевым уровнем, получает данные и содержит сигнал DownloadProgress.При этом моя реализация выглядит примерно так (снизу вверх от HTTPResponse, в значительной степени сокращенная до elide методов, которые не особенно показательны):

class HTTPResponse
{
public:
  // this will be called for every "chunk" the underlying HTTP
  // library receives
  void processData(const char* data, size_t size)
  {
    // process the data and then send the progress signal
    // assume that currentSize_ and totalSize_ are properly set
    progressSignal_(currentSize_ * 100.0 / totalSize_);
  }

  void onDownloadProgress(const DownloadProgress::slot_type& slot)
  {
    progressSignal_.connect(slot);
  }

private:
  DownloadProgress progressSignal_;
};

class HTTPRequest
{
public:
  HTTPRequest() : response_(new HTTPResponse) { }

  void onDownloadProgress(const DownloadProgress::slot_type& slot)
  {
    response_->connect(slot);
  }

  boost::shared_ptr<HTTPResponse> perform()
  {
    // start the request, which operates on response_.
    return response_;
  }

private:
  boost::shared_ptr<HTTPResponse> response_;
};

class UpdaterDownloadTask : public AsyncTask
{
public:
  DownloadTask() : request_(new HTTPRequest) { }

  void onDownloadProgress(const DownloadProgress::slot_type& slot)
  {
    request_->connect(slot);
  }

  void run()
  {
    // set up the request_ and:
    request_>perform();
  }

private:
  boost::shared_ptr<HTTPRequest> request_;
};

class Updater
{
public:
  Updater() : downloadTask_(new UpdaterDownloadTask) { }
  void onDownloadProgress(const DownloadProgress::slot_type& slot)
  {
    downloadTask_->onDownloadProgress(slot);
  }

  void runDownloadTask() { downloadTask_.submit() }

private:
  boost::shared_ptr<UpdaterDownloadTask> downloadTask_;
};

Итак, мой Updater должен иметь экземплярUpdaterDownloadTask, который всегда рядом, который имеет экземпляр HTTPRequest, который имеет экземпляр HTTPResponse - просто потому, что мне нужно перенаправить соединение через слот с Updater (точка входа публичного API) на HTTPResponse (где сигнал принадлежит).

Я бы предпочел реализовать UpdaterDownloadTask::run() примерно так:

void run()
{
  HTTPRequest request;
  request.onDownloadProgress(slots_);

#if 0
  // The above is more or less equivalent to
  BOOST_FOREACH(const DownloadProgress::slot_type& slot, slots_)
  {
      request.onDownloadProgress(slot);
  }
#endif

  request.perform();
}

Это будет иметь аналогичные последствия на уровне HTTPRequest (поэтому мне не нужно создаватьHTTPResponse до тех пор, пока я не выполню запрос) и в целом создаст более приятный поток данных с сильной семантикой RAII.Ранее я пытался определить переменную slots_ как вектор:

std::vector<DownloadProgress::slot_type> slots_;

И все же я смогу заставить это работать, только если заставлю звонящих вызывать onDownloadProgress(boost::ref(slot));.

Кто-нибудь сделал это успешно, или есть хорошее предложение о том, как хранить и пересылать, кроме того, что я делаю?

1 Ответ

2 голосов
/ 24 января 2012

Я думаю, что сохранение слотов в векторе должно работать нормально. Если вы хотите избавиться от необходимости boost::ref(...), вы можете удалить & из параметра onDownloadProgress (так как slot_type можно копировать).

В качестве альтернативы, вы можете иметь свой сигнал внутри HTTPResponse сработать и, в свою очередь, запустить сигнал в HTTPRequest, сделав это, вы можете подключить все слоты к сигналу в HTTPRequest, затем, когда HTTPResponse будет создано, вы подключаетесь к ответному сигналу onDownloadProgress(request.signalname). Где signalname - это сигнал вашего клиента.

псевдокод:

Request request;
request.onProgress(myProgressBarCallback);
    //calls: this.signal.connect(myProgressBarCallback);
request.go();
    //calls: Response response;
    //  and: response.onProgress(this.signal);

Надеюсь, это поможет.

...