Search code examples
c++pass-by-referencevariadic-templates

Specialize variadic template to skip non-reference parameters


I am writing a wrapper for some client-server function calls, and I need a way to set some output parameter before or after the actual call. Sample code follows:

#include <iostream>
#include <functional>
#include <string>
#include <vector>

void tryp(int a, int b, int &c, std::vector<char> ghost) { c = 7; }

void AssignRefs() {
    std::cout << "P2" << std::endl;
}

template <typename T>
void IsolateRef(T &&t) {
    t = 99;
}

template<typename T, typename ... A>
void AssignRefs(T &&t, A && ... a) {
    IsolateRef(t);
    std::cout << "P1" << std::endl;
    AssignRefs(a ...);
}

template<typename ... A>
std::function<void(A ...)> wrap(void (*fn)(A ...)) {
  return [](A ... a) { AssignRefs(a ...); };
}

int main() {
    int z = 6;
    int y = 4;
    int a = 9;
    int b = 77;
    tryp(1,4,y,std::vector<char>{'H'});
    wrap(tryp)(1,2,z,std::vector<char>{'Z'});
    std::cout << "Results here: " << y << " :: " << z << " :: " << a << " :: " << b << std::endl;
}

This of course does not compile. See Try it online! link for more information. It would work if I deleted the line "t = 99".

What I really need is a template specialization for AssignRefs or IsolateRef to skip over non-reference parameters.

The issue now is that if I omit the '&&' before the type in the function definition, all parameters would lose their reference qualifier, and the call to IsolateRef would not do anything. But with the '&&' every parameter is passed by reference to the function IsolateRef.

I want two separate functions, one to operate on non-const reference types and the other to skip over the rest of the parameters.

Is there any simple way to implement it in the above code?

Thanks a lot.


Solution

  • From your code, you might use std::forward and replace IsolateRef(T &&t) by 2 overloads, one for non-const reference, and one for const reference (No-op)

    template <typename T> void IsolateRef(T &t) { t = 99; }
    template <typename T> void IsolateRef(const T &) { /*Empty*/ }
    
    template<typename T, typename ... A>
    void AssignRefs(T &&t, A && ... a) {
        IsolateRef(std::forward<T>(t));
        std::cout << "P1" << std::endl;
        AssignRefs(std::forward<A>(a) ...);
    }
    
    template<typename ... A>
    std::function<void(A ...)> wrap(void (*fn)(A ...)) {
      return [](A ... a) { AssignRefs(std::forward<A>(a) ...); };
    }
    

    Demo