C++11 initializer lists can be used to initialize vectors and arrays with argument passing to constructors.
I have a piece of code below
I like your problem. Long ago, this kind of thing used to be handled with X macros http://www.drdobbs.com/the-new-c-x-macros/184401387
I'm a c++11 newb, but after some fiddling around I've got some kind of solution (g++ 4.8.4):
enum class Symbols { FOO, BAR, BAZ, First=FOO, Last=BAZ };
I kept your Size() but added some other boilerplate to make the initialization, lower down, easier to read.
template< typename E > constexpr size_t Size() { return (size_t)(E::Last) - (size_t)(E::First) + 1; }
template< typename E > constexpr size_t as_sizet( E s ) { return (size_t)s; }
template< typename E > constexpr E operator++( E& s, int ) { return (E)(1 + (size_t)s); }
template< typename E > constexpr bool operator<=( E& a, E& b ) { return (size_t)a < (size_t)b; }
There are two bits of magic here:
Like so:
template< typename E, typename EARR >
constexpr EARR& init_array( EARR& zArr, E sym = E::First, E junk = E::Last )
{
return sym <= E::Last ? init_array( zArr, sym++, zArr[ as_sizet( sym ) ] = sym ) : zArr;
}
In the end, it comes together with:
Like so:
typedef Symbols SymbolArr[ Size<Symbols>() ];
static SymbolArr symbolArr;
SymbolArr& symbolArrRef = init_array<Symbols, SymbolArr>(symbolArr);
Edit:
The junk parameter in the recursive initialization function can be removed using:
template< typename E > constexpr E next( E& s ) { return (E)(1 + (size_t)s); }
template< typename E, typename EARR >
constexpr EARR& init_array( EARR& zArr, E sym = E::First )
{
return sym <= E::Last ? init_array( zArr, next( zArr[ as_sizet( sym ) ] = sym ) ) : zArr;
}
I don't think you can do this with initializer lists. This isn't the sort of thing they're meant for. I think you could manage a decent workaround by defining an iterator that would iterate over any enumeration that had a First
and Last
member.
But first, your definition of Size
isn't quite right...
template< typename E >
constexpr size_t Size()
{
return (size_t)(E::Last) - (size_t)(E::First) + 1;
}
Declaring it constexpr
means that it's definition is a compile time constant. So you can use it in template arguments and the like.
I don't have time right now to create the range class for you. It's somewhat complicated by the fact that enum values and integers are not interchangeable for enum classes. But it's not too hard. You might use this question "Is there a range class in C++0x (aka C++11) for use with range based for loops?" as a starting point. You basically use the vector initializer that initializes from a [begin, end) pair in conjuction with a range class like is discussed in that question.
You can do this with variadic templates and what I'm going to call the "indices trick".
typedef std::underlying_type<eCOLORS>::type underlying;
// just a type to carry around variadic pack of numbers
template <underlying...> struct indices {};
// A template to build up a pack of Count numbers from first
// third parameter is an accumulator
template <underlying First, underlying Count, typename Acc = indices<>>
struct make_indices;
// base case
template <underlying X, underlying... Acc>
struct make_indices<X, 0, indices<Acc...>> { typedef indices<Acc...> type; };
// recursive build up of the pack
template <underlying First, underlying Count, underlying... Acc>
struct make_indices<First, Count, indices<Acc...>>
: make_indices<First, Count-1, indices<First+Count-1, Acc...>> {};
size_t const max_colors = underlying(eCOLORS::Last) - underlying(eCOLORS::First)+1;
// shortcut
typedef make_indices<
underlying(eCOLORS::First),
max_colors
>::type all_eCOLORS_indices;
// takes a dummy parameter with the pack we built
template <underlying... Indices>
std::array<eCOLORS, max_colors> const& all_colors(indices<Indices...>) {
// convert each number to the enum and stick it in an static array
static std::array<eCOLORS, max_colors> const all = {
eCOLORS(Indices)...
};
return all;
}
std::array<eCOLORS, max_colors> const& all_colors() {
// create a dummy object of the indices pack type and pass it
return all_colors(all_eCOLORS_indices());
}
This assumes all the enumerators are sequential, and needs std::underlying_type
which is not supported in GCC 4.6 (will be in 4.7, but you can emulate it to a certain extent).
The MACRO solution.
#include <stdio.h>
#include <initializer_list>
#define COLORS(V,E) \
V(RED) \
V(GREEN) \
E(BLUE)
#define COMMA(V) \
V,
#define NCOMMA(V) \
V
#define SCOMMA(V) \
#V,
#define SNCOMMA(E) \
#E
enum Colors {
COLORS(COMMA,NCOMMA)
};
const char * colors[] = {
COLORS(SCOMMA,SNCOMMA)
};
#define INIT_LIST(V) \
{ V(COMMA,NCOMMA) }
int main(int argc, char **argv) {
for ( auto i : INIT_LIST(COLORS) ) {
printf("%s\n", colors[i]);
}
}
There isn't an automatic way to do this using initializer lists, but you could do it algorithmically if you know the first and last values of the enum by just using a for
loop to insert the values.