Rahn
Rahn

Reputation: 5405

C++ template parameter: to find out if a parameter exists in parameter list during compilation time

I have a struct Robot:

template<typename... FeatureList>
struct Robot {
    Robot() = default;
};

That it can be configured with a few features (a few structs are used as token here):

struct CanWalk {
};
struct CanNotWalk {
};
struct CanFly {
};
struct CanNotFly {
};


Robot<CanWalk, CanFly> robot_A = Robot<CanWalk, CanFly>();

Then I have no idea how to implement a bool function isConfiguredWith() within struct Robot that tells me if a feature is encoded into the struct:


template<typename... FeatureList>
template<typename Feature>
constexpr bool Robot<FeatureList...>::isConfiguredWith() {
    // how do I implement it?
    return false;
}


int main() {
    Robot<CanWalk, CanFly> robot_A = Robot<CanWalk, CanFly>();

    static_assert(robot_A.isConfiguredWith<CanWalk>());
    static_assert(robot_A.isConfiguredWith<CanFly>());

    return 0;
}

How do I loop through FeatureList to find out if Feature exist in the lists, in compilation time?

Full code:

struct CanWalk {
};
struct CanNotWalk {
};
struct CanFly {
};
struct CanNotFly {
};

template<typename... FeatureList>
struct Robot {
    Robot() = default;

    template<typename Feature>
    constexpr bool isConfiguredWith();
};

template<typename... FeatureList>
template<typename Feature>
constexpr bool Robot<FeatureList...>::isConfiguredWith() {
    return false;
}

int main() {
    Robot<CanWalk, CanFly> robot_A = Robot<CanWalk, CanFly>();

    static_assert(robot_A.isConfiguredWith<CanWalk>());
    static_assert(robot_A.isConfiguredWith<CanFly>());

    return 0;
}

Upvotes: 2

Views: 76

Answers (2)

Ted Lyngmo
Ted Lyngmo

Reputation: 117268

You could add a feature test type trait:

#include <type_traits>

template<class Feature, class... FeatureList>
struct has_feature {
    static constexpr bool value = (std::is_same_v<Feature, FeatureList> || ...);
};

template<class Feature, class... FeatureList>
inline constexpr bool has_feature_v = has_feature<Feature,FeatureList...>::value;

And use it in your Robot class like this:

template<class... FeatureList>
struct Robot {
    Robot() = default;

    template<class Feature>
    static constexpr bool isConfiguredWith() {
        return has_feature_v<Feature,FeatureList...>;
    }
};

Demo

(std::is_same_v<Feature, FeatureList> || ...) is a fold expression.

Say FeatureList... is CanWalk, CanFly. The above fold expression then "unfolds" to:

std::is_same_v<Feature, CanWalk> || std::is_same_v<Feature, CanFly>

You can also investigate these template instantiations in more detail at cppinsights.io

Upvotes: 1

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122133

You can write a trait like this:

#include <type_traits>
#include <iostream>

template<typename... FeatureList>
struct Robot {
    Robot() = default;
};

struct CanWalk {};
struct CanNotWalk {};
struct CanFly {};
struct CanNotFly {};

template <typename Feature,typename... Features>
using has_feature_impl = std::integral_constant<bool,(std::is_same_v<Feature,Features> || ...)>;

template <typename Feature,typename T> struct has_feature;
template <typename Feature,typename... Features> struct has_feature<Feature,Robot<Features...>> : has_feature_impl<Feature,Features...> {};

int main()
{
   using R1 = Robot<CanWalk,CanFly>;
   std::cout << has_feature<CanWalk,R1>::value;
   std::cout << has_feature<CanNotFly,R1>::value;
}

Output:

10

Upvotes: 1

Related Questions