在用C++编写的解释器中处理内置函数的好方法是什么?

djf*_*jfm 5 c++ interpreter

我正在用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;}
};
Run Code Online (Sandbox Code Playgroud)

但是,当我想要表演时,我必须做以下事情:

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...
}
Run Code Online (Sandbox Code Playgroud)

这很快就会很烦人(对于 - */<< => = <> ....)以及其他几种类型.这很难维护.当然,通过在各处引入大量模板,我已经简化了尽可能多的过程,但是,我仍然不禁想到必须有一种更清洁的方式.你是a)看看我的问题是什么(我怀疑我的解释,而不是你的能力)b)有什么建议吗?

Dav*_*eas 3

您当前的实现基本上以正在存储的确切类型执行类型擦除,并且您以一种稍微不寻常的方式执行此操作(为什么不使用枚举而不是指向唯一对象的指针?)

我首先向基类提供促销操作,并在每个级别中实施它们:

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;
};
Run Code Online (Sandbox Code Playgroud)

然后你可以在一个简单的函子中实现这些操作:

template <typename T>
T sum_impl( T lhs, T rhs ) {
   return lhs + rhs;
}
Run Code Online (Sandbox Code Playgroud)

并提供一个简单的调度函数:

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();
   }
}
Run Code Online (Sandbox Code Playgroud)

所有的部分都就位后,您可以几乎简单地实现操作,只需为特定操作提供一个函数模板(如上所述sum),然后使用其余的地方:

// sum_impl as above
DataObject* sum( DataObject* lhs, DataObject* rhs ) {
   return perform_operation( lhs, rhs, sum_impl );
}
Run Code Online (Sandbox Code Playgroud)

现在这只是我要使用的模式,但我会做一些更改,我更喜欢使用尽可能少的指针,这意味着我不会通过指针传递参数,而是通过引用传递参数。另外,我会进行适当的类型擦除(看看 boost any)并创建Object一个包含元素的完整类型DataObject,然后对该类型(而不是层次结构)执行操作。这将使您能够提供也按值返回的函数(并在内部隐藏动态内存分配,这也意味着资源管理可以在内部控制,Object而不是用户代码的责任)。通过这些更改,您可以重用和简化上述结构并提供更清晰的解决方案。