2012-04-02 33 views
2

假設,我們有一個enumered類型:枚舉變量作爲動態模板參數

enum DataType { INT, DOUBLE }; 

和類型映射:

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; 
} 

我們從輸入讀取序列化抽象的樹,以將其轉化爲一個操作樹,然後 - 計算結果。

我們想寫類似:

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); 
    } 
} 

其中add_types_resolver的東西,它指定根據操作參數類型的加法運算的返回類型。

我們當然不能和編譯器會打我們到臉上。我們不能使用變量作爲模板變量!這是因爲實例化模板所需的所有信息必須在編譯期間可用!


現在 - 問題。

是否有任何其他的解決方案不是編寫大量的if-else,或switch-case語句?我們不能讓編譯器以任何方式在編譯期間擴展所有的情況嗎?模板由枚舉參數化的,所以我們必須保證,這樣的過程是有限的。

並請 - 不要寫諸如「我想整個例子是搞砸了」的反應。我只是想知道是否有辦法養活可變模板,知道它從有限,小套的。

整個事情可能看起來像一個矯枉過正的,但我真的很好奇,我怎麼能在這種不尋常的情況下實例化類。

+4

整個例子搞砸。模板參數化類型和編譯時常量,而不是運行時值。但它聽起來像你已經意識到這一點...... – 2012-04-02 22:07:33

+1

你可以使用int-to-type成語爲基於值的模板生成編譯時類型,但是這是否是個好主意完全是另一回事。 – AJG85 2012-04-02 22:14:29

+1

將動態查找代碼分解到程序的一個角落並使其可用而沒有太多噪音應該不是很困難。 – 2012-04-02 22:14:51

回答

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;               \ 
    }                 \ 
} 

藉此溶液「電平向上」您可以與(仍含有similiar開關構建體)的函數替換宏。但可能你想傳遞一個仿函數(帶有()運算符的對象)作爲這個函數的參數,而不是像這個宏那樣的普通函數。順便說一句:這正是他們在Google中的做法。


第一個腳註:嗨,我的同學來自華沙大學的Columnar和Distributed DataWarehouses課程!這當然也帶來了很大的精神錯亂的C++模板的問題:)

第二旁註:這裏是我的等效Typetraits的的樣子:

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

耶。這是棘手的,但很酷的解決方案! – 2012-04-06 17:46:55

0

難道我們不能要求編譯器以任何方式在編譯過程中擴展所有的情況嗎?

你可以做到這一點。引入條件 - > TMP它是動態的。如果你在正確的地方做,那麼你將沒有條件寫。將類型和常量引入編譯時間信息將有助於最小化這一點。