基本上我只想使用任意类型的给定参数进行任意操作。
参数类型基类是Var,而Operation是将为给定参数执行的操作的基类。
我有Evaluator类,它包含使用opId映射的运算符集合。 Evaluator将根据evaluate()成员函数中给出的opId参数进行操作,然后evaluate()函数将搜索将接受参数类型和opId的受支持运算符。
我想问的是,是否有有效模式或算法可以执行此操作没有dynamic_cast<>和/或循环运算符集。
`
class Var {
public:
bool isValidVar();
static Var invalidVar();
}
template<typename T> class VarT : public Var {
public:
virtual const T getValue() const;
}
class Operator {
public:
virtual Var evaluate(const Var& a, const Var& b) = 0;
}
template<typename T> class AddOperator : public Operator {
public:
virtual Var evaluate(const Var& a, const Var& b)
{ //dynamic_cast is slow!
const VarT<T>* varA = dynamic_cast<const VarT<T>*>(&a);
const VarT<T>* varB = dynamic_cast<const VarT<T>*>(&b);
if(varA && varB) //operation supported
{
return VarT<T>(varA->getValue() + varA->getValue());
}
return Var::invalidVar(); //operation for this type is not supported
}
}
class Evaluator {
private:
std::map<int,std::vector<Operator>> operatorMap;
public:
virtual Var evaluate(const Var& a, const Var& b,int opId)
{
std::map<int,std::vector<Operator>>::iterator it = this->operatorMap.find(opId);
if(it != this->operatorMap.end())
{
for(size_t i=0 ; i<it->second.size() ; i++)
{
Var result = it->second.at(i).evaluate(a,b);
if(result.isValidVar())
{
return result;
}
}
}
//no operator mapped, or no operator support the type
return Var::invalidVar();
}
}
`
答案 0 :(得分:2)
如果您不想使用dynamic_cast,请考虑在设计中添加类型特征。
已添加05/03/10:以下示例将演示运行时特征的工作原理
CommonHeader.h
#ifndef GENERIC_HEADER_INCLUDED
#define GENERIC_HEADER_INCLUDED
#include <map>
#include <vector>
#include <iostream>
// Default template
template <class T>
struct type_traits
{
static const int typeId = 0;
static const int getId() { return typeId; }
};
class Var
{
public:
virtual ~Var() {}
virtual int getType() const = 0;
virtual void print() const = 0;
};
template<typename T>
class VarT : public Var
{
T value;
public:
VarT(const T& v): value(v) {}
virtual int getType() const { return type_traits<T>::getId(); };
virtual void print() const { std::cout << value << std::endl; };
const T& getValue() const { return value; }
};
class Operator
{
public:
virtual ~Operator() {}
virtual Var* evaluate(const Var& a, const Var& b) const = 0;
};
template<typename T>
class AddOperator : public Operator
{
public:
virtual Var* evaluate(const Var& a, const Var& b) const
{
// Very basic condition guarding
// Allow operation within similar type only
// else have to create additional compatibility checker
// ie. AddOperator<Matrix> for Matrix & int
// it will also requires complicated value retrieving mechanism
// as static_cast no longer can be used due to unknown type.
if ( (a.getType() == b.getType()) &&
(a.getType() == type_traits<T>::getId()) &&
(b.getType() != type_traits<void>::getId()) )
{
const VarT<T>* varA = static_cast<const VarT<T>*>(&a);
const VarT<T>* varB = static_cast<const VarT<T>*>(&b);
return new VarT<T>(varA->getValue() + varB->getValue());
}
return 0;
}
};
class Evaluator {
private:
std::map<int, std::vector<Operator*>> operatorMap;
public:
void registerOperator(Operator* pOperator, int iCategory)
{
operatorMap[iCategory].push_back( pOperator );
}
virtual Var* evaluate(const Var& a, const Var& b, int opId)
{
Var* pResult = 0;
std::vector<Operator*>& opList = operatorMap.find(opId)->second;
for ( std::vector<Operator*>::const_iterator opIter = opList.begin();
opIter != opList.end();
opIter++ )
{
pResult = (*opIter)->evaluate( a, b );
if (pResult)
break;
}
return pResult;
}
};
#endif
DataProvider标题
#ifdef OBJECTA_EXPORTS
#define OBJECTA_API __declspec(dllexport)
#else
#define OBJECTA_API __declspec(dllimport)
#endif
// This is the "common" header
#include "CommonHeader.h"
class CFraction
{
public:
CFraction(void);
CFraction(int iNum, int iDenom);
CFraction(const CFraction& src);
int m_iNum;
int m_iDenom;
};
extern "C" OBJECTA_API Operator* createOperator();
extern "C" OBJECTA_API Var* createVar();
DataProvider实现
#include "Fraction.h"
// user-type specialization
template<>
struct type_traits<CFraction>
{
static const int typeId = 10;
static const int getId() { return typeId; }
};
std::ostream& operator<<(std::ostream& os, const CFraction& data)
{
return os << "Numerator : " << data.m_iNum << " @ Denominator : " << data.m_iDenom << std::endl;
}
CFraction operator+(const CFraction& lhs, const CFraction& rhs)
{
CFraction obj;
obj.m_iNum = (lhs.m_iNum * rhs.m_iDenom) + (rhs.m_iNum * lhs.m_iDenom);
obj.m_iDenom = lhs.m_iDenom * rhs.m_iDenom;
return obj;
}
OBJECTA_API Operator* createOperator(void)
{
return new AddOperator<CFraction>;
}
OBJECTA_API Var* createVar(void)
{
return new VarT<CFraction>( CFraction(1,4) );
}
CFraction::CFraction() :
m_iNum (0),
m_iDenom (0)
{
}
CFraction::CFraction(int iNum, int iDenom) :
m_iNum (iNum),
m_iDenom (iDenom)
{
}
CFraction::CFraction(const CFraction& src) :
m_iNum (src.m_iNum),
m_iDenom (src.m_iDenom)
{
}
DataConsumer
#include "CommonHeader.h"
#include "windows.h"
// user-type specialization
template<>
struct type_traits<int>
{
static const int typeId = 1;
static const int getId() { return typeId; }
};
int main()
{
Evaluator e;
HMODULE hModuleA = LoadLibrary( "ObjectA.dll" );
if (hModuleA)
{
FARPROC pnProcOp = GetProcAddress(hModuleA, "createOperator");
FARPROC pnProcVar = GetProcAddress(hModuleA, "createVar");
// Prepare function pointer
typedef Operator* (*FACTORYOP)();
typedef Var* (*FACTORYVAR)();
FACTORYOP fnCreateOp = reinterpret_cast<FACTORYOP>(pnProcOp);
FACTORYVAR fnCreateVar = reinterpret_cast<FACTORYVAR>(pnProcVar);
// Create object
Operator* pOp = fnCreateOp();
Var* pVar = fnCreateVar();
AddOperator<int> intOp;
AddOperator<double> doubleOp;
e.registerOperator( &intOp, 0 );
e.registerOperator( &doubleOp, 0 );
e.registerOperator( pOp, 0 );
VarT<int> i1(10);
VarT<double> d1(2.5);
VarT<float> f1(1.0f);
std::cout << "Int Obj id : " << i1.getType() << std::endl;
std::cout << "Double Obj id : " << d1.getType() << std::endl;
std::cout << "Float Obj id : " << f1.getType() << std::endl;
std::cout << "Import Obj id : " << pVar->getType() << std::endl;
Var* i_result = e.evaluate(i1, i1, 0); // result = 20
Var* d_result = e.evaluate(d1, d1, 0); // no result
Var* f_result = e.evaluate(f1, f1, 0); // no result
Var* obj_result = e.evaluate(*pVar, *pVar, 0); // result depend on data provider
Var* mixed_result1 = e.evaluate(f1, d1, 0); // no result
Var* mixed_result2 = e.evaluate(*pVar, i1, 0); // no result
obj_result->print();
FreeLibrary( hModuleA );
}
return 0;
}
答案 1 :(得分:1)
如果您可以修改类型Var
,则可以为参数类型添加type-Ids。但是在执行操作时,您总是必须在某个时候使用dynamic_cast
。如果您的类型和操作在编译时得到修复,您可以使用Boost.MPL(特别是容器)使用模板完成所有操作。
答案 2 :(得分:1)
您的示例代码包含许多错误,包括切片问题。
我不是百分百肯定,但我似乎记得你可以使用const type_info*
作为地图的关键。
如果是这样,您可以使用以下内容。它不是免费的RTTI(type_info
),但由于Evaluator已经检查了typeids,您可以使用static_cast
代替dynamic_cast
(但现在并不重要代码不会盲目地搜索适合的运算符)。
当然,在内存管理方面,以下内容完全被打破。使用您选择的智能指针重新实现。
#include <map>
#include <typeinfo>
#include <cassert>
#include <iostream>
struct CompareTypeinfo
{
bool operator()(const std::type_info* a, const std::type_info* b) const
{
return a->before(*b);
}
};
class Var {
public:
virtual ~Var() {}
virtual const std::type_info& getType() const = 0;
virtual void print() const = 0;
};
template<typename T> class VarT : public Var {
T value;
public:
VarT(const T& v): value(v) {}
const T& getValue() const { return value; }
virtual const std::type_info& getType() const { return typeid(T); }
virtual void print() const { std::cout << value << '\n'; }
};
class Operator {
public:
virtual ~Operator() {}
virtual Var* evaluate(const Var& a, const Var& b) const = 0;
virtual const std::type_info& getType() const = 0;
};
template<typename T> class AddOperator : public Operator {
public:
typedef T type;
virtual const std::type_info& getType() const { return typeid(T); }
virtual Var* evaluate(const Var& a, const Var& b) const
{
//it is the responsibility of Evaluator to make sure that the types match the operator
const VarT<T>* varA = static_cast<const VarT<T>*>(&a);
const VarT<T>* varB = static_cast<const VarT<T>*>(&b);
return new VarT<T>(varA->getValue() + varB->getValue());
}
};
class Evaluator {
private:
typedef std::map<const std::type_info*, Operator*, CompareTypeinfo> TypedOpMap;
typedef std::map<int, TypedOpMap> OpMap;
OpMap operatorMap;
public:
template <class Op>
void registerOperator(int opId)
{
operatorMap[opId].insert(std::make_pair(&typeid(typename Op::type), new Op));
}
Var* evaluate(const Var& a, const Var& b,int opId)
{
OpMap::const_iterator op = operatorMap.find(opId);
if (op != operatorMap.end() && a.getType() == b.getType()) {
TypedOpMap::const_iterator typed_op = op->second.find(&a.getType());
if (typed_op != op->second.end()) {
//double-checked
assert(typed_op->second->getType() == a.getType());
return typed_op->second->evaluate(a, b);
}
}
return 0;
}
};
int main()
{
Evaluator e;
e.registerOperator<AddOperator<int> >(0);
e.registerOperator<AddOperator<double> >(0);
VarT<int> i1(10), i2(20);
VarT<double> d1(2.5), d2(1.5);
VarT<float> f1(1.0), f2(2.0);
Var* i_result = e.evaluate(i1, i2, 0);
Var* d_result = e.evaluate(d1, d2, 0);
Var* f_result = e.evaluate(f1, f2, 0);
Var* mixed_result = e.evaluate(i1, d2, 0);
assert(i_result != 0);
assert(d_result != 0);
assert(f_result == 0); //addition not defined for floats in Evaluator
assert(mixed_result == 0); //and never for mixed types
i_result->print(); //30
d_result->print(); //4.0
}