I am writing some code that uses an external library, where several functions are defined approximately like this:
// Library.h
template<typename T>
void foo(int arg1, bool arg2);
template<typename T>
int bar(float arg);
(examples are given to illustrate that both argument lists and return value types are diverse, but do not contain the template type T
).
In my code, I want to be able to call different template instances offoo
and bar
, depending on some internal mapping logic. This can be e.g. a mapping from an enum representing data types, but, importantly, this logic is the same for foo
, bar
, or anything else form this library.
A simple way to achieve this would be something like
// MyCode.h
enum class MyType { BOOL, CHAR };
void foo_wrapper(MyType type, int arg1, bool arg2)
{
if (type == MyType::BOOL)
return foo<bool>(arg1, arg2);
else if (type == MyType::CHAR)
return foo<char>(arg1, arg2);
else
throw std::runtime_error("oops");
}
int bar_wrapper(MyType type, float arg)
{
if (type == MyType::BOOL)
return bar<bool>(arg);
else if (type == MyType::CHAR)
return bar<char>(arg);
else
throw std::runtime_error("oops");
}
However, this is a lot of logic duplication and correcting the arg names, etc., when it would be needed for another function, leaving plenty of possibilities for missing something. My current solution is to have a static map of relevant template instantiations in each wrapper function:
void foo_wrapper(MyType type, int arg1, bool arg2)
{
using FunctionType = std::function<void(int, bool)>;
static const std::unordered_map<MyType, FunctionType> functionMap{
{BOOL, foo<bool>},
{CHAR, foo<char>}
};
if (!functionMap.count(type))
throw std::runtime_error("oops");
return functionMap.at(type)(arg1, arg2);
}
int bar_wrapper(MyType type, float arg)
{
using FunctionType = std::function<int(float)>;
static const std::unordered_map<MyType, FunctionType> functionMap{
{BOOL, bar<bool>},
{CHAR, bar<char>}
};
if (!functionMap.count(type))
throw std::runtime_error("oops");
return functionMap.at(type)(arg);
}
Upside: Arguments are passed only in one place in code, the mapping is "centralized" at the beginning of each wrapper instead of distributed in wrapper function code. Also, less code of the choice logic is being copied around.
But: We still need to duplicate the mapping correspondencies - now in the shape of a map declaration - across multiple wrappers (imagine a dozen library functions used in this way...).
Ideally, I would like to have a magic switch_type_for_func
implemented that would allow doing something like
void foo_wrapper(MyType type, int arg1, bool arg2)
{
return switch_type_for_func<foo>(type, arg1, arg2);
}
int bar_wrapper(MyType type, float arg)
{
return switch_type_for_func<bar>(type, arg);
}
I see that this cannot work because foo
is a template, but it intuitively feels as if there should be some solution that would eliminate code duplication in this case.
I can almost imagine a macros doing the job (because what I need is just the name of the function, not much more), but AFAIU these are not exactly best practice... Maybe I am just stuck in my way of thinking about it and there is something more appropriate. Any feedback/advice is appreciated!