Я хотел бы использовать обратные вызовы с boost :: progress_display. Во-первых, у меня есть класс Foo, с которого мне нужно транслировать события:
class Foo
{
public:
template <typename L>
void attachListener(L const &listener)
{
m_callback.connect(listener);
}
void doWork()
{
for(...stuff...) {
m_callback(m_someData);
}
}
private:
Data m_someData;
boost::signals2::signal<void(Data const&)> m_callback;
}
Затем в каком-то другом коде у меня есть обратный вызов, который используется для обработки событий, поступающих из Foo. Затем я создаю Foo в otherFunction и прикрепляю к нему обратный вызов:
void callback(Data const &someData, boost::progress_display &pd)
{
// other stuff
++pd;
}
void otherFunction()
{
boost::progress_display pd(100);
boost::function<void(Data const&)> f(boost::bind(&callback, _1, boost::ref(pd)));
Foo foo;
foo.attachListener(f);
foo.doWork();
}
Когда вышеприведенное будет выполнено, обратный вызов будет вызван из Foo :: doWork.
Это правильный способ использовать обратные вызовы в сочетании с boost :: progress_display?
Это может раздражать, когда мне нужно создавать и подключать несколько обработчиков, каждый со своим собственным boost :: progress_display. Это будет означать, что каждая отдельная процентная полоса progress_display выводится одна за другой.
Спасибо,
Бен.
ОБНОВИТЬ В ответ на комментарий, вот простая реализация progress_group
а также group_progress_display
классы, которые вместе позволяют легко отображать один индикатор выполнения для нескольких различных элементов прогресса (progress_group::item
экземпляры).
Видеть это Жить на Колиру.
Давайте посмотрим на progress_group
:
struct progress_group {
struct item {
size_t current, total;
item(size_t total=100, size_t current=0)
: current(current), total(total) { }
void tick() {
if (current < total) current++;
}
};
std::list<boost::weak_ptr<progress_group::item> > members;
void add(boost::shared_ptr<item> const& pi) {
assert(pi);
members.push_back(pi);
}
item get_cumulative() {
item cumul(0, 0);
for(auto& wpi : members) {
auto pi = wpi.lock();
if (pi) {
cumul.current += pi->current;
cumul.total += pi->total;
}
}
return cumul;
}
};
Обратите внимание, что я (произвольно) решил использовать слабые указатели, чтобы элементы прогресса могли исчезнуть, а масштаб будет просто отрегулирован.
Фактический прогресс динамично масштабируется до разрешения базового виджета дисплея (boost::progress_display
). Основным ограничением, которое это оставляет, является то, что прогресс должен строго увеличиваться с течением времени (так как результат может быть не tty):
struct group_progress_display {
group_progress_display() : _display(1000), _reentrancy(0) {
}
void add(boost::shared_ptr<progress_group::item> pi) {
_group.add(pi);
}
void update() {
if (1 == ++_reentrancy) // cheap synch
{
auto cumul = _group.get_cumulative();
if (cumul.total > 0)
{
size_t target = (1.0 * cumul.current)/cumul.total * _display.expected_count();
if (target >= _display.count())
_display += target - _display.count();
}
}
--_reentrancy;
}
private:
boost::progress_display _display;
progress_group _group;
boost::atomic_int _reentrancy;
};
В этом образце выполняется 100 фоновых заданий с различными нагрузками в потоках и отображается один накопительный виджет прогресса:
int main()
{
boost::thread_group workers;
group_progress_display display;
for (int i = 0; i < 100; ++i)
{
auto load = (rand()%5) * 1500;
auto progress_item = boost::make_shared<progress_group::item>(load);
display.add(progress_item);
worker this_worker(progress_item->total);
this_worker.attachListener([=,&display]{
progress_item->tick();
display.update();
});
workers.create_thread(this_worker);
}
workers.join_all();
}
(Обратите внимание, как лямбда (или boost::bind
выражение для c ++ 03) само поддерживает общий указатель.)
worker
не знает ничего о реализации прогресса:
struct worker {
explicit worker(size_t count) : count(count) {}
template <typename L>
void attachListener(L&& listener) {
m_callback = std::forward<L>(listener);
}
void operator()()
{
for (size_t i = 0; i < count; ++i) {
boost::this_thread::sleep_for(boost::chrono::microseconds(500));
m_callback();
}
}
private:
boost::function<void()> m_callback;
size_t count;
};
Старый ответ
Это будет работать Жить на Колиру.
Потенциальная проблема заключается в том, что в данный момент индикатор прогресса должен каким-то образом иметь точную информацию о шагах процесса. Возможно, вы захотите скрыть эту информацию (внутри Data или обернуть ее в класс, который также добавляет информацию о ходе выполнения), а затем использовать простую арифметику, которую вы можете перевести в фиксированный масштаб (скажем, 100).
Это позволит даже ваш Foo::doWork
настраивать масштаб на лету, что в значительной степени является доказательством того, что развязка работает.