我有一个创建简单音乐可视化动画的应用程序。这些动画由节点驱动,每个节点都有一堆参数,可以有以下几种类型之一: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())但我仍然可以承受动态类型内容的运行时开销。
答案 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! 安吉洛