関数型言語 (OCaml) で以前に書いたコードを C++ に書き直そうとしています。
私の問題は次のように短縮できます。
- 私は値のスタックを持っています
- 値はバリアント型にすることができます (したがって、さまざまな種類の値のセット、たとえば
int
、float
、std::string
などstd::list
) - 値に作用する演算子を定義したい (例: 2 つの値をポップしてそれらの合計をプッシュする加算演算)
- 一部の演算子は、スタック上にある型に応じて異なる動作をします。理想的には、型に応じて引数の数を変更する演算子もあります (単純な例: 加算演算子は、1 つの値をポップし、
std::list
それがすべての間に適用された演算子をプッシュする場合)リストの値。それ以外の場合は別の値をポップし、両方が float の場合は加算を行います)
これまでは、テンプレートなどを使用して機能させることができました。
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); }
};
そして、演算子は次のように解釈されます
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()); };
};
できるように
Float *a = new Float(10);
Float *r = Unary<OP_MINUS, Float, Float>::ptr(f);
これは機能しますが、スタック上で見つかったものと使用されている演算子に応じて適切な関数を呼び出すことができるように、一般的な方法でそれを管理する方法をまだ確認できません。
ブーストは何とか私を助けますか?私が望んでいるのは、タイプセーフであると同時にエレガントなソリューションですが、ブーストには非常に多くの異なるライブラリがあり、何を探すべきかを理解するのが難しいです。欠けている簡単なものがある場合は、それを使用する必要はありません。この種のタスクのために関数型言語をドロップするときに、それほど多くの困難を見つけるとは思いませんでした。