2010-04-08 106 views
3

我目前正在使用新的C++ 0x variadic模板,它很有趣,雖然我對成員實例化過程有疑問。關於variadic模板

在這個例子中,我試圖模擬強類型枚舉的可能性,選擇一個隨機有效的強枚舉(這是用於單元測試)。

 

#include<vector> 
#include<iostream> 

using namespace std; 

template<unsigned... values> struct sp_enum; 

/* 
this is the solution I found, declaring a globar var 
vector<unsigned> _data; 

and it work just fine 

*/ 

template<> struct sp_enum<>{ 
    static const unsigned _count = 0; 
    static vector<unsigned> _data; 
}; 

vector<unsigned> sp_enum<>::_data; 

template<unsigned T, unsigned... values> 
struct sp_enum<T, values...> : private sp_enum<values...>{ 
    static const unsigned _count = sp_enum<values...>::_count+1; 
    static vector<unsigned> _data; 

    sp_enum(      ) : sp_enum<values...>(values...) {_data.push_back(T);} 
    sp_enum(unsigned v   )         {_data.push_back(v);} 
    sp_enum(unsigned v, unsigned...) : sp_enum<values...>(values...) {_data.push_back(v);} 
}; 

template<unsigned T, unsigned... values> vector<unsigned> sp_enum<T, values...>::_data; 

int main(){ 
    enum class t:unsigned{Default = 5, t1, t2}; 
    sp_enum<t::Default, t::t1, t::t2> test; 
    cout <<test._count << endl << test._data.size() << endl; 
    for(auto i= test._data.rbegin();i != test._data.rend();++i){cout<< *i<< ":";} 
} 


 

我這個代碼得到的結果是:

 
3 
1 
5: 

能有人指出我什麼我搞亂這裏???

PS:使用gcc 4.4.3


我已經修改了代碼更加通用的,以減少編碼鐵桿儘可能(@Matthieu M.)。但是我想解釋一下爲什麼我會這麼做。

我有很多開發人員在我的代碼中採用了新的C++ 0x標準,我對此感到很高興。但是在嘗試編寫測試單元時,我遇到了強類型枚舉的問題。

問題是你不能生成一個隨機強類型的枚舉(我知道,你可以,但想要以更優雅的方式)。所以,現在用這個代碼,我可以使用可變參數模板和可變宏(是舊的髒宏)聲明並隨機選擇一個強類型和範圍的枚舉。

這裏是代碼:

 

#include<vector> 
#include<iostream> 

#include <boost/preprocessor/array/elem.hpp> 
#include <boost/preprocessor/repetition/repeat.hpp> 
#include <boost/preprocessor/repetition/repeat_from_to.hpp> 

using namespace std; 

template<typename T, T... values> class sp_enum; 

template<typename T> class sp_enum<T>{ 
    protected: static const unsigned _count = 0; 
}; 

template<typename T, T head, T... values> 
class sp_enum<T, head, values...> : public sp_enum<T, values...>{ 
protected: 
    static const unsigned _count = sp_enum<T, values...>::_count+1; 
    static vector<T> _data; 

public: 
    sp_enum(  ) : sp_enum<T, values...>(values...) {_data.push_back(head);for(auto i= sp_enum<T, values...>::_data.begin();i != sp_enum<T, values...>::_data.end();++i){_data.push_back(*i);}} 
    sp_enum(T v  )         {_data.push_back(v );} 
    sp_enum(T v, T...) : sp_enum<T, values...>(values...) {_data.push_back(v );for(auto i= sp_enum<T, values...>::_data.begin();i != sp_enum<T, values...>::_data.end();++i){_data.push_back(*i);}} 

    vector<T> data() const { return _data ;} 
    unsigned count() const { return _count ;} 

    static T randomEnum() { srand (time(NULL));return _data[rand()%_count];} 

}; 

template<typename T, T head, T... values> vector<T> sp_enum<T, head, values...>::_data; 

#define PP_NARG(...) PP_NARG_(__VA_ARGS__,PP_RSEQ_N()) 
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__) 

#define PP_ARG_N(\ 
     _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ 
     _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ 
     _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ 
     _61,_62,_63,N,...) N 

#define PP_RSEQ_N() \ 
     63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40, \ 
     39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16, \ 
     15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0 

