У меня проблема в том, что мне приходится создавать экземпляры объектов раньше, чем я хотел бы, потому что мне нужно соединить сигнальные слоты через некоторое глубокое владение, и я хотел бы найти способ храненияи переадресация слотов, чтобы я мог создавать объекты ближе к месту их использования, вместо того, чтобы делать это в качестве переменных-членов.
Моя основная проблема заключается в том, что у меня есть процесс, который загружает файл обновления в отдельный потокотправить сигнал прогресса любому, кто заинтересован.По сути, это сигнал:
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));
.
Кто-нибудь сделал это успешно, или есть хорошее предложение о том, как хранить и пересылать, кроме того, что я делаю?