I have a class that does some capability handling before calling a certain system call. This is done with a variadic template function:
class PrivilegesLinux
{
private:
//! Prevent type deduction on template parameter.
template<typename T> struct NoDeduce { typedef T type; };
public:
template<typename... PAR>
static int syscallProxy(cap_value_t capability,
int(*syscall)(PAR...), typename NoDeduce<PAR>::type... params)
{
int rc = 0;
// ... acquire capabilities
// Forward to system call
rc = (*syscall)(params...);
// ... drop capabilities
return rc;
}
//...
};
This works nice on "normal" system calls. The system call's signature is deduced from the template arguments. The typename NoDeduce<PAR>::type... params
forces a non-deduced context for the actual parameters. This might be replaced by std::type_identity once I have a compiler supporting C++20. Here is an example that uses this function to acquire the privileges for a kill
system call:
PrivilegesLinux::syscallProxy(CAP_KILL, kill, 1, SIGTERM);
However, as soon as I try to use a system call that has an ...
in its signature (e.g. ioctl
), I get a compiler error (here I try to change the MTU size):
PrivilegesLinux::syscallProxy(CAP_NET_ADMIN, ioctl, nSocketID, SIOCSIFMTU, (char *)&ifr);
/*
error: no matching function for call to 'PrivilegesLinux::syscallProxy(int, int (&)(int, long unsigned int, ...) noexcept, const INT32&, int, char*)'
note: candidate: 'static int PrivilegesLinux::syscallProxy(cap_value_t, int (*)(PAR ...), typename PrivilegesLinux::NoDeduce<PAR>::type ...) [with PAR = {int, long unsigned int}; cap_value_t = int]'
note: candidate expects 4 arguments, 5 provided
*/
Obviously, the ellipsis as part of the parameter pack is causing this problem here. I thought I could solve this by using std::forward
:
template<typename... PAR>
static int syscallProxy(cap_value_t capability,
int(*syscall)(PAR...), typename NoDeduce<PAR>::type&&... params)
{
//...
rc = (*syscall)(std::forward<PAR>(params)...);
//...
}
However, this fails with:
error: cannot bind rvalue reference of type 'PrivilegesLinux::NoDeduce<long unsigned int>::type&&' {aka 'long unsigned int&&'} to lvalue of type 'pthread_t' {aka 'long unsigned int'}
Does anyone have a suggestion to make this work with a single template function? My current workaround is to define another template:
template<typename... SYSCALL_PAR, typename... PAR>
static int syscallProxy(cap_value_t capability,
int(*syscall)(SYSCALL_PAR..., ...), PAR... params)
{
// copy of the code above
}
What about reversing the deduction: a generic type for the function (avoiding deducing the types of the arguments) and deducing the arguments (maybe adding forwarding)?
I mean... something as
template <typename Func, typename... PARS>
static int syscallProxy (cap_value_t capability,
Func syscall, PARS && ... params)
{
int rc = 0;
// ... acquire capabilities
// Forward to system call
rc = syscall(std::forward<PARS>(params)...);
// ... drop capabilities
return rc;
}