Search code examples
c++templatestemplate-meta-programming

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;
}

Solution

  • 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