Как я могу эффективно использовать обратные вызовы с boost::progress_display?

Я хотел бы использовать обратные вызовы с 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 будет распечатываться один за другим.

Спасибо, Бен.


person Ben J    schedule 17.06.2014    source источник
comment
В какой библиотеке находится boost::progress_display? Я никогда не видел его раньше (уоу)   -  person sehe    schedule 17.06.2014
comment
boost.org/doc/libs/1_55_0/libs/ таймер/doc/index.html   -  person sehe    schedule 17.06.2014
comment
Я могу ошибаться, но я считаю, что это только заголовок, т.е. не требуется библиотека. Просто включите ‹boost/progress.hpp›. Это довольно удобно :-). Ах, я вижу, вы нашли его! Тем не менее, я не думаю, что вам действительно нужна зависимость от lib!   -  person Ben J    schedule 17.06.2014
comment
вы действительно ошибаетесь. Дерево включений не всегда отражает структуру библиотеки (к сожалению, например). ссылку я уже кинул :)   -  person sehe    schedule 17.06.2014
comment
Да.. это может немного раздражать. Но в любом случае, кажется, мне не нужно связываться с libboost_timer, чтобы в этом случае ссылка работала.   -  person Ben J    schedule 17.06.2014


Ответы (1)


ОБНОВЛЕНИЕ В ответ на комментарий вот простая реализация классов progress_group и group_progress_display, которые вместе упрощают отображение одного индикатора выполнения для нескольких различных элементов выполнения (экземпляров progress_group::item).

Смотрите Прямой эфир на Coliru.

Посмотрим на 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;
};

Старый ответ

Похоже, это работает Live On Coliru.

Потенциальная проблема может заключаться в том, что в данный момент индикатор выполнения каким-то образом должен иметь точную информацию об этапах процесса. Вы можете захотеть скрыть эту информацию (внутри данных или обернуть ее в класс, который также добавляет информацию о ходе выполнения), а затем, используя некоторые простые арифметические действия, вы можете преобразовать их в фиксированную шкалу (скажем, 100).

Это позволит даже вашему Foo::doWork регулировать масштаб на лету, что в значительной степени является доказательством того, что развязка работает.

person sehe    schedule 17.06.2014
comment
Большое спасибо сехе. Одна из основных проблем заключается в том, что в момент объявления boost::progress_display он выводит «процентную полосу». Представьте, если бы у Foo было два сигнала обратного вызова, которые срабатывали бы из Foo::doWork. Затем в otherFunction я бы объявил «progress_display pd» и «progress_display pd2», привязав их к соответствующим обратным вызовам и прикрепив их к Foo. Это приведет к тому, что индикаторы выполнения будут напечатаны друг над другом, а обе строки звездочек будут напечатаны под ними. - person Ben J; 17.06.2014
comment
@BenJ Тогда не делайте этого :) Конечно, вы можете обернуть progress_display в тот, который накапливает весы и прогресс. --- Это имеет мало общего с вашим первоначальным вопросом --- (кажется, я пропустил подробности о нескольких обработчиках), и это просто ограничение самого boost::progress_display. - person sehe; 17.06.2014
comment
Спасибо, да, вы правы - я не очень ясно выразился. Я надеялся, что есть какой-нибудь изящный трюк с progress_displays и обратными вызовами, и что все части встанут на свои места, как только я все сделаю правильно... Но похоже, что progress_display не был разработан для использования с обратными вызовами. Ах хорошо .. Ура! - person Ben J; 17.06.2014
comment
@BenJ Обратные вызовы не проблема. Наличие нескольких индикаторов выполнения является проблемой. Я обновил свой ответ демонстрацией, которая оборачивает дисплей, как я намекнул в своем предыдущем комментарии, используя progress_group и group_progress_display обертка (я пропустил сигналы, потому что думал, что boost::function<> здесь достаточно) - person sehe; 18.06.2014
comment
@BenJ Рад быть полезным! (Могу ли я попросить вас рассмотреть это) - person sehe; 19.06.2014