#define FOREACH_ARRAY(...) (PP_NARG(__VA_ARGS__) , (__VA_ARGS__)) 
#define FOREACH(Name, A, ...)  BOOST_PP_REPEAT_FROM_TO(1, PP_NARG(Name, __VA_ARGS__), A, FOREACH_ARRAY(Name, __VA_ARGS__)) 
#define DECORATION(z,n,data) ,BOOST_PP_ARRAY_ELEM(0, data) :: BOOST_PP_ARRAY_ELEM(n, data) 

#define SP_ENUM(enumName, ...)        \ 
    enum class _##enumName : unsigned { Default, __VA_ARGS__ }; \ 
    typedef sp_enum<_##enumName FOREACH(_##enumName, DECORATION, Default, __VA_ARGS__) > enumName; 

SP_ENUM(xx, test1, test2, test3) 

int main(){ 
    xx test; 
    cout <<test.count() << endl << test.data().size() << endl; 
    auto dt = test.data(); 
    for(auto i = dt.rbegin(); i != dt.rend();++i){ cout<< (unsigned)*i << ":" ; } 
    cout << "random strongly typed enum : " << (unsigned) test.randomEnum() << endl; 
} 
 

什麼打擾我現在,是PP_NARG宏的限制(我還沒有發現任何其他的方法來計算參數的個數)。

我會很樂意接受任何指針或提示來加強這一點。

+0

當我修正了幾個錯別字,我看見你包圍你的代碼與''標籤....嚴重? ''? – Motti 2010-04-08 19:20:21

+0

刪除。沒有任何解釋,爲什麼它首先在那裏,除了我當時並沒有使用我的想法。 :p – chedi 2010-04-09 15:59:05

回答

5

static vector<unsigned> _data;不會在sp_enum實例化中共享,只能跨具有相同參數的模板類的實例共享。

+0

是的,你是對的,現在我知道我錯過了什麼。 非常感謝。 – chedi 2010-04-08 11:00:17

0

感謝Pavel,我發現了這個問題。

這裏是解決

 

#include<vector> 
#include<iostream> 

using namespace std; 

template<unsigned... values> struct sp_enum; 

template<> struct sp_enum<>{ 
    static const unsigned _count = 0; 
    static vector<unsigned> _data; 
}; 

vector<unsigned> sp_enum<>::_data; 

template<unsigned T, unsigned... values> 
struct sp_enum<T, values...> : private sp_enum<values...>{ 
    static const unsigned _count = sp_enum<values...>::_count+1; 
    static vector<unsigned> _data; 

    sp_enum(      ) : sp_enum<values...>(values...) {_data.push_back(T);for(auto i= sp_enum<values...>::_data.begin();i != sp_enum<values...>::_data.end();++i){_data.push_back(*i);}} 
    sp_enum(unsigned v   )         {_data.push_back(v);} 
    sp_enum(unsigned v, unsigned...) : sp_enum<values...>(values...) {_data.push_back(v);for(auto i= sp_enum<values...>::_data.begin();i != sp_enum<values...>::_data.end();++i){_data.push_back(*i);}} 

}; 

template<unsigned T, unsigned... values> vector<unsigned> sp_enum<T, values...>::_data; 

int main(){ 
    enum class t:unsigned{Default = 5, t1, t2}; 
    sp_enum<t::Default, t::t1, t::t2> test; 
    cout <<test._count << endl << test._data.size() << endl; 
    for(auto i= test._data.rbegin();i != test._data.rend();++i){cout<< *i<< ":";} 
} 

+0

請注意,在StackOverflow,你可以編輯你的第一篇文章,並把解決方案在那裏;) – ALOToverflow 2010-04-08 19:22:50

+0

@Frank:我不知道,謝謝。 – chedi 2010-04-09 15:56:58

0

在一般筆記,這裏的主要問題是,你將需要你的vector每個枚舉的一個實例(邏輯,是不是?) 。

例如,如果使用2個枚舉,則更正不起作用。

所以做順理成章的事情將是沿着線的東西:

template <class Enum> 
struct sp_enum_vector { static std::vector<unsigned> _data; }; 

然後修改你的枚舉類爲:

template <class Enum, unsigned... values> 
struct sp_enum; 

爲了區分階級取決於枚舉我們正在談論。

下一個問題:我們如何獲得unsignedt?硬編碼不好:p

+0

我覺得還有一個問題就是不得不創建一個實例來設置一個靜態變量的值(首先創建兩個或更多,然後再打擊它)。難道這也是可變參數模板在這裏不加任何東西,因爲'initializer_list'會更方便嗎? – UncleBens 2010-04-08 15:17:08

+0

