views:

229

answers:

5

Hi,

I'm currently experiencing with the new c++0x variadic templates, and it's quite fun, Although I have a question about the process of member instantiation.

in this example, I'm trying to emulate the strongly typed enum with the possibility of choose a random valid strong enum (this is used for unit testing).


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



the result I'm getting with this code is :

3
1
5:

can someone point me what I'm messing here ???

Ps: using gcc 4.4.3

+5  A: 

static vector<unsigned> _data; is not shared across sp_enum instantiations, only across instances of the template class with the same parameters.

Pavel Radzivilovsky
yes, you are right, now I know what I missed.thanks a lot.
chedi
A: 
chedi
Please take note that on StackOverflow, you can edit your first post and put the solution there ;)
Frank
@Frank: I didn't know that, thanks.
chedi
A: 

On a general note, the main issue here is that you will need one instance of your vector per enum (logic, isn't it ?).

Your correction does not work if you use 2 enums for example.

So the logical thing to do would be something along the line of:

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

And then modify your enum class as:

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

in order to differentiate the class depending on the enum we are talking about.

Next question: how do we get the unsigned out of t ? It's bad to hardcode that :p

Matthieu M.
I think a further problem is having to create an instance to set values of a static variable in the first place (create two or more and again it blows). Could it also be that variadic templates add nothing here, as things would be more convenient with an `initializer_list`?
UncleBens
It's probably correct, but the example was not meant to be a full example. I'm just researching the possibility that variadic templates have. I'll be submitting a more complete code with a better explanation of my goal.thanks anyway
chedi
A: 

Hi,

I have reworked the code to be more generic and to reduce the hardcore coding as possible (@Matthieu M.). But I would like to explain why I'm doing all this before.

I have, as many developer embraced the new c++0x standard in my code and I'm quit happy about it. But I have an issue with the strongly typed enums when trying to write test units.

The problem is that you cannot generate a random strong typed enum (I know, that you can, but wanted to do it in a more elegant way). So, now with this code, I can, using variadic templates and variadic macro (yes old dirty macro) declare and randomly select a strongly typed and scoped enum.

here is the code:


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

What bother me now, is the limitation of the PP_NARG macro (I haven't found any other way to count the number of argument).

I will gladly accept any pointer or hint to enhance this.

chedi
@chedi: You should try to understand the idea of SO. It is not a discussion board. There is no continuity. It's about 1 question getting answers which are presented in the order of rating. - Hence you probably should delete your own replies and ask the question separately.
UncleBens
If it can be used the wrong way, it would be a shame not to do it :p
chedi
+1  A: 

Since this question has turned into a mess.

Are you after something like this? Sorry I don't know boost's preprocessor library, but it doesn't seem to be particularly necessary for that. The only question is, does count have to be a compile-time constant?

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

Admittedly the design could be better, and I haven't bothered about the Default idea (it breaks down anyway, if you don't want consecutive values).


Another thing, if this only supports consecutive values, there is no reason for the vector in the first place. Just store the first (if not always 0) and the last value, and everything else can be computed. The data method might return a range of boost's counting_iterator.

Or just specialize a corresponding traits class:

#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';
}
UncleBens
First, I'm sorry for my poor English and communication skills. And thanks to you for the comments.My purpose is not to develop a new strongly typed enum, I'm using the c++0x strongly typed enums (enum class) in my code and I'm satisfyed with them.The problem is to write testing units for code that uses strongly typed enums, as I have to find a generic way to encapsulate those enums in a container so I can get a random one at will.Adding also the fact that I wanted to try the new variadic templates and you get a very messy post in SO.
chedi
The all point is not to use enum in a scoped container class, but to have a container for pre existing strongly typed enum declared via :enum class XXX { .... };The use of the variadic macro and boost preprocessor is to simplify the declaration of the container classes.
chedi
The snippets I have also define a strong enum. Within the classes, there's another enum with the same values, so the identifiers could be accessed without having to specify the enum name. - Still would be interesting to see what you are going to do about enums where elements have arbitrary, non-consecutive values.
UncleBens