5

我有一个问题,我必须比我想要的更早地实例化对象实例,因为我需要通过一些深度所有权来连接信号槽,并且我想想出一种存储和转发的方法槽,以便我可以在更接近其使用站点的位置构造对象,而不是作为成员变量这样做。

我的基本问题是我有一个进程将在单独的线程上下载更新文件并向任何感兴趣的人发送进度信号。信号本质上是:

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

假设progress下面提到的函数的实现符合这个;信号本身的性质并不是很重要(尽管我大部分时间都在使用仿函数)。

信号被设置,代码被称为如下:

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

当您调用updater.runDownloadTask()时,它将启动 UpdaterDownloadTask,它启动HTTPRequest并返回一个 HTTPResponse。它HTTPResponse是与网络层交互并接收数据并包含DownloadProgress 信号的部分。有了这个,我的实现看起来有点像(自下而上 HTTPResponse,大量缩写为不特别说明性的省略方法):

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_;
};

所以,我的更新程序必须有一个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();
}

This would have similar implications at the HTTPRequest level (so I don't have to construct the HTTPResponse until I perform the request) and overall make for a nicer data flow with strong RAII semantics. I've previously tried defining the slots_ variable as a vector:

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

Yet I can only get this to work if I force the callers to call onDownloadProgress(boost::ref(slot));.

Has anyone done this successfully, or have a good suggestion on how to store and forward other than what I'm doing?

4

1 回答 1

2

I think storing the slots in a vector should work ok. If you want to get rid of the need for boost::ref(...) you can remove the & from the onDownloadProgress parameter (since slot_type is copyable).

Alternatively, you could have your signal inside HTTPResponse fire and in turn fire a signal in HTTPRequest, doing that, you could connect all the slots to the signal in HTTPRequest, then once the HTTPResponse is created, you connect to the response signal onDownloadProgress(request.signalname). Where signalname is the signal your client.

pseudocode:

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

I hope that helps.

于 2012-01-24T03:24:01.220 回答