2016-05-21 13 views
1

私は逆ポーランド記法を使って科学計算機をエミュレートするプロジェクトに取り組んでいます。 "1"、 "1.23"、 "2/3"などの私のlitteralsは、その種類のスタックに格納されています。例えば、 "1"は整数として格納され、 "1.23"はDoubleなどとして、私の型(Double、Integerなどはさまざまな理由で定義されています)を使用して格納されます。私のスタック上のユーザは、+などのオペレータを入力したときに、私は(ポップする必要があります)を2回、操作を実行し、(プッシュ):パラメータに応じて 'new XXXX'を使用する方法はありますか?

私は次のようにやりたい
class Litteral { 
    public: 
    virtual QString toString() const = 0; 
}; 

。私はすでに演算子の認識を行っています。ユーザーが+を入力すると、私は与えられたアルゴリズムを実行します。 私の問題は、+が認識され、2つのpop()が実行されたとき、私は実際に+を実行する必要があり、正しいタイプのオブジェクトを作成する必要があるということです。私は2つの整数を持っていた場合でも、私が持っていると思います

Litteral* l = new Double(pop1+pop2); 

Litteral* l = new Integer(pop1 + pop2); 

等... 私は整数とダブルを持っていた場合に例えば は、私が持っていると思います正しい「新しいXXXX()」を実行するために大量の「if」を使うことができましたが、これは良い解決策ではないように感じられます。 私はまた、対応するアルゴリズムを実行するために使用するテンプレートメソッドを使用することについて考えましたが、可能な組み合わせがあるので、この関数の多くのバージョンを作成する必要があります。それでも人間的に可能ですが、やはりきれいに感じません。 これを行うためのきれいな方法はありますか?

おかげ

編集:私は2つの整数をポップするとき例えば は、私は特定の操作を実行したいです。この操作の結果はIntegerでなければなりません。しかし結果は常に整数ではなく、それは私がポップしたものに依存します。それがIntegerとDoubleの場合、結果はDoubleになります。だから私がしなければならない「新しい」タイプは、私がポップしたものに依存します。 そして、私はスイッチ/倍数 'if'でそれを実装したくありません。私はそれを行うにはしたくない:

if ((typeid(*q1) == typeid(Integer)) && (typeid(*q2) == typeid(Double))) { //...} 

編集2:

Literal& pop1 = stack.top(); 
stack.pop(); 
Literal& pop2 = stack.top(); 
stack.pop(); 
Literal& toAdd = (*pop1.clone() + *pop2.clone()); 

エラー: '演算子+' の不一致(オペランドの型は、 'リテラル' と 'リテラル' であり)

クラス:

class Literal { 
    public: virtual QString toString() const = 0; 
     virtual int getValue() const = 0; 
     virtual Litteral * clone() const = 0; 
     virtual Litteral& operator+(const Integer& l) = 0; 
}; 

class Integer: public Literal { 
    friend class LitteralManager; 
    int value; 
    public: 
     Integer(int v) :value(v) {} 
     Integer(const Entier& e) { value = e.getValue(); }; 
     virtual QString toString() const; 
     int getValue() const { return value; } 
     virtual Entier& operator+(const Entier& e); 
     Entier * clone() const; 
}; 
+0

あなたの例の 'pop1'と' pop2'は 'Double'か' Integer '、そう?もしそうなら、 'operator +'はどのように定義されていますか?適切な型を返すようにすることはできませんし、まったく使用するときに 'new'を使用する必要はありませんか? – sepp2k

+0

* ..整数として、 "1.23"はダブルなどとして*。 * etc *に展開してください。どのくらいの種類の合計がありますか? –

+0

私は7種類のリッタータルを持っています。 – Chuck

答えて

0
#include <cassert> 
#include <typeinfo> 

using namespace std; 

struct Double; 
struct Integer; 

struct Literal { 
    virtual Literal* operator + (const Literal&) const = 0; 
    virtual Literal* operator + (const Double&) const = 0; 
    virtual Literal* operator + (const Integer&) const = 0;}; 

struct Double : Literal { 
    Literal* operator + (const Literal& rhs) const { 
     return rhs + *this;} 

    Literal* operator + (const Double& rhs) const { 
     return new Double;} 

    Literal* operator + (const Integer& rhs) const { 
     return new Double;}}; 

struct Integer : Literal { 
    Literal* operator + (const Literal& rhs) const { 
     return rhs + *this;} 

    Literal* operator + (const Double& rhs) const { 
     return new Double;} 

    Literal* operator + (const Integer& rhs) const { 
     return new Integer;}}; 

int main() { 
    { 
    Literal* p = new Double; 
    Literal* q = new Double; 
    Literal* r = *p + *q; 
    assert(typeid(*r) == typeid(Double)); 
    } 

    { 
    Literal* p = new Double; 
    Literal* q = new Integer; 
    Literal* r = *p + *q; 
    assert(typeid(*r) == typeid(Double)); 
    } 

    { 
    Literal* p = new Integer; 
    Literal* q = new Double; 
    Literal* r = *p + *q; 
    assert(typeid(*r) == typeid(Double)); 
    } 

    { 
    Literal* p = new Integer; 
    Literal* q = new Integer; 
    Literal* r = *p + *q; 
    assert(typeid(*r) == typeid(Integer)); 
    } 

    return 0;} 
+0

この解決策に関するすべてのことを忘れました。完璧です。ありがとう! – Chuck

+0

あなたは大歓迎です。私はあなたが必要とするものに合ってうれしいです。そのパターンは、ダブルディスパッチと呼ばれています。理想からまだ遠いです、うまく調整できません。答え?:-) –

