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

How can I effectively use callbacks with boost::progress_display?

本文关键字:progress display 回调 boost 有效地      更新时间:2023-10-16

我想使用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百分比条被一个接一个地打印出来。

谢谢,本。

UPDATE作为对评论的回应,这里有一个简单的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动态调整比例,这几乎证明了解耦是有效的。