使用 boost.signals2 中继信号
Relaying a signal with boost.signals2
从下面的代码中可以看出(作为问题的说明实现),我正在尝试从内部 class 向中间 class 发送信号,这将将其转发到外部 class.
#include <boost/bind.hpp>
#include <boost/signals2.hpp>
#include <iostream>
class inner {
public:
template <class T>
void register_callback(boost::function<void(T *)> cb, T *obj)
{
sig_inner_.connect(boost::bind(cb, boost::ref(obj)));
}
void trigger()
{
std::cout << "inner" << std::endl;
sig_inner_();
}
private:
boost::signals2::signal<void()> sig_inner_;
};
class mid {
public:
mid() { inner_obj.register_callback<mid>(&mid::handle_sig_mid, this); }
template <class T>
void register_callback(boost::function<void(T *)> cb, T *obj)
{
sig_mid_.connect(boost::bind(cb, boost::ref(obj)));
}
void trigger() { sig_mid_(); }
void inner_trigger() { inner_obj.trigger(); }
void handle_sig_mid()
{
std::cout << "mid" << std::endl;
trigger();
}
private:
boost::signals2::signal<void()> sig_mid_;
inner inner_obj;
};
class outer {
public:
outer() { mid_obj.register_callback<outer>(&outer::handle_sig_outer, this); }
void inner_trigger() { mid_obj.inner_trigger(); }
private:
mid mid_obj;
void handle_sig_outer() { std::cout << "outer" << std::endl; }
};
int main()
{
outer outer_obj;
outer_obj.inner_trigger();
return 0;
}
而不是期望的结果:
inner
mid
outer
程序运行时,实际发生的情况是:
inner
mid
mid
随后崩溃。
我已经注意到 'this' 在处理程序中的地址与我在常规方法中所期望的不同,但我不知道如何解决这个问题。
我为此找到的唯一解决方案是将外部 class 中的信号连接到其处理程序,然后将指针(unique_ptr,在本例中)存储在内部 class,从而避免了中继它的需要,但感觉它并不是一种使用信号的安全方式。
我是 c++ 的新手,尤其是 boost,所以我真的不知道如何从内部 class 中干净地触发外部 class 中的回调安全的方式。
无论您在何处调用处理程序,都将当前 class 实例的 std::shared_ptr
实例传递给它。您可以通过 public 继承 enable_shared_from_this
来实现。这样,您的 mid
实例将保持活动状态,直到处理程序完成。
class mid : public std::enable_shared_from_this<mid>
{
mid()
{
inner_obj.register_callback<mid>(&mid::handle_sig_mid, shared_from_this());
}
//...
};
两件事:
当您绑定到 boost::ref(obj)
时,您使绑定表达式持有对函数参数的引用,该函数参数在 register_callback
的出口处超出范围。 (参见 Does boost::bind() copy parameters by reference or by value?)
只需绑定到指针本身,这使得绑定表达式持有指针本身的副本。
注册回调时注意生命周期问题很重要。通常,您必须在销毁信号槽中的任何绑定对象之前取消注册。
在你的例子中,这并没有真正发生,因为连接的槽都存在于成员对象中。这意味着插槽在外部对象消失之前被破坏。
但是,如果某些东西 copied/moved 出现故障。解决这个问题的常用模式是使用 scoped_connection
s.
让我分两步展示我的建议:
简化:尽早绑定,Signals2 为您进行类型擦除
不需要模板 register_callback
因为您使用 bind
和空信号槽立即擦除对象类型 T
。
所以,改为让它采用任意 nullary 并在调用者中进行绑定?事实上,更喜欢在调用者处使用 lambda。
template <class F> void register_callback(F&& f) {
sig_inner_.connect(std::forward<F>(f));
}
然后
mid() { inner_obj.register_callback([this] { handle_sig_mid(); }); }
生命周期和信号2:连接
不要使用重量级选项和随处使用动态分配的 enable_shared_from_this()
,而是使用库设施:http://www.boost.org/doc/libs/1_65_1/doc/html/boost/signals2/scoped_connection.html
Note In your example, using shared_from_this()
is out of the question because it's not valid inside the constructor.
我的建议:
template <class F> boost::signals2::scoped_connection register_callback(F&& f) {
return sig_inner_.connect(std::forward<F>(f));
}
然后
mid() { _connection = inner_obj.register_callback([this] { handle_sig_mid(); }); }
让 _connection
成为会员:
boost::signals2::scoped_connection _connection;
这样,当包含的 class 被破坏时,槽会断开连接。
完整演示
#include <boost/bind.hpp>
#include <boost/signals2.hpp>
#include <iostream>
class inner {
public:
template <class F> boost::signals2::scoped_connection register_callback(F&& f) {
return sig_inner_.connect(std::forward<F>(f));
}
void trigger() {
std::cout << "inner" << std::endl;
sig_inner_();
}
private:
boost::signals2::signal<void()> sig_inner_;
};
class mid {
public:
mid() { _connection = inner_obj.register_callback([this] { handle_sig_mid(); }); }
template <class F> boost::signals2::scoped_connection register_callback(F&& f) {
return sig_mid_.connect(std::forward<F>(f));
}
void trigger() { sig_mid_(); }
void inner_trigger() { inner_obj.trigger(); }
void handle_sig_mid() {
std::cout << "mid" << std::endl;
trigger();
}
private:
boost::signals2::scoped_connection _connection;
boost::signals2::signal<void()> sig_mid_;
inner inner_obj;
};
class outer {
public:
outer() { _connection = mid_obj.register_callback([this] { handle_sig_outer(); }); }
void inner_trigger() { mid_obj.inner_trigger(); }
private:
boost::signals2::scoped_connection _connection;
mid mid_obj;
void handle_sig_outer() { std::cout << "outer" << std::endl; }
};
int main() {
outer outer_obj;
outer_obj.inner_trigger();
return 0;
}
版画
inner
mid
outer
从下面的代码中可以看出(作为问题的说明实现),我正在尝试从内部 class 向中间 class 发送信号,这将将其转发到外部 class.
#include <boost/bind.hpp>
#include <boost/signals2.hpp>
#include <iostream>
class inner {
public:
template <class T>
void register_callback(boost::function<void(T *)> cb, T *obj)
{
sig_inner_.connect(boost::bind(cb, boost::ref(obj)));
}
void trigger()
{
std::cout << "inner" << std::endl;
sig_inner_();
}
private:
boost::signals2::signal<void()> sig_inner_;
};
class mid {
public:
mid() { inner_obj.register_callback<mid>(&mid::handle_sig_mid, this); }
template <class T>
void register_callback(boost::function<void(T *)> cb, T *obj)
{
sig_mid_.connect(boost::bind(cb, boost::ref(obj)));
}
void trigger() { sig_mid_(); }
void inner_trigger() { inner_obj.trigger(); }
void handle_sig_mid()
{
std::cout << "mid" << std::endl;
trigger();
}
private:
boost::signals2::signal<void()> sig_mid_;
inner inner_obj;
};
class outer {
public:
outer() { mid_obj.register_callback<outer>(&outer::handle_sig_outer, this); }
void inner_trigger() { mid_obj.inner_trigger(); }
private:
mid mid_obj;
void handle_sig_outer() { std::cout << "outer" << std::endl; }
};
int main()
{
outer outer_obj;
outer_obj.inner_trigger();
return 0;
}
而不是期望的结果:
inner
mid
outer
程序运行时,实际发生的情况是:
inner
mid
mid
随后崩溃。
我已经注意到 'this' 在处理程序中的地址与我在常规方法中所期望的不同,但我不知道如何解决这个问题。
我为此找到的唯一解决方案是将外部 class 中的信号连接到其处理程序,然后将指针(unique_ptr,在本例中)存储在内部 class,从而避免了中继它的需要,但感觉它并不是一种使用信号的安全方式。
我是 c++ 的新手,尤其是 boost,所以我真的不知道如何从内部 class 中干净地触发外部 class 中的回调安全的方式。
无论您在何处调用处理程序,都将当前 class 实例的 std::shared_ptr
实例传递给它。您可以通过 public 继承 enable_shared_from_this
来实现。这样,您的 mid
实例将保持活动状态,直到处理程序完成。
class mid : public std::enable_shared_from_this<mid>
{
mid()
{
inner_obj.register_callback<mid>(&mid::handle_sig_mid, shared_from_this());
}
//...
};
两件事:
当您绑定到
boost::ref(obj)
时,您使绑定表达式持有对函数参数的引用,该函数参数在register_callback
的出口处超出范围。 (参见 Does boost::bind() copy parameters by reference or by value?)只需绑定到指针本身,这使得绑定表达式持有指针本身的副本。
注册回调时注意生命周期问题很重要。通常,您必须在销毁信号槽中的任何绑定对象之前取消注册。
在你的例子中,这并没有真正发生,因为连接的槽都存在于成员对象中。这意味着插槽在外部对象消失之前被破坏。
但是,如果某些东西 copied/moved 出现故障。解决这个问题的常用模式是使用
scoped_connection
s.
让我分两步展示我的建议:
简化:尽早绑定,Signals2 为您进行类型擦除
不需要模板 register_callback
因为您使用 bind
和空信号槽立即擦除对象类型 T
。
所以,改为让它采用任意 nullary 并在调用者中进行绑定?事实上,更喜欢在调用者处使用 lambda。
template <class F> void register_callback(F&& f) {
sig_inner_.connect(std::forward<F>(f));
}
然后
mid() { inner_obj.register_callback([this] { handle_sig_mid(); }); }
生命周期和信号2:连接
不要使用重量级选项和随处使用动态分配的 enable_shared_from_this()
,而是使用库设施:http://www.boost.org/doc/libs/1_65_1/doc/html/boost/signals2/scoped_connection.html
Note In your example, using
shared_from_this()
is out of the question because it's not valid inside the constructor.
我的建议:
template <class F> boost::signals2::scoped_connection register_callback(F&& f) {
return sig_inner_.connect(std::forward<F>(f));
}
然后
mid() { _connection = inner_obj.register_callback([this] { handle_sig_mid(); }); }
让 _connection
成为会员:
boost::signals2::scoped_connection _connection;
这样,当包含的 class 被破坏时,槽会断开连接。
完整演示
#include <boost/bind.hpp>
#include <boost/signals2.hpp>
#include <iostream>
class inner {
public:
template <class F> boost::signals2::scoped_connection register_callback(F&& f) {
return sig_inner_.connect(std::forward<F>(f));
}
void trigger() {
std::cout << "inner" << std::endl;
sig_inner_();
}
private:
boost::signals2::signal<void()> sig_inner_;
};
class mid {
public:
mid() { _connection = inner_obj.register_callback([this] { handle_sig_mid(); }); }
template <class F> boost::signals2::scoped_connection register_callback(F&& f) {
return sig_mid_.connect(std::forward<F>(f));
}
void trigger() { sig_mid_(); }
void inner_trigger() { inner_obj.trigger(); }
void handle_sig_mid() {
std::cout << "mid" << std::endl;
trigger();
}
private:
boost::signals2::scoped_connection _connection;
boost::signals2::signal<void()> sig_mid_;
inner inner_obj;
};
class outer {
public:
outer() { _connection = mid_obj.register_callback([this] { handle_sig_outer(); }); }
void inner_trigger() { mid_obj.inner_trigger(); }
private:
boost::signals2::scoped_connection _connection;
mid mid_obj;
void handle_sig_outer() { std::cout << "outer" << std::endl; }
};
int main() {
outer outer_obj;
outer_obj.inner_trigger();
return 0;
}
版画
inner
mid
outer