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();
}

これは、HTTPRequest レベルで同様の意味を持ち (したがって、リクエストを実行するまで HTTPResponse を構築する必要はありません)、全体的に強力な RAII セマンティクスを備えたより優れたデータ フローを実現します。私は以前、slots_変数をベクトルとして定義しようとしました:

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

それでも、発信者に強制的に onDownloadProgress(boost::ref(slot));.

誰かがこれを成功させましたか、または私がしていること以外に保存および転送する方法について良い提案がありますか?

4

1 に答える 1

2

スロットをベクターに保存するとうまくいくはずです。の必要性を取り除きたい場合は、パラメーターからboost::ref(...)を削除できます (はコピー可能であるため)。&onDownloadProgressslot_type

別の方法として、内部で信号をHTTPResponse発生させ、次に で信号を発生させることもできます。これHTTPRequestにより、すべてのスロットを で信号に接続しHTTPRequestHTTPResponseが作成されたら、応答信号に接続できますonDownloadProgress(request.signalname)signalnameクライアントの信号はどこにありますか。

疑似コード:

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

それが役立つことを願っています。

于 2012-01-24T03:24:01.220 に答える