О вариационных шаблонах - PullRequest
       23

О вариационных шаблонах

3 голосов
/ 08 апреля 2010

В настоящее время я испытываю новые вариационные шаблоны 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:

Может кто-нибудь указать мне, что я тут шучу ???

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 (я не нашел другого способа подсчета количества аргументов).

Я с радостью приму любой указатель или подсказку для улучшения этого.

Ответы [ 4 ]

5 голосов
/ 08 апреля 2010

static vector<unsigned> _data; не используется совместно для экземпляров sp_enum, а только для экземпляров класса шаблона с теми же параметрами.

1 голос
/ 08 апреля 2010

Поскольку этот вопрос превратился в беспорядок.

Вы после чего-то вроде этого? Извините, я не знаю библиотеку препроцессора 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;
}

По общему признанию, дизайн мог бы быть лучше, и я не беспокоился об идее Default (она все равно ломается, если вы не хотите последовательных значений).


Другое дело, что если это поддерживает только последовательные значения, то для вектора нет никаких оснований. Просто сохраните первое (если не всегда 0) и последнее значение, и все остальное можно вычислить. Метод данных может вернуть диапазон усиления 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 голосов
/ 08 апреля 2010

В общем, основная проблема здесь в том, что вам понадобится один экземпляр вашего vector на перечисление (логика, не так ли?).

Ваша коррекция не работает, если вы используете, например, 2 перечисления.

Итак, логично было бы сделать что-то вроде:

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

А затем измените свой класс enum следующим образом:

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

чтобы дифференцировать класс в зависимости от перечисления, о котором мы говорим.

Следующий вопрос: как мы получаем unsigned из t? Плохо закодировать что: p

0 голосов
/ 08 апреля 2010

Благодаря Павлу, я разобрался с проблемой.

вот решение

<code>
#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<< ":";}
}

...