3

I'm working on a little compile time helper that let's me determine if a function (for now: no namespaces or class member functions) with a specific signature exists (e.g. __builtin_pop_count which is widely spread, but not guaranteed to be available on any platform).

For a fixed number of arguments, this is easily done:

template <typename ReturnType, typename ArgumentType, typename = void>
struct Exists : std::false_type // Base case
{
};

template <typename T>
using void_t = void;

template <typename T>
using return_t = decltype(foo(std::declval<T>())); // here it is hidden: foo, although this symbol is never declared!

// specialization (compiler has to pick this one if no substitution failure in return_t
template <typename ReturnType, typename ArgumentType>
struct Exists<ReturnType, ArgumentType, void_t<return_t<ArgumentType>>>
  : std::is_same<return_t<ArgumentType>, ReturnType> // check the return type
{
};

static_assert(!Exists<void, int>::value, "");
static_assert(!Exists<void, void>::value, "");
static_assert(!Exists<void, char*>::value, "");
static_assert(!Exists<int, void>::value, "");
static_assert(!Exists<int, int>::value, "");
static_assert(!Exists<int, char*>::value, "");

This compiles fine. Adding the function void foo(int) negates the first assertion, but leaves the rest intact.

Now I would like to extend this helper to support an arbitrary number of argument types.

However,

template <typename ReturnType, typename... ArgumentTypes, typename = void>

cannot work, because typename... must be at the end of the list,

template <typename ReturnType, typename = void, typename... ArgumentTypes>

on the other hand requires the following ArgumentTypes to have a default type which also is not possible.

How can I circumvent this? Can a std::tuple<ArgumentTypes...> help in any way?

1 Answer 1

5

You guessed it.
Use a pack template, and "simplify" a bit:

template <typename...> struct pack {}; // To be potentially introduced in C++1Z

template <typename, typename, typename=void>
struct Exists_impl : std::false_type {};

template <typename R, typename... Args>
struct Exists_impl<R, pack<Args...>,
  std::enable_if_t<std::is_same<decltype(foo(std::declval<Args>()...)), R>::value>>
    : std::true_type {};

template <typename R, typename... Args>
using Exists = Exists_impl<R, pack<Args...>>;

Demo.
Note that this template will never be able to find functions like void(int) via ADL, as the set of associated namespaces is empty in such cases. Those functions must be declared at the point of definition.
Also it might be feasible to use is_convertible instead of is_same for the check of the return type, depending on the use-case.

Sign up to request clarification or add additional context in comments.

3 Comments

When should one use pack over a regular tuple if they just want a type list?
@0x499602D2 A tuple is a heavy template. You should never use it in TMP unless really necessary.
If you use is_convertible, you probably want to specialize for void return type, unless you want int foo(); to be incompatible with the signature void(). Or not, if you prefer that to be blocked!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.