Search code examples
c++overloadingvariadic-templates

Variadic template with type checking in C++


I need to have an interface to the driver that provides a set of send() functions only of the following types:

void send(u8 a);
void send(u8 a, u8 b);
void send(u8 a, u8 b, u8 c);
void send(u8 *a, u32 size);

I want to shorten the appearance of this interface and implement it using variable templates. As you can see, it is required, firstly, to check whether the first argument is a pointer, and if so, then handle this case separately. In other cases, you must prevent entering more than 3 arguments of type u8 (unsigned char).

I'm completely confused with the use of enable_if, SFINAE, etc.

My example:

template<typename ...Args>
void send(Args... args) {
}

void send(u8 *p, u32 size) {
}

int main() {
  u8 b;

  send(1);              // OK
  send(1, 2);           // OK
  send(1, 2, 3);        // OK

  send('1', '2', 3, 4); // NOT OK: 4 arguments were passed, I need a maximum of 3

  send(&b, 10);         // NOT OK: call template 'send', not specialized 'send(u8 *p, u32 size)'
}

Is this possible?


Solution

  • Try something like this:

    void send() {} // do nothing
    
    void send(u8 *a, u32 size) {
        ...
    }
    
    template<typename... Args>
    void send(u8 arg, Args... args) {
        static_assert(sizeof...(Args) <= 2, "No more than 3 args allowed!");
        // use arg as needed, then...
        send(args...); // send the rest...
    }
    

    Alternatively:

    void send(u8 *a, u32 size) {
        ...
    }
    
    template<typename... Args>
    void send(u8 arg, Args... args) {
        static_assert(sizeof...(Args) <= 2, "No more than 3 args allowed!");
        // use arg as needed, then...
        if constexpr (sizeof...(Args) > 0) {
            send(args...); // send the rest...
        }
    }
    

    However, one gotcha with this approach is that something like send(1, &b, 10); would be accepted. It would process arg=1 and then call send(&b, 10). If you want to avoid that, you can use additional template trickery to ensure that all of the variadic parameters are the same type, see:

    Specifying one type for all arguments passed to variadic function or variadic template function w/out using array, vector, structs, etc?

    Is there a way to define a variadic number of arguments of the same type?