2010-04-08 5 views
3

현재 새로운 C++ 0x 가변 템플릿을 사용하고 있습니다. 회원 인스턴스 생성 과정에 대한 질문이 있지만 상당히 재미 있습니다.가변 템플릿 정보

이 예제에서는 강력하고 강력한 열거 형을 선택할 가능성이있는 강력한 형식의 열거 형을 에뮬레이트하려고합니다 (단위 테스트 용으로 사용됨).

 

#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: 

사람이 내가 여기에 쌓여있는 무슨 날 지점 수 있습니다 ???

시 : 내가 더 일반적인 것으로 가능 (@Matthieu M.)와 같은 하드 코어 코딩을 줄이기 위해 코드를 재 작업 한 GCC 4.4.3


를 사용하여. 그러나 나는 왜 내가이 모든 일을하고 있는지 설명하고 싶다.

많은 개발자들이 내 코드에 새로운 C++ 0x 표준을 채택 했으므로 기쁘게 생각합니다. 하지만 테스트 단위를 작성하려고 할 때 강력한 형식의 열거 형에 문제가 있습니다.

문제는 임의의 강력한 형식의 열거 형을 생성 할 수 없다는 것입니다. (내가 할 수는 있지만 더 세련된 방식으로 수행하려고합니다.) 이제이 코드를 사용하여 가변성 템플릿과 가변 매크로 (예전의 더티 매크로)를 사용하여 강력한 형식의 범위가 지정된 enum을 선언하고 무작위로 선택할 수 있습니다. 여기

코드입니다 :의 PP_NARG 매크로의 한계가 지금 귀찮게 무엇

 

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

(I는 인수의 수를 계산하는 다른 방법을 발견하지 않았습니다).

나는 이것을 향상시키기 위해 기꺼이 모든 포인터 또는 힌트를 수락 할 것입니다.

+0

난 당신이 ....''태그 심각 코드를 포위보고를? ''? – Motti

+0

이 삭제되었습니다. 내가 왜 그때에 내 마음을 사용하지 않고 있었는지 다른 곳에서 처음으로 왜 거기에 있었는지에 대한 설명이 없습니다. : p – chedi

답변

5

static vector<unsigned> _data;은 sp_enum 인스턴스화에서 동일한 매개 변수가있는 템플릿 클래스의 인스턴스간에 만 공유됩니다.

+0

예, 당신 말이 맞아, 지금 내가 무엇을 놓쳤는 지 안다. 감사합니다. – chedi

0

파벨 덕분에 문제를 발견했습니다. 여기

는 일반 노트에 솔루션

 

#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

+0

@ 프랭크 : 나는 그것을 고마워했습니다. – chedi

0

이며, 여기에 주요 문제는 당신이 당신의 vector 열거 당의 인스턴스 필요가 있다는 것이다 (그렇지, 논리를?) .

예를 들어 2 개의 열거 형을 사용하는 경우 정정이 작동하지 않습니다. 열거에 따라 클래스를 구별하기 위해

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

:

그래서 할 수있는 논리적 인 것은의 라인을 따라 뭔가 될 것이다 :로

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

그리고 다음 열거 클래스를 수정 우리가 얘기하고 있습니다.

다음 질문 : t에서 unsigned을 어떻게 얻습니까? 다음 코드를 하드 코딩하는 것은 좋지 않습니다. p

+0

추가 문제는 처음부터 정적 변수의 값을 설정하는 인스턴스를 만드는 것 (두 개 이상 만들고 다시 불면)이 필요하다고 생각합니다. 'initializer_list'를 사용하면 더 편리 할 수 ​​있기 때문에 가변성 템플릿이 아무 것도 추가하지 않을 수 있습니까? – UncleBens

+0

아마 맞을 수도 있지만 예제가 완전한 예제는 아닙니다. 난 variadic 템플릿이 가지고있는 가능성을 연구하고 있습니다. 나는 나의 목표에 대한 더 나은 설명과 함께보다 완전한 코드를 제출할 것이다. 어쨌든 고마워요. – chedi

1

이 질문은 엉망이되었습니다.

다음과 같은 경우가 발생 했습니까? 미안 해요 부스트의 전처리 도서관 모르겠지만 특히 필요하지 않습니다. 유일한 문제는 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이 아닌 경우) 및 마지막 값을 저장하면 나머지는 모두 계산할 수 있습니다. 데이터 메서드는 boost의 범위 인 counting_iterator을 반환 할 수 있습니다.

하거나 해당 특성 클래스 전문 : 나는 오타의 몇 가지를 고정 할 때

#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 형식의 열거 형 (열거 형)을 사용하고 있으며 만족 스럽습니다. 강하게 입력 된 열거 형을 사용하는 코드의 테스트 단위를 작성하는 것은 컨테이너에서 이러한 열거 형을 캡슐화하는 일반적인 방법을 찾아야 만 무작위로 얻을 수 있으므로 테스트 단위를 작성하는 것입니다. 또한 새로운 variadic 템플릿을 사용해보고 싶었으므로 SO에서 매우 지저분한 게시물을 얻습니다. – chedi

+0

모든 항목은 범위가 지정된 컨테이너 클래스에서 열거 형을 사용하지 않고 다음을 통해 선언 된 기존 강하게 입력 된 열거 형에 대한 컨테이너를 갖습니다. enum class XXX {....}; 가변 매크로 및 부스트 전처리기를 사용하면 컨테이너 클래스의 선언을 단순화 할 수 있습니다. – chedi

+0

또한 강력한 열거 형을 정의한 스 니펫입니다. 클래스 내에서 같은 값을 가진 다른 열거 형이 있으므로 열거 형 이름을 지정하지 않아도 식별자에 액세스 할 수 있습니다. - 요소가 임의의 비 연속 값을 갖는 enum에 대해 무엇을 할 것인지 여전히 흥미로울 것입니다. – UncleBens