Reputation: 504
I am trying to statically check to see if a type exists in a variadic template parameter list. However, this template list actually exists within a class that is passed a single type. The answer here shows how to check a list of parameters or a parameter pack, but I am unsure how to test a class that contains variadic templates.
For example
template <typename ...S>
class Services {};
template <typename Services>
class ServiceLocator
{
public:
template <typename T>
T& Resolve()
{
static_assert( check_t_exists_in_variadic_template_within_Services );
return Find<T>();
}
};
What could I write in this static_assert to ensure that each call to this service locator is checked and a compiler error thrown if Resolve is called with a type that does not exist in the template parameter list inside Services?
What I am specicially after is something along the lines of:
static_assert(is_any<T,Services::S...>::value, "T does not exist in Services::S");
Upvotes: 2
Views: 2247
Reputation: 1863
Here is a method using constexpr
:
#include <type_traits>
template <typename T>
constexpr bool contains() {
return false;
}
template <typename T, typename A, typename... Tail>
constexpr bool contains() {
return std::is_same<T, A>::value ? true : contains<T, Tail...>();
}
int main()
{
// usage: contains<type_you_want_to_check, type_1, type_2,...>()
static_assert(contains<float, int, double, float>(), "does contain float");
static_assert(contains<float, int, double, char>(), "does not contain float");
}
Aside from being simpler and easier to understand (IMO), this method has the advantage of being easily extensible to other needs, as you can replace the std::is_same
call with any other constexpr bool
expression, such as std::is_base_of
in order to check if the parameter pack contains any base or derived types.
Upvotes: 3
Reputation: 860
Based on François' answer, here's a shorter version that avoids usage of std::tuple
and uses std::integral_constant
(via true/false_type
) and provides a C++14-style contains_v
alias. The general idea is the same though.
template <typename T, typename... Args>
struct contains;
template <typename T>
struct contains<T> : std::false_type {};
template <typename T, typename... Args>
struct contains<T, T, Args...> : std::true_type {};
template <typename T, typename A, typename... Args>
struct contains<T, A, Args...> : contains<T, Args...> {};
template <typename T, typename... Args>
constexpr bool contains_v = contains<T, Args...>::value;
You can use it like this:
static_assert(contains_v<float, float, double>,
"failure: float not among <float, double>"); // does not trigger
static_assert(contains_v<int, float, double>,
"failure: int not among <float, double>"); // triggers
Upvotes: 2
Reputation: 790
One issue with your current code is that ServiceLocator takes a concrete type so you lose the template parameters passed to Services. To retrieve them you need to typedef it somehow so I chose std::tuple since you can't typedef the variadic list directly.
#include <tuple>
#include <type_traits>
template <typename Type, typename Collection>
struct contains;
template <typename Type>
struct contains<Type, std::tuple<>>
{
typedef std::false_type result;
};
template <typename Type, typename ... Others>
struct contains<Type, std::tuple<Type, Others...>>
{
typedef std::true_type result;
};
template <typename Type, typename First, typename ... Others>
struct contains<Type, std::tuple<First, Others...>>
{
typedef typename contains<Type, std::tuple<Others...>>::result result;
};
template <typename ... S>
struct Services
{
typedef std::tuple<S...> Collection;
};
template <typename ServicesType>
class ServiceLocator
{
public:
template <typename T>
T * Resolve()
{
static_assert(contains<T, typename ServicesType::Collection>::result::value, "Fail");
return nullptr;
}
};
class S1 {};
class S2 {};
class S3 {};
int main(int /*argc*/, char * /*argv*/[])
{
Services< S1, S2 > services;
ServiceLocator< decltype(services) > locator;
locator.Resolve< S1 >();
locator.Resolve< S2 >();
locator.Resolve< S3 >(); // triggers static_assert
return 0;
}
I only checked with clang but I hope this helps.
Upvotes: 2