Home > other >  How can I write a variadic function that in compile time translates IDs to variables and calls anoth
How can I write a variadic function that in compile time translates IDs to variables and calls anoth

Time:12-21

I have a function mapping from some IDs to variables like so:

enum CID
{
    A, B
};

struct ComponentA{};
struct ComponentB{};

ComponentA ca;
ComponentB cb;

template <uint32_t id>
constexpr auto & componentForId()
{
    if constexpr (id == A)
        return ca;
    if constexpr (id == B)
        return cb;
}

And then I have a given target function that I want to call:

void someFunction(ComponentA&, ComponentB&)
{ ... }

But I want to call this function indirectly using only numeric identifiers that are translated using the componentForId function. I imagine the function skeleton would be something like this:

template <typename ...Args>
void callIndirectly(Args&&... args)
{
    // How to implement this function so that it in effect calls 
    //      someFunction(componentForId<A>(), componentForId<B>());
}

So that I can call it like so:

callIndirectly(A, B);

And have that be equivalant to

someFunction(componentForId<A>(), componentForId<B>());

I have tried all sorts of variations, but I just can't figure out how to do the parameter packing correctly to work in this case. How can I implement callIndirectly here?

https://godbolt.org/z/sxdxeojf3

Full example:

#include <cstdint>

enum CID
{
    A, B
};

struct ComponentA{};
struct ComponentB{};

ComponentA ca;
ComponentB cb;

template <uint32_t id>
constexpr auto & componentForId()
{
    if constexpr (id == A)
        return ca;
    if constexpr (id == B)
        return cb;
}

void someFunction(ComponentA&, ComponentB&)
{}

template <typename ...Args>
void callIndirectly(Args&&... args)
{
    // How to implement this function so that it in effect calls 
    //      someFunction(componentForId<A>(), componentForId<B>());
}

int main() {
    someFunction(ca, cb);
    someFunction(componentForId<A>(), componentForId<B>());

    callIndirectly(A, B);
}

CodePudding user response:

Your call callIndirectly() with run-time arguments. That you can only convert to compile-time arguments using a switch-case (or if-else if) based delegation parameter-by-parameter, calling the next converter (and ultimately, the visitor) using cps-like construct:

// base case for visitNext
template<CID... PrevArgs>
auto visitNext() {
    someFunction(ComponentForId<PrevArgs>()...);
}

// assuming arg, args... as arguments, PrevArgs... as compile-time arguments
template<CID... PrevArgs, typename... Args>
auto visitNext(CID arg, Args... args) {
    switch(arg) {
    case 'A': return visitNext<PrevArgs..., A>(args...);
    case 'B': return visitNext<PrevArgs..., B>(args...);
    }
}

However, based on how you call it, you likely don't need all this. Either you can directly call using template value arguments (which is the base case here), or you might make use of std::integral_constant<>. The latter encapsulates the integral value in the type (i.e., std::integral_constant<CID, A> and std::integral_constant<CID, B> are distinct types), but can be converted both to compile-time and runtime values.

  • Related