2012-04-02 9 views
2

は、我々はenumeredタイプ持っている、と仮定しよう(列挙型変数

template<DataType T> 
struct TypeTraits {}; 

template<> 
struct TypeTraits<INT> { typedef int T; }; 

template<> 
struct TypeTraits<DOUBLE> { typedef double T; }; 

と操作を表し、いくつかのテンプレートがありません醜いボイドポインタやC型の型キャストを気にする):

struct Operation { 
    DataType rettype; 

    Operation(DataType rettype) : rettype(rettype); 
    virtual void* compute(); 
}; 

template<DataType RetType> 
class Constant : public Operation { 
    typedef typename TypeTraits<RetType>::T RType; 
    RType val; 

    Constant(RType val) : val(val), Operation(RetType) {}; 
    virtual void* compute(){ return &val; } 
}; 

template<DataType T1, DataType T2, DataType RetType> 
class Add : public Operation { 
    typedef typename TypeTraits<RetType>::T1 T1Type; 
    typedef typename TypeTraits<RetType>::T2 T2Type; 
    typedef typename TypeTraits<RetType>::RetType RType; 
    RType val; 

    Operation *c1, *c2; 

    Add(Operation *c1, Operation *c2) : c1(c1), c2(c2), Operation(RetType) {}; 

    virtual void* compute(){ 
    T1Type *a = (T1Type *)c1->compute(); 
    T2Type *b = (T2Type *)c2->compute(); 
    val = *a + *b; 
    return &val; 
    } 
}; 

抽象的なツリー表現:

class AbstractNode { 
    enum Type { ADD, INT_CONSTANT, DOUBLE_CONSTANT }; 

    Type type; 
    int intval; 
    double doubleval; 
    child1 *AbstractNode; 
    child2 *AbstractNode; 
} 

入力からシリアル化された抽象ツリーを読み込んで、操作ツリーに変換し、結果を計算します。

は、我々のような何かを書きたい:add_types_resolverは、操作の引数の型に基づいて、追加操作の戻り値の型を指定するものです

algebrator(Operation *op){ 
    if(op->type == AbstractNode::INT_CONSTANT) 
    return new Constant<INT>(op->intval); 
    else if(op->type == AbstractNode::DOUBLE_CONSTANT) 
    return new Constant<DOUBLE>(op->doubleval); 
    else { 
    Operation *c1 = algebrator(op->child1), 
       *c2 = algebrator(op->child2); 
    DataType rettype = add_types_resolver(c1->rettype, c2->rettype); 
    return new Add<c1->rettype, c2->rettype, rettype>(c1, c2); 
    } 
} 

私たちはもちろん失敗し、コンパイラは私たちの顔を打つでしょう。変数をテンプレート変数として使用することはできません!これは、テンプレートをインスタンス化するために必要なすべての情報が合併中に利用可能でなければならないからです!


今、質問です。

if-elseやswitch-case文をたくさん書く以外の方法はありますか?コンパイル中にすべてのケースを拡張するためにコンパイラーに何らかの方法で問い合わせることはできませんか?テンプレートはenumによってパラメータ化されているので、そのようなプロセスは有限であるという保証があります。

そして、「私はすべての例が台無しになっていると思う」といったような回答を書いてはいけません。テンプレートを変数に渡す方法があるかどうかを知りたいだけで、有限の小さなセットからのものであることがわかります。

全体的に見れば過度のように思えるかもしれませんが、私は本当に不思議なことに、どのようにクラスをインスタンス化できますか?

+4

:ここTypetraitsの私の同等がどのように見えるかです。テンプレートは、実行時の値ではなく、型とコンパイル時定数でパラメータ化されます。しかし、それはあなたがすでにそれを認識しているように聞こえます。 –

+1

int型のイディオムを使用してテンプレートのコンパイル時の型を生成できますが、それは良い考えかどうかはまったく別の問題です。 – AJG85

+1

動的ルックアップコードをプログラムの1つのコーナーに分解して、あまりノイズを発生させずに使用できるようにするのは非常に難しいことではありません。 –

