0

I'm having problems creating a push_back function for an std::vector of std::variants. This is a minimal implementation of my struct so far:

template<class T, class Ts...> //Must construct with at least 1 argument
struct entity_container
{
  entity_container(T arg1, Ts ...args) //constructor, populate vector of std::variant's
  {
    entities.push_back(std::forward<decltype(arg1)>(arg1));

    auto loop = [&](auto&& arg)
    {
      entities.push_back(std::forward<decltype(arg)>(arg));
    }
    (loop(args),...);
  }
  
  std::vector<std::variant<T, Ts...>> entities;
}

Now suppose I want to add a push_back function then there are some things I considered:

  • if we are adding a type which already exists in the std::variant of entity_container::entities then we can simply use: entities.push_back(entity)

  • if we are adding a new type to entity_container::entities then we need to extended the type of the std::variant, then create a new std::vector of said type and move all the elements from the old vector to the new one.

for this second case, I came across two posts:

extending a variant type in c++ and copy/move elements between two std::variants

So using these tools this is what I wanted to do for my push_back implementation:

template<typename E>
void push_back(E&& entity)
{
  if( /* E is a new type to the variant */)
  {
    //std::variant<Old..., New>  =  std::variant<Old...> + New
    using extended_type = t_variant_cat_t<std::remove_reference_t<decltype(Entities[0])>, 
                                          std::remove_reference_t<E>>;

    //construct (with the new element) a new entity_container with the extended type, 
    entity_container<extended_type> entities_new(entity);

    //move the entities from the old container to the new one
    for(auto& ent : Entities)
      entities_new.entities.push_back(std::move(ent));    //all entities are moveable types

    //update replace the old container with the new
    entities = entities_new;
  }
  else /* E is not a new type to the variant */
  {
    entities.push_back(entity);
  }
}

But there are many, many problems with this.

  1. How can we check that the type E is a new type to the variant?

  2. extended_type would be something along the lines of std::variant<T1, T2, T3, T4> but in reality we have to construct an entity_container as entity_container<T1, T2, T3, T4> - how can we convert from an std::variant to a type pack?

  3. when we do entities = entities_new at the end, these two objects have different types and therefore cannot be assigned to one another (and that's even if (2) could be fixed)

  4. Finally, we have to reconstruct the container every single time an element is pushed back. That's gonna be really slow.

So my overall question is, does anyone have any ideas to fix the above problems, or perhaps a whole new approach to writing this function.

Side Note, the structure is used as so:

template<class T>
struct Entity{};

Entity<A> entity_a;
Entity<B> entity_b;
Entity<C> entity_c;

entity_container c(entity_a, entity_b);

c.push_back(entity_c);
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
jf192210
  • 127
  • 7
  • This is setting off a lot of alarm bells for me. Mainly due to the awkward mixture of runtime and compile-time concepts. If you want this to compile at all, you will either need all potential types to be known up-front, some form of type erasure, or (as you mentioned in #4) you have to copy the entire container into a potentially new type every time an element is added. – 0x5453 Sep 23 '20 at 16:18
  • For an alternative approach, consider the `object_t` being put into a `Table` in my answer to a previous question: https://stackoverflow.com/a/63743699/4641116 – Eljay Sep 23 '20 at 16:26
  • @0x5453 yeah, im starting to think this approach is a dead end. Even if it worked, it'd be slow – jf192210 Sep 23 '20 at 16:46
  • @jf192210 Since `Entity` is a template, presumably you know all of the potential contained types (`A`, `B`, `C`, etc) at compile time? `std::vector>` might be all you need. – 0x5453 Sep 23 '20 at 17:33
  • well, it would be `std::vector, Entity, Entity>>`, and the thing is, thats fine but if you were to `push_back` an `Entity` then you need to extend the type of the variant, and we see the problems i described in my post – jf192210 Sep 23 '20 at 17:42

0 Answers0