In a previous post I show how to check type requirements using the REQUIRES macro:

#define REQUIRES(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0

template<class T, REQUIRES(std::is_integral<T>())>
void increment(T& x)
{
    ++x;
}

However, there are times one may need to check type requirements using perhaps a member function to a class:

template<class T>
struct foo 
{
    void bar(); // Only valid when `T` is a float    
};

Or a free function:

void serialize(int n); // Only valid when int is 4 bytes.

The REQUIRES macro needs to be placed into a template parameter, so we could start my making the function a template, like this:

template<REQUIRES(sizeof(int) == 4)>
void serialize(int n);

But that doesn’t completely work. That is because the boolean clause doesn’t depend on any template parameters that are being deduced or substituted. We can fix it easily by adding another default template parameter:

template<bool PrivateBool=true, REQUIRES(PrivateBool && sizeof(int) == 4)>
void serialize(int n);

Now that does work, so lets just incorporate that into our REQUIRES macro:

#define REQUIRES(...) bool PrivateBool=true, typename std::enable_if<PrivateBool && (__VA_ARGS__), int>::type = 0

However, we are not there yet, if we try to overload on the requirements, it will fail:

template<REQUIRES(sizeof(int) == 4)>
void serialize(int n);

template<REQUIRES(sizeof(int) == 8)>
void serialize(int n);

The compiler seems to think we are redefining the same function again. So we need to add a unique type to each template overload, perhaps like this:

template<int = 0, REQUIRES(sizeof(int) == 4)>
void serialize(int n);

template<long = 0, REQUIRES(sizeof(int) == 8)>
void serialize(int n);

This works but it doesn’t scale well. Instead lets try to define a unique template type. Template parameters can also be enums, so lets define a simple enum nested in a template class:

template<long N>
struct requires_enum
{
    enum class type
    {
        none,
        all
    };
};

And then we can assign a number to each overload and define the functions like this:

template<requires_enum<0>::type = requires_enum<0>::type::none, REQUIRES(sizeof(int) == 4)>
void serialize(int n);

template<requires_enum<1>::type = requires_enum<1>::type::none, REQUIRES(sizeof(int) == 8)>
void serialize(int n);

So lets make it neater by incorporating this into the macro. We can use the __LINE__ macro to specify the integer:

#define REQUIRES(...) requires_enum<__LINE__>::type = requires_enum<__LINE__>::type::none, bool PrivateBool=true, typename std::enable_if<PrivateBool && (__VA_ARGS__), int>::type = 0

And now we can just write:

template<REQUIRES(sizeof(int) == 4)>
void serialize(int n);

template<REQUIRES(sizeof(int) == 8)>
void serialize(int n);