答えて

2

Quick'n'dirty溶液:

column_type.cc:

enum ColumnType { 
    INT = 1, 
    DOUBLE = 2, 
    BOOL = 3 
}; 

typed_call_test.cc(使用例):

#include <iostream> 
#include "column_type.cc" 
#include "typed_call.cc" 

template <ColumnType T> 
void PrintType() { 
    ::std::cout << T <<::std::endl; 
} 

int main() { 
    ColumnType type = INT; 
    // this won't compile: 
    // PrintType<type>();     
    // and instead of writing this: 
    switch (type) {     
    case INT:     
     PrintType<INT>();     
     break;     
    case DOUBLE:     
     PrintType<DOUBLE>();     
     break;     
    case BOOL:     
     PrintType<BOOL>();     
     break;     
    }     
    // now you can write this: 
    TYPED_CALL(PrintType, type,); 

    return 0; 
} 

typed_call。CC(「ライブラリ」):

// Requirements: 
// |function| return type must be void 
// 
// Usage: 
// 
// having for instance such |type| variable: 
// ColumnType type = INT; 
// and such |foo| function definition: 
// template <ColumnType T> 
// void foo(t1 arg1, t2 arg2) { 
//  … 
// } 
// 
// instead of writing (won't compile): 
// foo<type>(arg1, arg2);     
// write this: 
// TYPED_CALL(foo, type, arg1, arg2); 
// 
// 
// for foo with 0 arguments write this: 
// TYPED_CALL(foo, type,); 
// 
#define TYPED_CALL(function, type, args...) {      \ 
    switch (type) {             \ 
    case INT:              \ 
     function<INT>(args);           \ 
     break;               \ 
    case DOUBLE:              \ 
     function<DOUBLE>(args);          \ 
     break;               \ 
    case BOOL:              \ 
     function<BOOL>(args);           \ 
     break;               \ 
    }                 \ 
} 

#define BASE_TYPED_CALL(function, type, args...) {     \ 
    switch (type) {             \ 
    case INT:              \ 
     function<int>(args);           \ 
     break;               \ 
    case DOUBLE:              \ 
     function<double>(args);          \ 
     break;               \ 
    case BOOL:              \ 
     function<bool>(args);           \ 
     break;               \ 
    }                 \ 
} 

このソリューションを取るために「レベルアップ」あなたは(まだ似スイッチ構築物を含む)機能でマクロを置き換えることができます。しかしおそらく、このマクロのような通常の関数の代わりに、この関数のパラメータとしてファンクタ(オブジェクトを持つオブジェクト)を渡したいとします。 Btw:これはGoogleでこれを行う方法とまったく同じです。


第1回sidenote:ワルシャワ大学の柱状および分散データウェアハウスコースからの私のクラスメート!このコースは、心曲げC++テンプレートの質問の多くを生成している:)

第二追記を:全例が台無しにされ

template <ColumnType T> 
struct EnumToBuiltin { 
}; 

template <> 
struct EnumToBuiltin<INT> { 
    typedef int type; 
}; 
template <> 
struct EnumToBuiltin<DOUBLE> { 
    typedef double type; 
}; 
template <> 
struct EnumToBuiltin<BOOL> { 
    typedef bool type; 
}; 


template <typename T> 
struct BuiltinToEnum { 
}; 

template <> 
struct BuiltinToEnum<int> { 
    static const ColumnType type = INT; 
}; 
template <> 
struct BuiltinToEnum<double> { 
    static const ColumnType type = DOUBLE; 
}; 
template <> 
struct BuiltinToEnum<bool> { 
    static const ColumnType type = BOOL; 
}; 
+2

それはトリッキーですがクールな解決策です! –

0

コンパイル時にすべてのケースを拡張するようコンパイラーに問い合せることはできませんか?

これは可能です。それが動的である条件 - > TMPを導入してください。あなたが正しい場所でそれを行うなら、あなたは書くべき条件がほとんどありません。コンパイル時の情報として型と定数を導入することで、これを最小限に抑えることができます。マクロを