如何在C ++中实现一般类型的成员对象?

时间:2014-08-26 18:23:18

标签: c++ qt templates closures

我有一个创建简单音乐可视化动画的应用程序。这些动画由节点驱动,每个节点都有一堆参数,可以有以下几种类型之一:int,float,color等。参数既可以有用户设置值,也可以连接到输出另一个节点。

我目前正在使用模板类型以及std::function<>,如下所示:

#include <functional>

template <class PT>
class Param
{
public:
    Param(PT value=PT()) : _value(value), _provider(nullptr) {}
    void    setValue(const PT &value) {_value = value;}
    void    setProvider(std::function<void(PT&)> provider) {_provider = provider;}

    void    getCurrentValue(PT &value)  {
        // update current member value
        if (_provider)
            _provider(_value);
        value = _value;
    }

private:
    PT                             _value;
    std::function<void(PT &value)> _provider;
};

然后我为这样的动画节点实例化参数:

class AnimationNode
{
public:
    AnimationNode(Model *model = nullptr);
    void evaluate();

private:
    Param<int>        _xoffset;
    Param<int>        _yoffset;
    Param<float>      _scale;
    Param<ColorType>  _color;
};

这些参数可以连接到驱动程序节点,例如:

class SublevelMeter {    
public:
    SublevelMeter();
    void setRange(Subrange &_range);
    ...
    std::function<void(float&)> createProviderClosure();

private:
    float _level;
    ...
}

std::function<void(float&)> SublevelMeter::createProviderClosure() {
    return [this] (float &out) {out = _level;};
}

通过执行以下操作将一个节点连接到另一个节点:

AnimationNode::connectScaleToSublevel(SublevelMeter *slm) {
    _scale->setProvider(slm->createProviderClosure());
}

问题是,我希望有一个抽象的Param类型,我可以传递给对象,所以我可以将一个参数传递给我的SublevelMeter,而不是上面的代码:

SublevelMeter::connectToParam(Param *param) {
    param->setProvider(slm->createProviderClosure());
} 

这在编写创建我的GUI编辑器小部件的例程时也会有所帮助:编辑器可以通过内省Param来找出正确的类型。 但我不确定如何从模板化的类中做到这一点,也不知道如何在C ++中实现内省的最佳方法。 (我是从python设计背景来的,这或许鼓励我以pythonic而不是C ++的方式思考它;如果有更好的方法来解决这个问题,我会爱上听到它!)

我使用的是Qt,所以我考虑过使用QVariant或其他Qt Meta-Object的东西,但我不确定如何使用它,或者它是否适合。 (我没有使用Boost,虽然我知道它有某些类型的擦除设施,但我担心趟过这些水域......)

我对最干净/最好的&#34;感兴趣?这样做的方式。虽然效率是一个考虑因素(动画播放时每帧会多次调用getCurrentValue())但我仍然可以承受动态类型内容的运行时开销。

2 个答案:

答案 0 :(得分:0)

至少问题的第一部分是可解决的,没有抽象Param

class SublevelMeter {
    ...

    template<class PT>
    void connectToParam(Param<PT> *param) {
        param->setProvider(createProviderClosure<PT>());
    }

    // specialize this for different PTs
    template<class PT>
    std::function<void(PT&)> createProviderClosure();
}

如果您确实需要操纵Param - s的动态列表,并且您不想使用任何类型的RTTI,请考虑使用Visitor pattern

class Visitor;

class ParamBase
{
public:
    virtual ~ParamBase() = default;
    virtual void acceptVisitor(Visitor* v) = 0;
};

template <class PT>
class Param : public ParamBase
{
public:
    ...
    void acceptVisitor(Visitor* v) override;
};

class Visitor {
public:
    virtual ~Visitor() = default;

    void visit(ParamBase* p) {
        p->acceptVisitor(this);
    }

    virtual void visitParam(Param<float>* p) = 0;
    // add more functions for other Params
};  


class PrintVisitor : public Visitor {
public:
    void visitParam(Param<float>* p) override {
        std::cout << "visited Param<float>, value = " << p->getValue() << std::endl;
    }
};

template<class PT>
void Param<PT>::acceptVisitor(Visitor* v) {
    v->visitParam(this);
}

int main() {
    std::unique_ptr<ParamBase> p(new Param<float>(123.4f));
    std::unique_ptr<Visitor> v(new PrintVisitor());
    v->visit(p.get());
    return 0;
}

答案 1 :(得分:0)

我为您实现了泛型类型管理的简单类。此类是在不使用模板的情况下实现的,因此您可以声明变量并直接在运行时分配值和类型。这个实现非常简单,您应该将它作为参考来开发自己的解决方案。在以下示例中,我仅实现了对3种类型的支持:int,double和char *(C string)。 main函数显示了对LVALUE和RVALUE赋值使用泛型类:

#include <stdio.h>
#include <stdlib.h>

enum Types {tInteger, tDouble, tString};

class TGenericType
{

private:
    char m_Value[100];
    Types m_Type;

protected:

public:

    void operator=(int AValue)
    {
        m_Type = tInteger;
        sprintf(m_Value, "%d", AValue);
    }

    operator int()
    {
        // try to convert the m_Value in integer
        return atoi(m_Value); // the result depend by atoi() function
    }

    void operator=(double AValue)
    {
        m_Type = tDouble;
        sprintf(m_Value, "%f", AValue);
    }

    operator double()
    {
        // try to convert the m_Value in double
        return atof(m_Value);  // the result depends by atof() function
    }

    void operator=(char* AValue)
    {
       m_Type = tString;
       strcpy(m_Value, AValue);
    }

    operator char*()
    {
        return m_Value;
    }


};

 int _tmain(int argc, _TCHAR* argv[])
{
    TGenericType LVar;

    // int assignment LVar used as LVALUE
    LVar = 10;

    // int assignment LVar used as RVALUE
    int i = LVar;

    // Double assignment LVar used as LValue
    LVar = 10.1;

    // double assignment LVar used as RVALUE
    double d = LVar;

    // costant string assignment LVar used as RVALUE
    LVar = "Ciao Mondo";

    // string copying LVar used as const string RVALUE
    char Buffer[100];
    strcpy(Buffer, LVar);

    return 0;
}

我在c ++ builder 32bit和c ++ builder(CLang)64bit上测试了上面的代码 如果我的解决方案回答了您的问题,请将其检查为已解答。

来自意大利的Ciao! 安吉洛