我想在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百分比栏会一个接一个地打印出来。
谢谢, 本。
答案 0 :(得分:1)
更新在回复评论时,这里是progress_group
和group_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
也可以即时调整比例,这几乎可以证明解耦正在起作用。