這可能是正確的,但這個例子並不是一個完整的例子。我只是在研究可​​變參數模板的可能性。我將提交更完整的代碼,更好地解釋我的目標。 謝謝 – chedi 2010-04-08 15:30:41

1

由於這個問題變成了一團糟。

你是否在這樣的事情?對不起,我不知道boost的預處理程序庫,但它似乎並不特別需要。唯一的問題是,count必須是編譯時常量嗎?

#include<vector> 
#include<iostream> 
#include <cstdlib> 

#define DECLARE_ENUM(enum_name, ...) \ 
enum class enum_name##_ : unsigned { __VA_ARGS__}; \ 
class enum_name { \ 
public: \ 
    static enum_name##_ random() { return static_cast<enum_name##_>(values[rand() % values.size()]); } \ 
    static unsigned count() { return values.size(); } \ 
    static std::vector<unsigned> data() { return values; } \ 
private: \ 
    enum : unsigned {__VA_ARGS__}; \ 
    static std::vector<unsigned> values; \ 
}; \ 
std::vector<unsigned> enum_name::values{__VA_ARGS__}; 

DECLARE_ENUM(xx, test1, test2, test3) 

int main(){ 
    xx test; 
    std::cout <<test.count() << std::endl << test.data().size() << std::endl; 
    auto dt = test.data(); 
    for(auto i = dt.rbegin(); i != dt.rend();++i){ std::cout<< (unsigned)*i << ":" ; } 
    xx_ random_value = test.random(); 
    std::cout << "random strongly typed enum : " << (unsigned) random_value << std::endl; 
} 

不可否認的設計可以更好,而我沒有打擾關於默認的想法(它打破了,無論如何,如果你不想連續值)。


另一件事,如果這隻支持連續的值,那麼沒有理由將矢量放在第一位。只需存儲第一個(如果不是總是0)和最後一個值,並且可以計算所有其他值。數據方法可能會返回一系列提升的counting_iterator

還是隻專注一個相應的traits類:

#include<iostream> 
#include <cstdlib> 

namespace detail { 
template <unsigned ...values> 
struct last; 

template <unsigned N, unsigned ...values> 
struct last<N, values...> 
{ 
    static const unsigned value = last<values...>::value; 
}; 

template <unsigned N> 
struct last<N> 
{ 
    static const unsigned value = N; 
}; 

template <unsigned N, unsigned ...> 
struct first 
{ 
    static const unsigned value = N; 
}; 
} 
template <class T> 
struct enum_traits; 

#define DECLARE_ENUM(enum_name, ...) \ 
enum class enum_name : unsigned { __VA_ARGS__}; \ 
template <> struct enum_traits<enum_name> { \ 
private: enum : unsigned { __VA_ARGS__ }; \ 
public: \ 
    static const unsigned first = detail::first< __VA_ARGS__>::value; \ 
    static const unsigned last = detail::last< __VA_ARGS__>::value; \ 
    static const unsigned count = last - first + 1; \ 
}; 

template <class T> 
T random_enum_value() 
{ 
    return static_cast<T>(rand() % enum_traits<T>::count + enum_traits<T>::first); 
} 

DECLARE_ENUM(xx, test1, test2, test3) 

int main(){ 
    std::cout << enum_traits<xx>::first << ' ' << enum_traits<xx>::last << ' ' << enum_traits<xx>::count << '\n'; 
    std::cout << (unsigned) random_enum_value<xx>() << '\n'; 
} 
+0

首先,我很抱歉我的英語和溝通能力差。並感謝您的意見。 我的目的不是開發一個新的強類型枚舉,我在我的代碼中使用C++ 0x強類型枚舉(枚舉類),我滿意他們。 問題是爲使用強類型枚舉的代碼編寫測試單元,因爲我必須找到將這些枚舉封裝在容器中的通用方法,以便隨意得到一個。 此外,我還想嘗試新的可變參數模板,並在SO中獲得非常混亂的帖子。 – chedi 2010-04-09 15:42:32

+0

所有的要點不是在一個作用域容器類中使用枚舉,而是通過聲明一個預先存在的強類型枚舉的容器: enum class XXX {....}; 使用可變宏和增強預處理器是爲了簡化容器類的聲明。 – chedi 2010-04-09 15:53:51

+0

我也定義了一個強枚舉片段。在這些類中,還有另一個具有相同值的枚舉,因此可以訪問標識符而無需指定枚舉名稱。 - 如果元素具有任意的非連續值,枚舉枚舉的做法仍然很有趣。 – UncleBens 2010-04-09 16:40:53