+0

もちろん、問題ありません! – Chuck

0
#include <cassert> 
#include <stack> 

using namespace std; 

struct Literal { 
    virtual Literal* clone() const = 0;}; 

struct Double : Literal { 
    Double* clone() const { 
     return new Double(*this);}}; 

struct Integer : Literal { 
    Integer* clone() const { 
     return new Integer(*this);}}; 

int main() { 
    stack<Literal*> x; 

    x.push(new Double()); 
    x.push(new Integer()); 

    Literal* p = x.top(); // the Integer 
    x.pop(); 
    Literal* q = p->clone(); // create a new Integer 

    assert(typeid(*q) == typeid(Integer)); 

    return 0;} 
+0

最初に、あなたの答えをありがとうが、工場が私をどのように助けているのか分からない。 – Chuck

+0

DoubleがIntegerより優先されるべきであることを知る明確な方法はありません。最初のポップされたオブジェクトの型が、結果、しかし今私はあなたが何をしようとしているかを見ています。イテレータの特性がどのように機能するかを見てください。アルゴリズムが最も適切な実装を選択できるようにする階層を形成します。 –

0

ここで可能なソリューションです:

#include <exception> 
#include <functional> 
#include <iostream> 
#include <cassert> 
#include <memory> 

class Literal { 
public: 
    virtual ~Literal() = default; 
}; 

class Double : public Literal { 
public: 
    Double(double val) : m_val(val) { } 
    Double(const Double&) = default; 
    Double(Double&&) = default; 
    ~Double() = default; 

    double value() const { return m_val; } 

private: 
    double m_val; 
}; 

class Integer : public Literal { 
public: 
    Integer(int val) : m_val(val) { } 
    Integer(const Integer&) = default; 
    Integer(Integer&&) = default; 
    ~Integer() = default; 

    int value() const { return m_val; } 

private: 
    int m_val; 
}; 

template <typename T> struct make_literal_result; 
template <> struct make_literal_result<int> { 
    using type = Integer; 
}; 
template<> struct make_literal_result<double> { 
    using type = Double; 
}; 
template <typename T> 
using make_literal_result_t = typename make_literal_result<T>::type; 

template <typename T> std::unique_ptr<make_literal_result_t<T>> make_literal(T x) { 
    return std::make_unique<make_literal_result_t<T>>(x); 
} 

template <typename Res, typename Fn> 
Res dynamic_apply(Fn f, const Literal* val) { 
    auto vald = dynamic_cast<const Double*>(val); 
    if (vald != nullptr) 
    return f(vald->value()); 
    auto vali = dynamic_cast<const Integer*>(val); 
    if (vali != nullptr) 
    return f(vali->value()); 
    throw std::invalid_argument("Invalid type"); 
} 

std::unique_ptr<Literal> dynamic_plus(const Literal* x, const Literal* y) { 
    auto curried_plus = [](auto val1) -> std::function<std::unique_ptr<Literal>(const Literal*)> { 
    return [val1](const Literal* y) -> std::unique_ptr<Literal> { 
     return dynamic_apply<std::unique_ptr<Literal>>([val1](auto val2) { 
     return make_literal(val1 + val2); 
     }, y); 
    }; 
    }; 
    auto plus_x = dynamic_apply<std::function<std::unique_ptr<Literal>(const Literal*)>>(curried_plus, x); 
    return plus_x(y); 
} 

int main() { 
    Double x { 2.5 }; 
    Integer y { 3 }; 
    auto sum = dynamic_plus(&x, &y); 
    auto sum_Double = dynamic_cast<Double*>(sum.get()); 
    assert(sum_Double != nullptr); 
    std::cout << "sum is: " << sum_Double->value() << '\n'; 
    return 0; 
} 

dynamic_plusの一般的な考え方は次のとおりです。plus_xに格納された結果は、その体その作業を行うために、一般的なラムダにdynamic_applyを使用する関数const Literal*を取り、std::unique_ptr<Literal>を返し、です。 plus_xを構築するために、上位レベルの機能にmake_literalの別のアプリケーションが順番に適用されます。 (dynamic_applyが返されるためには、std::functionを使用してタイプ消去を行う必要があります)

+0

お返事ありがとうございます。私は別の解決策を検討し始めましたが、うまくいかない場合は、あなたのポストに戻ってきます。答えに時間をとってくれてありがとう! – Chuck

関連する問題