2012-01-20 81 views
11

說我有這樣一個宏:如何將多參數模板傳遞給宏?

#define SET_TYPE_NAME(TYPE, NAME) \ 
    template<typename T>   \ 
    std::string name();   \ 
            \ 
    template<>     \ 
    std::string name<TYPE>() { \ 
     return NAME;    \ 
    } 

這不會工作,如果我通過它有多個參數的模板,因爲在<int, int>逗號被解釋爲分離參數,而不是模板參數。

SET_TYPE_NAME(std::map<int, int>, "TheMap") 
// Error: macro expects two arguments, three given 

這個問題似乎是這樣解決:

SET_TYPE_NAME((std::map<int, int>), "TheMap") 

但現在出現了另一個問題,一個是我真的沒有想到:

template<> 
std::string name<(std::map<int, int>)>() 
// template argument 1 is invalid 

似乎多餘括號使模板參數無效。有沒有辦法解決?

回答

8

此外typedef,你可以切換的參數的順序,並使用複雜的宏(需要C99或C++ 11兼容的編譯器):

#define SET_TYPE_NAME(NAME, ...) \ 
template<typename T>   \ 
std::string name();   \ 
           \ 
template<>     \ 
std::string name<__VA_ARGS__>() { \ 
    return NAME;    \ 
} 

...

SET_TYPE_NAME("TheMap", std::map<int, int>) 
+6

只是C++ 11,除非C99加入'template'趁我們不看。 –

+0

@KennyTM:該死的,比我早一分鐘! –

+0

@MikeDeSimone:__VA_ARGS__是在C99中引入的,所以C++編譯器(例如gcc 4.2)可以用C++作爲非標準擴展來支持它。 – kennytm

8

你可以使用typedef

typedef std::map<int, int> int_map; 

SET_TYPE_NAME(int_map, "TheMap"); 

升壓轉換器的BOOST_FOREACH患有同樣的問題。

1
typedef std::map<int,int> IntMap_t; 
SET_TYPE_NAME(IntMap_t, "TheMap") 

你可以聲明一個typedef,並用它在宏觀

3

我喜歡的道具的typedef方式通過hmjd osed更好,但爲了記錄在案,我這周圍看到的通常的方法是踢尖括號出宏觀的,寫:

#define SET_TYPE_NAME(TYPE, NAME) \ 
    template<typename T>   \ 
    std::string name();   \ 
            \ 
    template<>     \ 
    std::string name TYPE() { \ 
     return NAME;    \ 
    } 

用法:

SET_TYPE_NAME(<std::map<int, int> >, "TheMap") 

這是用於錯誤消息報告和fprintf舊技術的變:

#define Error(args) do { \ 
    printf("ERROR: "); \ 
    printf args; \ 
    printf("\n"); \ 
    return 1; \ 
    } while(0) 

調用與:

Error(("Index out of range: %d not in %d ... %d.", var, min, max)); 

這是醜陋的,但它的工作。編碼樣式規則禁止typedef有用。

+1

有趣,但是這不會再次引發第一個錯誤嗎?宏將模板解釋爲兩個參數的那個。 –

+2

如果編碼風格規則禁止'typedef' - >,那麼這很有用,現在你讓我擔心了。 –

+2

不幸的是,只有括號,而不是'<' and '>',被預處理器識別爲括號,所以這不起作用。 –

5

前段時間(當在網上搜索Identity<T>的工具時)我得到了this page。簡而言之,要回答這個問題,如果你沒有C++ 11的支持和/或不能(或不想)使用typedef,你可以稱你的宏爲「正確」的方式:

// If an argument contains commas, enclose it in parentheses: 
SET_TYPE_NAME((std::map<int, int>), "TheMap") 
// For an argument that doesn't contain commas, both should work: 
SET_TYPE_NAME((SomeType1), "TheType1") 
SET_TYPE_NAME(SomeType2, "TheType2") 

,然後擺脫(可能)不希望括號繞式,你可以使用一個「助手」是這樣的:

template<typename> struct RemoveBrackets; 
template<typename T> struct RemoveBrackets<void (T)> { 
    typedef T Type; 
}; 

,並在您的宏更改的行:

std::string name<TYPE>() { \ 

到:

std::string name< RemoveBrackets<void (TYPE)>::Type >() { \ 

(或限定一個輔助宏,說

#define REMOVE_BRACKETS(x) RemoveBrackets<void (x)>::Type 

然後用

std::string name< REMOVE_BRACKETS(TYPE) >() { \ 

替換行)。

(完整故事讀一段「一個更好的解決方案」,在上面鏈接的文章的末尾)。

編輯:剛剛發現that。但它使用<void X>時,它確實應該使用<void (X)>(在get_first_param<void X>::type);的確括號必要的,如果你傳遞一個「簡單」,非加括號的參數(如SomeType2上面我的代碼) - 他們不如果X已經括號傷害(例如,void ((SomeType1))相當於void (SomeType1);再次,請參閱文章)。 (順便說一下,我注意到在其他SO頁面上的許多答案本質上是「宏是愚蠢的」,但我不會評論。)

+0

編輯:[現在你可以使用'BOOST_IDENTITY_TYPE'(感謝OP)](http://stackoverflow.com/a/11701846/688659) –

4

我想爲自己的問題添加一個答案。現在Boost 1.50.0發佈了,Boost.Utility有了一個新的小型庫,可以幫助解決這類問題。如果你看看源代碼,你會看到它的實現與gx_'s solution相同。以下是如何使用它:

#include <boost/utility/identity_type.hpp> 
SET_TYPE_NAME(BOOST_IDENTITY_TYPE((std::map<int, int>)), "TheMap"); 
+0

_(我現在可以留言了!)_謝謝你的消息:) –

+0

這是最好的答案,謝謝! – spatz

1

一個更通用的方法是經常使用()在你的論點可能包含一個逗號和使用CONCAT刪除括號。如果你這樣做,你可以定義多個參數包鬥智鬥勇逗號內或將參數您喜歡的順序

#ifndef CONCAT 
#define CONCAT __VA_ARGS__ 
#endif 

#define MYMACRO(tparam,classname)\ 
    template < CONCAT tparam >\ 
    class CONCAT classname {}; 

//create a template class X<T,U> 
MYMACRO((typename T,typename U) , (X<T,U>))