如何使用boost::signals2存储和转发插槽



我遇到了一个问题,必须实例化对象的实例比我想做的更早,因为我需要连接信号通过一些深度所有权,我想想出一种方法存储和转发插槽,以便我可以更紧密地构建对象到他们的使用站点,而不是作为成员变量这样做。

我的基本问题是我有一个下载更新的过程文件在一个单独的线程上,并向任何感兴趣的信号本质上是:

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

假设上述progress函数的实现以下内容符合这一点;信号本身的性质不是很好重要的(尽管我在大多数情况下使用函子)。

信号被设置,代码被称为这样的东西:

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

当您调用updater.runDownloadTask()时,它将启动UpdaterDownloadTask,它启动一个HTTPRequest并返回一个HTTPResponseHTTPResponse是与网络层,并接收数据并包含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_;

然而,只有我强迫打电话的人打电话,我才能做到这一点CCD_ 16。

有人成功地做到了这一点吗?或者对如何做到这一点有很好的建议吗存储和转发而不是我正在做的事情?

我认为将插槽存储在向量中应该可以正常工作。如果您想消除对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);

我希望这能有所帮助。

最新更新