Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

C++ template runtime choices

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,

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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?

>Solution :

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(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(p));
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading