I'm using a heavily templated library with a function that has multiple template arguments. I'd like to create a dynamic wrapper for this function by creating a class whose members will be the arguments to this templated function. I stumbled across std::visit, which requires a Visitor object, but it seems quite painful to write a visitor helper class to wrap the function, even when there are only a handful of possible template classes in consideration. Is there a less painful way to generate the Visitor?
Example with a manually implemented visitor helper:
#include <iostream>
#include <variant>
namespace NotMyLibrary
{
class A
{
public:
void execute()
{
std::cout << "From A!" << std::endl;
}
};
class B
{
public:
void execute()
{
std::cout << "From B!" << std::endl;
}
};
class C
{
public:
void apply()
{
std::cout << "From C!" << std::endl;
}
};
class D
{
public:
void apply()
{
std::cout << "From D!" << std::endl;
}
};
template<typename AorB, typename CorD>
void library_function_with_static_dependencies(AorB f, CorD g)
{
size_t num_iterations = 1; //but really 1'000'000+
for(size_t i=0; i < num_iterations; ++i)
{
f.execute();
g.apply();
std::cout << std::endl;
}
}
} //end namespace NotMyLibrary
class VisitHelper
{
public:
void operator()(NotMyLibrary::A f, NotMyLibrary::C g) {NotMyLibrary::library_function_with_static_dependencies(f,g);}
void operator()(NotMyLibrary::A f, NotMyLibrary::D g) {NotMyLibrary::library_function_with_static_dependencies(f,g);}
void operator()(NotMyLibrary::B f, NotMyLibrary::C g) {NotMyLibrary::library_function_with_static_dependencies(f,g);}
void operator()(NotMyLibrary::B f, NotMyLibrary::D g) {NotMyLibrary::library_function_with_static_dependencies(f,g);}
};
class DynamicFunctionExecutor
{
public:
std::variant<NotMyLibrary::A, NotMyLibrary::B> f;
std::variant<NotMyLibrary::C, NotMyLibrary::D> g;
void dispatch()
{
VisitHelper vh{};
std::visit(vh, f, g);
}
};
int main()
{
using namespace NotMyLibrary;
DynamicFunctionExecutor e;
e.f = A();
e.g = C();
e.dispatch();
e.f = B();
e.g = D();
e.dispatch();
}
You can make
operator()a template:Which you can further replace with a generic lambda:
... Which is the same as creating an object of a class type with a templated
operator(), likeVisitHelper, and passing it tostd::visit{}.Most of the time you use
std::visityou should use a generic lambda. Usually, you wantconst auto&to prevent copies.