如何使用boost :: progress_display有效地使用回调?

时间:2014-06-17 13:04:19

标签: c++ boost callback boost-timer

我想在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的事件。然后我在otherFunction中创建一个Foo并附加回调:

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百分比栏会一个接一个地打印出来。

谢谢, 本。

1 个答案:

答案 0 :(得分:1)

更新在回复评论时,这里是progress_groupgroup_progress_display类的简单实现,它们可以轻松地为多个类显示单个进度条不同的进度项(progress_group::item个实例)。

查看 Live On 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();
}

注意lambda(或c ++ 03的boost::bind表达式)本身如何使共享指针保持活动状态。

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

潜在的问题是,此时,进度指示器必须以某种方式获得有关流程步骤的准确信息。您可能希望隐藏该信息(在Data内部,或将其包装在同时添加进度信息的类中),然后使用一些简单的算术,您可以转换为固定的比例(例如,100)。

这样即使你的Foo::doWork也可以即时调整比例,这几乎可以证明解耦正在起作用。