C++11 initializer lists can be used to initialize vectors and arrays with argument passing to constructors.
I have a piece of code below where I would like to initialize such an array with all the enumerations of eCOLORS
from eCOLORS::First
to eCOLORS::Last
using initializer lists.
Since all information is known at compile time, I think there is a way to solve this problem.
enum class eCOLORS
{
kBLUE=0, kGREEN, kRED, kPURPLE,
First=kBLUE, Last=kPURPLE
};
template< typename E >
size_t Size()
{
return (size_t)(E::Last) - (size_t)(E::First) + 1;
}
struct Foo
{
Foo( eCOLORS color ) { }
};
int main(int argc, char** argv)
{
Foo a[2] = {
{ eCOLORS::kRED },
{ eCOLORS::kGREEN }
}; // works, but requires manual maintenance if I add another color
/* how to feed v with all the enums from First to Last
without hard-coding?
Foo v[Size<eCOLORS>()] = {
};
*/
}
The consensus appears to be that there is currently no way to this.
My original intent in asking this question is, I want to automagically create an array of Foo objects whose initialization is solely based on the enumeration of
eColors
. I wanted a no maintenance solution that would work even after you add more entries intoeColors
.
Using the Enum class from this earlier post, I can write a function template that gives me the functionality that I need. Even without using that Enum class, you could still loop from eCOLORS::First
to eCOLORS::Last
, along with some ugly casts.
My ugly pseudo-answer is kludgy (nowhere as nice as a compile-time initializer list), but at least it is zero maintenance.
NOTE: if better solutions come up, I will update the OP accordingly.
template <typename T, typename E>
std::vector< T >
Initialize_With_Enums()
{
std::vector< T > v;
for( auto p : Enum<E>() )
v.push_back( T( p ));
return v;
}
int main( int argc, char** argv )
{
auto w = Initialize_With_Enum<Foo,eCOLORS>();
}
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With