2

我有一个由许多链接对象组成的应用程序,每个对象都有它们运行所需的参数。我正在使用上下文模式,以便每个对象根据构造时给出的上下文对象引用设置自己的参数。这在下面给出的简化代码示例中效果很好。

我尝试添加的下一个特性是观察者模式,这样当上下文对象中的参数发生变化时,每个订阅者对象都会收到通知并相应地更新其参数。但是,我无法确定将信号连接到插槽所需使用的语法。

理想情况下,我希望订阅者对象在构造时使用参数类注册自己,以便他们可以在对象的生命周期内更新自己(即他们必须在销毁时注销自己,或者更好的是,使用某种 RAII 技术如果超出范围,则注销自己)。下面是一个代码示例,我希望它尽可能简单。

目前的输出是

处理单元 0 参数 = 1

处理单元 1 参数 = 2

处理单元 0 参数 = 1

处理单元 1 参数 = 2

目标是让它成为...

处理单元 0 参数 = 1

处理单元 1 参数 = 2

处理单元 0 参数 = 11

处理单元 1 参数 = 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;    
}
4

2 回答 2

3

假设您希望在信号触发时调用 updatePars ,您将连接到您对此发表评论的位置:

// Something like contextObj.sig.connect ... here
contextObj.sig.connect(boost::bind(&cProcessingUnit::updatePars, this, _1));

您可能需要保留返回的连接,以便在您被破坏时断开连接。您的问题的措辞不是很好,所以很难说这是否是您真正需要的。

基本上,要连接到信号,您需要向其传递与信号签名匹配的“可调用实体”。这可以是一个 binder(bind 和其他一些函数的结果)、一个自由函数、一个 lambda 表达式......一个自定义仿函数......等等......

我最新的博客条目可能会有所帮助,但它相当快速和表面。

于 2012-05-02T19:07:09.713 回答
2

对于任何人来说,使用 scoped 执行此操作的方法是将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 将我推向正确的方向

于 2012-05-02T20:11:34.393 回答