根据C ++中的特定类型执行代码

时间:2012-12-29 16:28:08

标签: c++ templates generics boost

我正在尝试将我之前用函数式语言(OCaml)编写的一些代码重写为C ++。

我的问题可以缩短为:

  • 我有一堆价值
  • 值可以是变体类型(因此可以是一组不同类型的值,例如intfloatstd::stringstd::list等等。
  • 我想定义处理值的运算符(例如,弹出两个值并推送它们之和的加法运算)
  • 根据堆栈中找到的类型,某些运算符的行为会有所不同,理想情况下,某些运算符甚至会根据类型更改参数的数量(一个简单示例:加法运算符可以弹出一个值,如果它是{{1}然后在列表的所有值之间推送应用运算符,否则弹出另一个值,如果它们都浮动则执行加法)

到目前为止,我已经能够通过使用模板使其工作。

std::list

然后通过执行

来解释运算符
class Value
{
  public:
    Value(Type type) : type(type) { }

    virtual string svalue() const = 0;
    virtual string lvalue();

    virtual bool equals(Value *value) const = 0;
    virtual Value* clone() const = 0;

    const Type type;

    virtual ~Value() { };
}

template <class T>
class TValue : public Value
{
  protected:
    T value;

  public:
    TValue(Type type, T value) : Value(type), value(value) {}

    void set(T value) { this->value = value; }
    T get() const { return this->value; }
};

class Int : public TValue<int>
{
  private:

  public:  
    Int(int value) : TValue<int>(TYPE_INT, value) { };

    virtual string svalue() const;

    virtual bool equals(Value *value) const { return this->value == ((TValue<int>*)value)->get(); }
    virtual Value *clone() const { return new Int(value); }
};

现在,这可行,但我不喜欢这种方法,因为它听起来很笨拙,它需要很多类型转换,并且它根本不优雅(与我的功能实现相比)。我开始研究 boost 库,看看我是否能找到更好的方法来管理一切,在开始之前我试图定义一种不同的方法来定义运算符,如

Value *v1, *v2,
case OP_PLUS:
{
  if (vm->popTwo(&v1, &v2))
  {  
    switch (v1->type << 4 | v2->type)
    {
      case TYPES(TYPE_INT, TYPE_INT): vm->push(new Int(((Int*)v1)->get() + ((Int*)v2)->get())); break;
      case TYPES(TYPE_FLOAT, TYPE_INT): vm->push(new Float(((Float*)v1)->get() + ((Int*)v2)->get())); break;
      case TYPES(TYPE_INT, TYPE_FLOAT): vm->push(new Float(((Int*)v1)->get() + ((Float*)v2)->get())); break;
      case TYPES(TYPE_FLOAT, TYPE_FLOAT): vm->push(new Float(((Float*)v1)->get() + ((Float*)v2)->get())); break;
    }
  }
  break;
}

这样我才能做到

template <Opcode T, class X, class A>
class Unary
{
public:
  static A* ptr(X* x)
  {
    cout << "Missing instruction!" << endl;
    return NULL;
  };
};


template <>
class Unary<OP_MINUS, Float, Float>
{
  public:
    static Float *ptr(Float *x) { return new Float(-x->get()); };
}; 

这样可行,但我仍然无法看到我应该如何以通用方式管理它,以便我可以根据堆栈上的内容和使用的运算符调用正确的函数。

会不会以某种方式帮助我?我想要的是同时安全和优雅的解决方案,但是有很多不同的库,我很难理解要查找的内容。如果有一些我更容易丢失的东西,我不需要使用它,在为这类任务删除函数式语言时,我并没有想到会遇到这么多困难。

1 个答案:

答案 0 :(得分:3)

你想要boost::variant,对于元素列表,boost::make_recursive_variant(所以你可以参考类型中的类型)。

虽然apply_visitor允许您将函数应用于多种类型,但我发现这样的事情更容易开始考虑(假设您的编译器支持C ++ 11):

template<typename T, typename Func, typename Types...>
bool TryApplyFuncOn( boost::variant<Types...>& var, Func f ) {
  struct HelperVisitor {
    HelperVisitor( Func f_ ):func(f_) {}
    Func func;
    typedef bool return_type;
    template<typename U>
    return_type operator()( U& unused ) { return false; }
    return_type operator()( T& t ) { f(t); return true; }
  };
  return boost::apply_visitor( HelperVisitor(f), var );
}

获取要应用函数的类型和变量,如果要求应用的类型是变体中的类型,则应用它。如果找到匹配则返回true。

一般情况下,您可以“一次性完成”。

所以你可以这样做:

// easy case:
typedef boost::variant<int,double> scalar;
scalar times_two(scalar const& left) {
  scalar retval = left;
  TryApplyFuncOn<int>( retval, []( int& value ){ value*=2; } );
  TryApplyFuncOn<double>( retval, []( double& value ){ value*=2.; } );
  return retval;
}
// tricky case:
scalar multiply(scalar const& left, scalar const& right) {
  scalar retval = left;
  TryApplyFuncOn<int>( retval, [&right]( int& left_value ){
    TryApplyFuncOn<int>( right, [&left_value]( int& right_value ){
      left_value *= right_value;
    });
    TryApplyFuncOn<double>( right, [&left_value]( double& right_value ){
      left_value *= right_value;
    });
  });
  TryApplyFuncOn<double>( retval, [&right]( double& left_value ){
    TryApplyFuncOn<int>( right, [&left_value]( int& right_value ){
      left_value *= right_value;
    });
    TryApplyFuncOn<double>( right, [&left_value]( double& right_value ){
      left_value *= right_value;
    });
  });
  return retval;
}

尚未进行类型提升(因此int * double不会变为double),但没有任何基本的停止。

有意义吗?