In my simulation engine, I have a SimulationObject template that I specialize for different type of SimulationObjects using a Tag. I also have a SimulationObjects type that lists all the SimulationObjects that I support.
struct Particle3D{};
struct RigidBody{};
template <typename Tag>
struct SimulationObject{};
struct SimulationObjects {
using value = std::tuple<SimulationObject<Particle3D>, SimulationObject<RigidBody>>;
}
I also have a bunch of Projection classes which implement a common interface. Each SimulationObject<> particular type only supports a set of those Projections, so I list them using a Projections<> template. Each SimulationObject can only hold 0 or 1 of those Projections, so I use a variant for that.
struct Projection3D{};
struct AnotherProjection3D{};
struct ProjectionRB{};
…
template <>
struct Projections<RigidBody>
{
using value = std::variant<std::monostate, ProjectionRB>;
}
Since I can have several SimulationObject<> of each type, and each one can have a projection. I want to have a tuple where I store the active projections.
struct SystemMatrix
{
template <typename Tag>
struct ProjectionBlock
{
int offset;
std::reference_wrapper<const typename Projections<Tag>::value> projection;
};
// How to write this using the SimulationObject info? I have all the possible SimulationObject types listed there
std::tuple<
std::vector<ProjectionBlock<const Projections<Particle3D>>>,
std::vector<ProjectionBlock<const Projections<RigidBody>>>> projections;
}
Is there a way to create the SystemMatrix::projections such that I don't need to list all the supported SimulationObjects types?
Disclaimer: I advise against going down the road of extravagant metaprogramming. You claim that repeating is error prone, but metaprogramming is itself error prone and the complexity introduced by metaprogramming is quite hard to justify. Chances are, the list of types aren't that long and won't be changing all that frequently. Copying and pasting works just fine most of the times.
Now, suppose you know metaprogramming is definitely the correct choice. You would want to work on a primitive that isn't std::tuple
.
template<typename... Ts>
struct typelist
{
template<template<typename...> class C>
using as = C<Ts...>;
template<template<typename> class M>
using map = typelist<M<Ts>...>;
};
Then write
using SimulationObjects = typelist<Particle3D, RigidBody>;
struct SystemMatrix
{
// ...
using P = SimulationObjects::map<ProjectionBlock>::map<std::vector>::as<std::tuple>;
P projections;
};
I should add that in your question, you wrapped the simulation object types with Projections
twice, which is most likely an error.
Alternatively, use boost MPL or Hana for heavy-weight but feature-rich metaprogramming.