Home > OS >  C template runtime choices
C template runtime choices

Time:09-07

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));
}
  • Related