Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to initialize static array from std::integer_sequence?

I made an iterable generator for enums that conform with the following rules:

  • Enum is an integer sequence, there are no gaps
  • Given last element of the enum is not an actual enum element

The class looks like this:

template <typename EnumType, EnumType LENGTH>
class EnumArrayNonStatic
{
public:
  using ValueType = typename std::underlying_type<EnumType>::type;
  //! Initialize values via private constructor
  constexpr EnumArrayNonStatic() : EnumArrayNonStatic(std::make_integer_sequence<ValueType, (ValueType)LENGTH>{}) {}

  //! All values generated via std::integer_sequence
  EnumType values[(int)LENGTH];
private:
  //! Private constructor that populates values
  template <int... Indices>
  constexpr EnumArrayNonStatic(std::integer_sequence<int, Indices...>) : values{(static_cast<EnumType>(Indices))...} {}
};

Usage:

enum class TestEnum
{
  A,
  B,
  C,
  D,
  LENGTH
};
int main()
{
  for (const TestEnum val : EnumArrayNonStatic<TestEnum, TestEnum::LENGTH>().values)
  {
    std::cout << (int)val << "\n";
  }
  return 0;
}

However, I would instead like to be able to use EnumArray<TestEnum, TestEnum::LENGTH>::values and have the values generated via template during compilation. I wrote this:

template <typename EnumType, EnumType LENGTH>
class EnumArray
{
private:
  using ValueType = typename std::underlying_type<EnumType>::type;
  //! Static generator of value array (returns EnumType[])
  template <ValueType... Indices>
  static constexpr auto GenerateArray(std::integer_sequence<ValueType, Indices...>) { return {(static_cast<EnumType>(Indices))...}; }
public:
  //! Static array of values of an enum
  static constexpr EnumType values[static_cast<ValueType>(LENGTH)] = GenerateArray(std::make_integer_sequence<ValueType, static_cast<ValueType>(LENGTH) >{});
};

I've been messing around with the code for a while but I always keep getting errors. The version above prints:

1>enumiteratortest.cpp(22): error C3108: cannot deduce a type as an initializer list is not an expression
1>enumiteratortest.cpp(25): note: see reference to function template instantiation 'auto EnumArray<TestEnum,TestEnum::LENGTH>::GenerateArray<0,1,2,3>(std::integer_sequence<int,0,1,2,3>)' being compiled
1>enumiteratortest.cpp(52): note: see reference to class template instantiation 'EnumArray<TestEnum,TestEnum::LENGTH>' being compiled
1>enumiteratortest.cpp(22): error C2440: 'return': cannot convert from 'initializer list' to 'auto'
1>enumiteratortest.cpp(22): note: There is no context in which this conversion is possible
1>enumiteratortest.cpp(25): error C2440: 'initializing': cannot convert from 'void' to 'const EnumType [4]'
1>        with
1>        [
1>            EnumType=TestEnum
1>        ]
1>enumiteratortest.cpp(25): note: There are no conversions to array types, although there are conversions to references or pointers to arrays

Surely there must be a way to initialize the array statically. Is the GenerateArray even necessary? Isn't there a way to do this?

int myArray[] = std::integer_sequence<ValueType, Indices...>{Indices...}

Or something along the lines?

like image 652
Tomáš Zato - Reinstate Monica Avatar asked Dec 31 '22 17:12

Tomáš Zato - Reinstate Monica


1 Answers

You cannot initialize a language array with an initializer_list. And, you cannot change the return type of that function to an array - functions cannot return arrays.

Just change everything to std::array:

template <ValueType... Indices>
static constexpr auto GenerateArray(std::integer_sequence<ValueType, Indices...>)
    -> std::array<EnumType, sizeof...(Indices)>
{
    return {(static_cast<EnumType>(Indices))...};
}

static constexpr std::array<EnumType,  static_cast<ValueType>(LENGTH)> values
      = GenerateArray(std::make_integer_sequence<ValueType, static_cast<ValueType>(LENGTH)>{});
like image 137
Barry Avatar answered Jan 09 '23 08:01

Barry