我正在用C ++编写一个解释器,用于我简洁设计的类似lisp的语言。这是为了娱乐和学习,所以我不会追求绝对的效率。但我想尝试一个非常干净的C ++代码。我目前想知道如何实现内置函数。
基本上,我所做的就是:
我有一个抽象基类DataObject,它只提供类型信息(目前是double,int,bool),并由特定的数据容器继承,如:
class DataObject
{
public:
virtual const Type *type() = 0;
};
template<class T, const Type * myType>
class DataObjectValue : public DataObject
{
T value;
public:
const Type *type(){return myType;}
};
但是,当我想要表演时,我必须做以下事情:
DataObject * sum(DataObject *a, DataObject *b)
{
if(a->type() == &Integer and b->type == &Integer)
{
DataObjectValue<int>* ia = dynamic_cast< DataObjectValue<int>* >(a);
DataObjectValue<int>* ib = dynamic_cast< DataObjectValue<int>* >(b);
return new DataObjectValue<int>(ia->value+ib->value);
}
else if(a->type() == &Real and b->type == &Real)
{
DataObjectValue<double>* ra = dynamic_cast< DataObjectValue<double>* >(a);
DataObjectValue<double>* rb = dynamic_cast< DataObjectValue<double>* >(b);
return new DataObjectValue<double>(ra->value+rb->value);
}
else...
}
很快就会很烦人(对于 - * /&lt;&lt; =&lt; =&gt; =&lt;&gt; ....)以及其他几种类型。这很难维护。当然,通过在各处引入大量模板,我已经简化了尽可能多的过程,但是,我仍然不禁想到必须有一种更清洁的方式。你是a)看看我的问题是什么(我怀疑我的解释,而不是你的能力)b)有任何建议吗?
答案 0 :(得分:3)
你基本上执行类型擦除的当前实现正在存储的确切类型中,并且你是以一种稍微不同寻常的方式执行它(为什么不使用枚举而不是指向唯一对象的指针?)< / p>
我首先将 promotion 操作提供给基类,然后在每个级别实现它们:
enum DataType {
type_bool,
type_int,
type_double
};
struct DataObject {
virtual ~DataObject() {} // remember to provide a virtual destructor if you
// intend on deleting through base pointers!!!
virtual DataType type() const = 0;
virtual bool asBool() const = 0;
virtual int asInt() const = 0;
virtual double asDouble() const = 0;
};
然后,您可以在一个简单的仿函数中实现操作:
template <typename T>
T sum_impl( T lhs, T rhs ) {
return lhs + rhs;
}
并提供简单的调度功能:
DataType promoteTypes( DataType lhs, DataType rhs ) {
if ( lhs == type_double || rhs == type_double ) {
return type_double;
} else if ( lhs == type_int || rhs == type_int ) {
return type_int;
} else {
return type_bool;
}
}
template <template <typename T> T operation (T,T)>
DataObject* perform_operation( DataObject* lhs, DataObject* rhs, operation op ) const {
DataType result_type = promoteTypes( lhs->type(), rhs->type() );
switch ( result_type ) {
case type_double:
return new DataObjectValue<double>( op( lhs->asDouble(), rhs->asDouble() );
case type_int:
return new DataObjectValue<int>( op( lhs->asInt(), rhs->asInt() );
case type_bool:
return new DataObjectValue<bool>( op( lhs->asBool(), rhs->asBool() );
default:
abort();
}
}
有了所有部分,您可以通过为特定操作提供功能模板(如上面的sum
),然后使用其余的地方来实现操作:
// sum_impl as above
DataObject* sum( DataObject* lhs, DataObject* rhs ) {
return perform_operation( lhs, rhs, sum_impl );
}
现在这只是我将使用的模式,但我会做一些更改,我更喜欢使用尽可能少的指针,这意味着我不会通过指针而是通过引用传递参数。此外,我会进行正确的类型擦除(看看boost any)并使Object
成为包含DataObject
元素的完整类型,然后对该类型执行操作(而不是层次结构)。这将使您能够提供也按值返回的函数(并在内部隐藏动态内存分配,这也意味着可以在Object
内控制资源管理,而不是用户代码的责任)。通过这些更改,您可以重复使用并简化上述结构,并提供更清晰的解决方案。
答案 1 :(得分:1)
您可以使用宏程序:
(为了简洁,我离开了\
#define IMPLEMENT_OPERATOR(name, operator)
DataObject * name(DataObject *a, DataObject *b)
{
if(a->type() == &Integer and b->type == &Integer)
{
DataObjectValue<int>* ia = dynamic_cast< DataObjectValue<int>* >(a);
DataObjectValue<int>* ib = dynamic_cast< DataObjectValue<int>* >(b);
return new DataObjectValue<int>(ia->value operator ib->value);
}
else if(a->type() == &Real and b->type == &Real)
{
DataObjectValue<double>* ra = dynamic_cast< DataObjectValue<double>* >(a);
DataObjectValue<double>* rb = dynamic_cast< DataObjectValue<double>* >(b);
return new DataObjectValue<double>(ra->value operator rb->value);
}
else...
}
现在,在此之后你可以这样做:
IMPLEMENT_OPERATOR(sum, +);
IMPLEMENT_OPERATOR(multiply, *);
IMPLEMENT_OPERATOR(division, /);
...
这将是快速和可维护的,但对于特殊操作员可能会有问题。
编辑: Shahbaz提到您可以在其他宏中使用宏。这可以通过以下方式应用。
(再次,为了简洁而遗漏了\
)
#define IMPLEMENT_OPERATOR_TYPE(typeobject, internal_type, operator)
if(a->type() == &typeobject and b->type == &typeobject)
{
DataObjectValue<internal_type>* ia = dynamic_cast< DataObjectValue<internal_type>* >(a);
DataObjectValue<internal_type>* ib = dynamic_cast< DataObjectValue<internal_type>* >(b);
return new DataObjectValue<internal_type>(ia->value operator ib->value);
}
现在可以在IMPLEMENT_OPERATOR
宏中使用:
#define IMPLEMENT_OPERATOR(name, operator)
DataObject * name(DataObject *a, DataObject *b)
{
IMPLEMENT_OPERATOR_TYPE(&Integer, int, operator);
IMPLEMENT_OPERATOR_TYPE(&Real, double, operator);
...
}
答案 2 :(得分:1)
如果两个操作数必须是同一类型,那么你可以试试这个:
class DataObject
{
public:
virtual const Type *type() = 0;
virtual DataObject *operator+(DataObject&) = 0;
virtual DataObject *operator-(DataObject&) = 0;
virtual DataObject *operator*(DataObject&) = 0;
virtual DataObject *operator/(DataObject&) = 0;
};
template<class T, const Type * myType>
class DataObjectValue : public DataObject
{
typedef DataObjectValue<T, myType> selfType;
T value;
public:
const Type *type(){return myType;}
DataObject *operator+(DataObject& other) {
if (other.type() != myType)
return null;
selfType &otherValue = static_cast<selfType&>(other);
return new selfType(value + otherValue.value);
}
// etc.
};
当你开始允许int + double时,事情变得更加激烈。在某些情况下,你可以通过检查你知道的所有类型(即已经被声明)来处理它,否则将自己传递给另一个对象:
if (other.type() != myType)
return other + *this; // assume *other is a <double> that knows how to add an <int>