I would like to improve the following runtime template selection code:
We have 4 functors for p-norm calculation (1 general case, 3 specialized cases). During runtime, we can select the best one.
template <typename T>
struct p_norm { // contains functors to calculate p-norm
struct general { ... }; // general case
struct one { ... }; // specialize for p=1
struct two { ... }; // specialize for p=2
struct inf { ... }; // specialize for p=inf
};
For example, we can get the distance based on p,
auto impl_fptr = distance_fn<T, p_norm<T>::general>;
if (p == 1.0) {
impl_fptr = distance_fn<T, p_norm<T>::one>;
} else if (p == 2.0) {
impl_fptr = distance_fn<T, p_norm<T>::two>;
} else if (std::isinf(p)) {
impl_fptr = distance_fn<T, p_norm<T>::inf>;
}
impl_fptr(vec1, vec2);
or maybe get the angle based on p,
auto impl_fptr = angle_fn<T, p_norm<T>::general>;
if (p == 1.0) {
impl_fptr = angle_fn<T, p_norm<T>::one>;
} else if (p == 2.0) {
impl_fptr = angle_fn<T, p_norm<T>::two>;
} else if (std::isinf(p)) {
impl_fptr = angle_fn<T, p_norm<T>::inf>;
}
impl_fptr(vec1, vec2);
The problem is that there are a lot of code duplications in selecting a suitable template case. I have no clue on how to improve the type selection.
OTOH, the minor problem is that I would like to implement p_norm<double>
for the general case and implement p_norm<1.>, p_norm<2.>, ...
for template specialization. I know it's not how the template works, but is there any way to make the specialization more obvious? Probably via tag dispatching?
CodePudding user response:
std::variant
with tag (std::type_identity
here) might help:
template <typename T>
std::variant<std::type_identity<p_norm <T>::general>,
std::type_identity<p_norm <T>::one>,
std::type_identity<p_norm <T>::two>,
std::type_identity<p_norm <T>::inf>>
get_method_var(double d)
{
if (d == 1.0) { return std::type_identity<p_norm <T>::one>{}; }
else if (d == 2.0) { return std::type_identity<p_norm <T>::two>{}; }
else if (std::isinf(d)) { return std::type_identity<p_norm <T>::inf>{}; }
else { return std::type_identity<p_norm <T>::general>{}; }
}
and then
template <typename T>
auto distance(const auto& vec1, const auto& vec2, double p)
{
return std::visit([&](auto tag){
return distance_fn<T, typename decltype(tag)::type>(vec1, vec2);
}, get_method_var<T>(p));
}
template <typename T>
auto angle(const auto& vec1, const auto& vec2, double p)
{
return std::visit([&](auto tag){
return angle_fn<T, typename decltype(tag)::type>(vec1, vec2);
}, get_method_var<T>(p));
}