0

For example i have templated structure

template <class T>
struct Event {
    T args;
    int32_t coordinate;
};

And for example i want to store Event<int32_t> and Event<std::vector<int32_t>> in one vector. Is there any analog to generics from other languages like: std::vector<Event<?>>?

UPD for all, who need to understand what i need to do: i have many events, which have different types of arguments. For example some events may have int32_t as arg, some std::vector<int32_t>, some Event<int32_t>, etc...

thematdev
  • 3
  • 2
  • 4
    `std::vector>` with [any](https://en.cppreference.com/w/cpp/utility/any) – user2407038 Oct 02 '20 at 19:05
  • One way to do that would be to use a regular class (could be empty) as a base class for the class templates. – R Sahu Oct 02 '20 at 19:06
  • 1
    What would you do with it if it can have any type? "There is no useful universal class: a truly universal carries no semantics of its own." - Stroustrup. If your event class has an interface, store them using such interface. – Quimby Oct 02 '20 at 19:12
  • 3
    you can also use variant : ```std::vector>>>``` – AKL Oct 02 '20 at 19:13
  • BTW it is hard to understand what you want to do. Please edit your question – AKL Oct 02 '20 at 19:15
  • 2
    @user2407038, Please elaborate. `Event` cannot be used where `Event` is expected. I am not sure what you had in mind. – R Sahu Oct 02 '20 at 19:15
  • @user2407038 @AKL is there any variant for C++11? `std::any` and `std::variant` was added in C++17 – thematdev Oct 02 '20 at 19:17
  • your question is wage, do you want to have vector of events or event of vectors? – AKL Oct 02 '20 at 19:18
  • In the question for polymorphic object creator, my [answer](https://stackoverflow.com/a/63743699/4641116) has a `Table` that stores a vector of `object_t`, where they can are a homogenous polymorphic wrapper around non-homogenous objects. Maybe something like that would work for you...? – Eljay Oct 02 '20 at 19:20
  • Dear thematdev, there are number of solutions to deal with gathering different types under same banner like std::variant and std::tuple. as soon as you clarify what exactly you want to do, some one can give you an answer – AKL Oct 02 '20 at 19:24
  • @AKL I have many types of Events. `Event`, `Event`, etc. And in need to store them(for example in `std::vector`) to iterate over them or to sort them, etc. – thematdev Oct 02 '20 at 19:27
  • 3
    The classical approach to this is inherit all `Event` from a single non-template base class, then store `unique_ptr`s to that base in the vector. – HolyBlackCat Oct 02 '20 at 19:42
  • @parktomatomi To cite the OP: _"i have many events"_ `std::variant` gets a bit tedious if there are many types to support. Though it could be placed in a single `typedef` or `using` clause to make maintencance easier. – πάντα ῥεῖ Oct 02 '20 at 19:59
  • @πάνταῥεῖ that's exactly the way to go. The alternative is the visitor pattern, which I find much more tedious. – parktomatomi Oct 02 '20 at 20:01
  • @thematdev if `any` isn't available to you, your best solution will likely depend on exactly how you plan to use this type. `any` is a big hammer, and the most literal translation of `std::vector>` from other languages. Other options are `unique_ptr` or `variant` but it's hard to say which is best for you. If you really need the full power of `any`, but `any` isn't available in the version you're using, your best bet is to use a 3rd party lib which provides (boost) or to roll your own. – user2407038 Oct 02 '20 at 20:02
  • @parktomatomi The _Visitor Pattern_ is in deed the least way to go. I'd prefer _@Holy's_ solution over all the other proposals. This seems to be the most robust and least tedious in maintenance. Also you can define a homogeneous interface to handle processing the diverse event types. – πάντα ῥεῖ Oct 02 '20 at 20:05
  • This gets asked a _lot_: https://stackoverflow.com/a/59495982/1863938 – parktomatomi Oct 02 '20 at 20:13
  • @user2407038 IMO, `std::any` is a good example of _type erasure_, but in practice is just inconvenient and error prone. – Maxim Egorushkin Oct 02 '20 at 20:29
  • We get that you have different event types, but _many events_ isn't enough information. Do those events form an event queue in time order? Do you need random access or sequential access? – Maxim Egorushkin Oct 02 '20 at 21:07

1 Answers1

1

Comment by @HolyBlackCat

The classical approach to this is inherit all Event<T> from a single non-template base class, then store unique_ptrs to that base in the vector.

This is how you would do it.

#include <vector>
#include <cstdint>
#include <memory>

struct EventBase
{
   virtual ~EventBase() = default;
};

template <class T>
struct Event : public EventBase {
    T args;
    std::int32_t coordinate;
};

int main()
{
   std::vector<std::unique_ptr<EventBase>> v;
   v.emplace_back(new Event<int>());
   v.emplace_back(new Event<double>());
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • What happens if `emplace_back` throws? That can only happen here on `std::bad_alloc` or `std::out_of_range`, which are rather unlikely, but this example doesn't generalise well, IMO. – Maxim Egorushkin Oct 02 '20 at 22:01
  • 1
    Please explain how does one read back from the vector if he does not know the types – AKL Oct 02 '20 at 22:19
  • @MaximEgorushkin, it didn't occur to me that that would be an issue for such a simple program. If you have some ideas, please suggest. I, and I'm sure the OP, will appreciate that. – R Sahu Oct 03 '20 at 03:40
  • @AKL, ideally, that wouldn't be necessary. If you need to get a derived class pointer, you'll have to preform `dynamic_cast` on the underlying pointer. – R Sahu Oct 03 '20 at 03:43
  • 1
    @RSahu - @MaximEgorushkin might be hinting that you should use `push_back(std::make_unique>())` to insert, so that if there's an exception (e.g. out of memory) while adding it to the container, the `unique_ptr` destructor will clean up the allocation – parktomatomi Oct 03 '20 at 15:53
  • @RSahu, `dynamic_cast` is widely considered an anti-pattern because it requires the compiler to add RTTI information, requires run-time overhead to test, and design-wise forces you to update switch blocks everywhere you test. If you need type-specific functionality, use a virtual method to do functions without downcasting, or use the visitor pattern to separate your types and functions. – parktomatomi Oct 03 '20 at 15:56