1

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?

https://godbolt.org/z/3175aM4fc

5
  • 1
    Personal opinion: purpose of tuple is to make template programing easier in some contexts. Tuple with fixed types should be replaced with old fashioned struct to provide proper names for each field, so code it easier to read code and maintain. Commented Jun 10, 2024 at 9:09
  • 1
    Please edit your question and get rid of this large amount on noise. Try build minimal reproducible example. Based on what type of SystemMatrix::projections should be created? You didn't explain this. You just wrote that this type shouldn't be created manually. You need some other type which binds all interesting types together, so template could transform this type to desired tuple. Commented Jun 10, 2024 at 9:17
  • @MarekR If I list all the types without tuples, then I will have to write them in several places, again and again, and when I add a new type, I will have to write it in all those place. Is very error prone. tuple in this context makes sense Commented Jun 10, 2024 at 9:22
  • In situations like this your code probably needs one more specialization point : a user provided conversion function or functions (that can convert from one tuple/struct, to another tuple/struct). (Which probably also allows you to rewrite your code without tuples) Commented Jun 10, 2024 at 9:39
  • @PepijnKramer could you provide an example or something? Not sure I follow what you propose Commented Jun 11, 2024 at 18:49

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

6 Comments

Its not that I the typelist will change a frequently. But I have to iterate on all the possible SimulationObjects on a lot of places. That is, statically iterate on the types and iterate on the vector<SimulationObject<>>. I use the tuple to abstract that. Also, inside each SimulationObject I have a Energies tuple<vector>; a secondary type which implements another interface, but only some of Energies<> are supported by a particular SImulationObject. I think having tuples and list all this things in a header makes sense.
"Copying and pasting works just fine most of the times." how about introducing a type alias. Spelling out the name of an alias twice is not code repetition
I agree that I can use a struct with the needed vectors. But I need to iterate on all the type of SImulationObject<> that the struct would contain. That is the main reason I am using the tuple, to be able to statically iterate on the supported types and not forget any
@463035818_is_not_an_ai OP says this is not the only place to use the list of types, with each place requiring its own transformations on the list. A plain alias doesn't work.
@jjcasmar If your comments are addressing something wrong with the answer, then I don't understand them. projections here is precisely what you asked for. If you additionally want the plain list as a tuple, it's accessible as SimulationObjects::as<std::tuple>.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.