我有一个由许多链接对象组成的应用程序,每个链接对象都有为了运行而需要的参数。我正在使用 context 模式,以便每个对象根据构造时给出的上下文对象引用设置自己的参数。这适用于下面给出的简化代码示例。
我想要添加的下一个功能是 observer 模式,这样当上下文对象中的参数发生更改时,会通知每个订阅者对象并相应地更新其参数。但是,我在编写我需要使用的语法以便将信号连接到插槽时遇到了麻烦。
理想情况下,我希望订阅者对象在构造时使用参数类注册自己,这样他们就可以在对象的生命周期内更新自己(即他们必须在销毁时注销自己,或者更好的是,使用某种类型RAII技术如果超出范围则撤销注册。下面是一个代码示例,我希望尽可能简单。
目前输出
处理单元0 param = 1
处理单元1 param = 2
处理单元0 param = 1
处理单元1 param = 2
目标是让它成为......
处理单元0 param = 1
处理单元1 param = 2
处理单元0 param = 11
处理单元1 param = 22
我目前正在使用的代码如下。如果你想尝试它,它的编译就好了。请建议我需要进行的更改,以便将信号连接到插槽。随意指出任何可能导致故障的设计问题。提前谢谢。
//
// main.cpp
// context_observer
//
#include <iostream>
#include <sstream>
#include <map>
#include "boost/signals2.hpp"
/* This class holds the parameters and is the class that I want to set
* observable. It holds a std::map of the parameters and a simple accessor*/
class cParamsContext
{
typedef std::map<std::string, float> myMap_t;
typedef boost::signals2::signal<void (cParamsContext&)> signal_t;
myMap_t paramMap;
public:
signal_t sig;
cParamsContext()
{
paramMap["a0"] = 1.f;
paramMap["a1"] = 2.f;
}
float getParam( std::string key, float const & defaultVal )
{
myMap_t::iterator it = paramMap.find(key);
if ( it == paramMap.end() )
return defaultVal;
else
return it->second;
}
void changePars()
{
paramMap["a0"] = 11.f;
paramMap["a1"] = 22.f;
sig(*this);
}
};
/* This is an example of a processing class that I would like to have
* subscribe to the parameter class.*/
class cProcessingUnit
{
float parameter;
int id;
public:
cProcessingUnit(cParamsContext &contextObj, int id_) : parameter (80.f), id(id_)
{
updatePars(contextObj);
// Something like contextObj.sig.connect ... here
}
void updatePars(cParamsContext &contextObj)
{
std::stringstream idStream;
idStream << id;
std::string key = std::string( "a" + idStream.str() );
parameter = contextObj.getParam( key, parameter );
}
float getParam() {return parameter;}
};
/* This is a very simple main function used here for testing. It
* instantiates 2 processing objects. The parameters are then changed
* in the observable parameter object. The processing objects should
* then update themselves and so the last "cout" calls should reveal the
* new parameters. At least, this is what I would like to happen!*/
int main(int argc, char *argv[])
{
cParamsContext contextObj;
cProcessingUnit temp0(contextObj, 0);
cProcessingUnit temp1(contextObj, 1);
std::cout << "Processing unit " << 0 << " param = " << temp0.getParam() << std::endl;
std::cout << "Processing unit " << 1 << " param = " << temp1.getParam() << std::endl;
contextObj.changePars();
std::cout << "Processing unit " << 0 << " param = " << temp0.getParam() << std::endl;
std::cout << "Processing unit " << 1 << " param = " << temp1.getParam() << std::endl;
}
答案 0 :(得分:3)
假设您希望在信号触发时调用updatePars,请将您评论的位置连接到此处:
// Something like contextObj.sig.connect ... here
contextObj.sig.connect(boost::bind(&cProcessingUnit::updatePars, this, _1));
您可能需要保留已返回的连接,以便在销毁时断开连接。你的问题措辞不是很好,但很难说这是否是你真正需要的。
基本上,要连接到一个信号,你需要传递一个&#34;可调用的实体&#34;与信号的签名相匹配。这可以是一个绑定器(绑定和其他一些结果),一个自由函数,一个lambda表达式......一个自定义函子......等等......
我最新的博客文章可能会有一些帮助,但它相当快速而且表面。
答案 1 :(得分:2)
对于任何人来说,使用作用域的方法是将上面cProcessingUnit
类的第一部分更改为以下内容。 。 。
class cProcessingUnit
{
float parameter;
int id;
boost::signals2::scoped_connection c;
public:
cProcessingUnit(cParamsContext &contextObj, int id_) : parameter (80.f), id(id_)
{
updatePars(contextObj);
c = contextObj.sig.connect( boost::bind(&cProcessingUnit::updatePars, this, _1) );
}
非常感谢@Crazy Eddie推动我走向正确的方向