Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile-time generating an array of non-constexpr objects that should be created in constructor

U have made an example of the structure, it looks like this:

#include <array>
enum class ItemType
{
  Tree,
  Bush,
  Flower,
  Grass,
  Mushroom,
  CountOfTypes
};

class Items;

class Item
{
public:
  Item(Items* parentContainer, ItemType type) : parentContainer(parentContainer), type(type) { DoSomething(); }
  ItemType type;
  // Parent container needs to be acessed occasionally
  Items* parentContainer;
  // This cannot be constexpr
  void DoSomething();
};

class Items
{
public:
  using ItemArray = std::array<Item, ItemType::CountOfTypes>;
  // Should contain one item per type, at index equal to that type
  ItemArray items;
};

Note the requirement on contents of the array, this must always be true for items array:

ItemType someType = ... any of item type enum except of CountOfTypes ...;
/// must be true
items[(size_t)someType].type == someType;

This code will not compile, because Item has no default constructor, yet we try to allocate it in Items::Items. Even if it did compile, the values will be uninitialised and we don't want that.

One option to solve it is manually enumerating the types in correct order:

class Items
{
public:
  Item items[(size_t)ItemType::CountOfTypes] = { {this, ItemType::Tree}, {this, ItemType::Bush}, {this, ItemType::Grass}, {this, ItemType::Mushroom} };
};

Another option would be to add a default constructor to Item and then call some initializing function. I don't like that either.

But I have used std::make_integer_sequence in the past a few times and I'd like to try to do it again. If the code above is valid, there should be some sequence that generate it. Thus, I tried this:

class Items
{
public:
  using ItemArray = std::array<Item, (size_t)ItemType::CountOfTypes>;
  template <int... Indices>
  static ItemArray GenerateItems(std::integer_sequence<int, Indices...>, Items* parent) { return { ({parent, Indices})... }; }

  ItemArray items = GenerateItems(std::make_integer_sequence<int, static_cast<int>(ItemType::CountOfTypes)>{}, this);
};

This is based on my previous usage of this feature. I am not sure if it's even the correct approach.

The error I get in visual studio is:

1>o:\projects\cpptest\cpptest\cpptest\arraywiththis.cpp(30): error C2760: syntax error: unexpected token '{', expected 'expression'
1>o:\projects\cpptest\cpptest\cpptest\arraywiththis.cpp(30): note: This diagnostic occurred in the compiler generated function 'Items::ItemArray Items::GenerateItems(std::integer_sequence<int,Indices...>,Items *)'

GCC is much more outspoken (their error messages improved unimaginably over the last 6 years):

PS O:\projects\cpptest\CPPTest\CPPTest> g++ .\ArrayWithThis.cpp
.\ArrayWithThis.cpp: In static member function 'static Items::ItemArray Items::GenerateItems(std::integer_sequence<int, Indices ...>, Items*)':
.\ArrayWithThis.cpp:30:117: error: expected ';' before '}' token
   static ItemArray GenerateItems(std::integer_sequence<int, Indices...>, Items* parent) { return { ({parent, Indices})... }; }
                                                                                                                     ^
                                                                                                                     ;
.\ArrayWithThis.cpp: In instantiation of 'static Items::ItemArray Items::GenerateItems(std::integer_sequence<int, Indices ...>, Items*) [with int ...Indices = {0, 1, 2, 3}; Items::ItemArray = std::array<Item, 4>]':
.\ArrayWithThis.cpp:32:116:   required from here
.\ArrayWithThis.cpp:30:123: error: could not convert '{((void)0, 0), ((void)0, 1), ((void)0, 2), ((void)0, 3)}' from '<brace-enclosed initializer list>' to 'Items::ItemArray' {aka 'std::array<Item, 4>'}
   static ItemArray GenerateItems(std::integer_sequence<int, Indices...>, Items* parent) { return { ({parent, Indices})... }; }

This in particular is bugging me: ((void)0, 0) Why void?

Is what I'm trying to do possible? If it is, how to fix my code?

like image 467
Tomáš Zato - Reinstate Monica Avatar asked Dec 04 '25 10:12

Tomáš Zato - Reinstate Monica


1 Answers

You have extra parentheses; it might be:

class Items
{
public:
  static constexpr std::size_t ItemCount = static_cast<std::size_t>(ItemType::CountOfTypes);
  using ItemArray = std::array<Item, ItemCount>;

  template <std::size_t ... Is>
  static ItemArray GenerateItems(std::index_sequence<Is...>, Items* parent)
  { return {{ Item{parent, Indices}... }}; }

  ItemArray items = GenerateItems(std::make_index_sequence<ItemCount>{}, this);
};

like image 147
Jarod42 Avatar answered Dec 07 '25 01:12

Jarod42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!