makar makar - 3 months ago 8
C++ Question

How can you statically check that a type T exists in a variadic template parameter list

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");

Answer

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.