I have a function template parametrized on a member function, that accepts an object and calls different functions based on whether the return value of that member function on the object satisfies some requirement.
Example:
template<auto member> void callerFunc(S* ps)
{
if constexpr (requires { { (ps->*member)()->f1() } -> std::same_as<int>; })
useF1((ps->*member)());
else
useF2(ps);
}
This template is only used in one place, so I'd like to convert it to a lambda and put it inside the function that uses it. However, the function arguments are not dependent on the template parameter and I do not know how to express it in terms of a lambda (or whether it is even possible).
The full example is provided below and is available on compiler explorer.
The actual code is more complex, and uses classes that are generated from protobuf files (with nested messages and oneof
fields) that I do not control, and macros that generate code in the switch
cases.
Edit:
To clarify: in the code below, setting the caller
inside the switch
and calling it outside it is important, since in the actual code the switch
is in a try
block and the call is in the corresponding catch
.
#include <concepts>
#include <iostream>
struct S1 {
int f1() { return 1; }
int f2() { return 2; }
};
struct S2 {
// doesn't provide f1(), use f2() instead
int f2() { return 2; }
};
struct S {
S1* getS1() { return &s1; }
S2* getS2() { return &s2; }
S1 s1;
S2 s2;
};
template <typename T> void useF1(T* t) { std::cout << t->f1() << "\n"; }
template <typename T> void useF2(T* t) { std::cout << t->getS2()->f2() << "\n"; }
////////////////////////////////////////////////////////////////////////////////
// How can I move this template inside Thing::foo below?
template<auto member> void callerFunc(S* ps)
{
if constexpr (requires { { (ps->*member)()->f1() } -> std::same_as<int>; })
useF1((ps->*member)());
else
useF2(ps);
}
struct Thing {
S s;
void foo(char c) {
auto caller = &callerFunc<&S::getS2>; // has to be available outside the switch
switch (c) {
case 'a': caller = &callerFunc<&S::getS1>; break;
case 'b': caller = &callerFunc<&S::getS2>; break;
}
caller(&s); // has to be outside the switch
}
};
////////////////////////////////////////////////////////////////////////////////
int main() {
Thing thing;
thing.foo('a');
thing.foo('b');
}