什么';这是在用C+编写的解释器中处理内置函数的好方法+;? 我正在用C++编写一个解释,就像我粗略设计的LISP语言一样。这是为了好玩和学习,所以我不追求绝对的效率。但是我正在尝试一个非常干净的C++代码。我现在想知道如何实现内置函数
基本上,我所做的是: 我有一个抽象基类DataObject,它只提供类型信息(当前为double、int、bool),并由特定的数据容器继承,如:什么';这是在用C+编写的解释器中处理内置函数的好方法+;? 我正在用C++编写一个解释,就像我粗略设计的LISP语言一样。这是为了好玩和学习,所以我不追求绝对的效率。但是我正在尝试一个非常干净的C++代码。我现在想知道如何实现内置函数,c++,interpreter,C++,Interpreter,基本上,我所做的是: 我有一个抽象基类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:
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;}
};
类数据对象
{
公众:
虚拟常量类型*Type()=0;
};
模板
类DataObjectValue:公共DataObject
{
T值;
公众:
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...
}
DataObject*sum(DataObject*a,DataObject*b)
{
如果(a->type()==&Integer和b->type==&Integer)
{
DataObjectValue*ia=动态广播(a);
DataObjectValue*ib=动态广播(b);
返回新的DataObjectValue(ia->value+ib->value);
}
否则如果(a->type()==&Real和b->type==&Real)
{
DataObjectValue*ra=动态广播(a);
DataObjectValue*rb=动态广播(b);
返回新的DataObjectValue(ra->value+rb->value);
}
其他的
}
这很快就会让人恼火(对-*/<=>..和其他几种类型都这样做)。这很难维持。当然,通过到处引入大量模板,我已经尽可能地简化了流程,但我还是忍不住认为一定有更干净的方法。你a)明白我的问题是什么(我怀疑我的解释,而不是你的能力)b)有什么建议吗?你可以使用宏方法: (为了简洁起见,我留下了
\
)
这将是快速和可维护的,但对于特殊操作员来说可能有问题
EDIT:Shahbaz提到可以在其他宏中使用宏。这可以通过以下方式应用
(为了简洁起见,\
s再次省略)
如果两个操作数必须是同一类型,则可以尝试以下操作:
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.
};
类数据对象
{
公众:
虚拟常量类型*Type()=0;
虚拟数据对象*运算符+(数据对象&)=0;
虚拟数据对象*运算符-(数据对象&)=0;
虚拟数据对象*运算符*(数据对象&)=0;
虚拟数据对象*运算符/(数据对象&)=0;
};
模板
类DataObjectValue:公共DataObject
{
typedef DataObjectValue selfType;
T值;
公众:
const Type*Type(){return myType;}
DataObject*运算符+(DataObject和其他){
if(other.type()!=myType)
返回null;
selfType和otherValue=静态类型转换(其他);
返回新的selfType(value+otherValue.value);
}
//等等。
};
当你开始允许int+double的时候,事情会变得更复杂。在某些情况下,您可以通过检查您知道的所有类型(即已声明的类型)来处理此问题,否则将自己传递给另一个对象:
if (other.type() != myType)
return other + *this; // assume *other is a <double> that knows how to add an <int>
if(other.type()!=myType)
返回其他+*此;//假设*另一个是知道如何添加
您当前的实现基本上是以存储的确切类型执行类型擦除,并且您是以一种稍微不寻常的方式执行的(为什么不使用枚举而不是指向唯一对象的指针?)
我将首先向基类提供升级操作,并在每个级别实施这些操作:
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;
}
现在这正是我要使用的模式,但我要做一些更改,我更喜欢使用尽可能少的指针,这意味着我不会通过指针传递参数,而是通过引用传递参数。另外,我会进行适当的类型擦除(看看boost any),并将
对象
制作成包含数据对象
元素的完整类型,然后对该类型(而不是层次结构)执行操作。这将使您能够提供也按值返回的函数(并在内部隐藏动态内存分配,这也意味着资源管理可以在对象
内部控制,而不是用户代码的责任)。通过这些更改,您可以重用和简化上面的结构,并提供更干净的解决方案。如何将DataObjectValue
a联合起来?这样,您可以检查a.typy()==b.type(),然后使用aswtich
。它不会提高性能(也就是说,它基本上是相同的算法),但是你会有更干净的代码。我恐怕无法避免这种情况。你可以把它们放在另一个你写过一次就再也不看的文件里,这样就不会把你更重要的代码弄得乱七八糟。@Shahbaz实际上一个开关
可以变成一个跳转表,但我认为一系列if
/else if
是不可能的。我曾经用过一个策略来制作如此冗长和重复的代码可维护的是编写一个更小、更简单的Python脚本来生成C++代码。这不是这里的重点,所以我不想把事情复杂化。宏来拯救!对于每种情况,您甚至可以在宏中使用宏。很好的一点是,操作符中的每种类型也可以用宏声明。我非常喜欢这一种,它更接近(尽管更优雅)我实际代码的非精简版本。谢谢你给我提供了很多有趣的话题,我会更深入地了解这一点!不过,我并不太担心指针,因为这些类是从一个我已经实现了ref计数的类派生出来的(因为表达式树往往很难手动管理)。这取决于您的语言如何发展(例如,如果是这样的话)
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.
};
if (other.type() != myType)
return other + *this; // assume *other is a <double> that knows how to add an <int>
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_impl as above
DataObject* sum( DataObject* lhs, DataObject* rhs ) {
return perform_operation( lhs, rhs, sum_impl );
}