使用可变参数模板转发 Linux 系统调用的代理
Proxy to forward Linux sytem calls using variadic templates
我有一个 class 可以在调用某个系统调用之前进行一些功能处理。这是通过可变参数模板函数完成的:
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;
}
//...
};
这在“普通”系统调用上效果很好。系统调用的签名是从模板参数中推导出来的。 typename NoDeduce<PAR>::type... params
为实际参数强制使用非推导上下文。一旦我有一个支持 C++20 的编译器,这可能会被 std::type_identity 取代。下面是使用此函数获取 kill
系统调用权限的示例:
PrivilegesLinux::syscallProxy(CAP_KILL, kill, 1, SIGTERM);
但是,一旦我尝试使用签名中包含 ...
的系统调用(例如 ioctl
),我就会收到编译器错误(此处我尝试更改 MTU尺码):
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
*/
显然,作为参数包一部分的省略号导致了这里的问题。我想我可以通过使用 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)...);
//...
}
然而,这失败了:
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'}
有没有人建议使用单个模板函数来完成这项工作?
我目前的解决方法是定义另一个模板:
template<typename... SYSCALL_PAR, typename... PAR>
static int syscallProxy(cap_value_t capability,
int(*syscall)(SYSCALL_PAR..., ...), PAR... params)
{
// copy of the code above
}
如何反转推导:函数的通用类型(避免推导参数的类型)和推导参数(可能添加转发)?
我的意思是……
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;
}
或者,您可以通过添加额外的重载来显式处理 C-ellipsis:
template<typename... PAR, typename ... Ts>
static int syscallProxy(cap_value_t capability,
int(*syscall)(PAR..., ...),
typename NoDeduce<PAR>::type... params,
Ts... args) // ellipsis arguments
{
// Possibly check Ts... are "valid" type for ellipsis
int rc = 0;
// ... acquire capabilities
// Forward to system call
rc = (*syscall)(params..., args...);
// ... drop capabilities
return rc;
}
注:
int(*syscall)(PAR..., ...)
也可以写成int(*syscall)(PAR......)
Ts
是按值传递的,因为 ellipsis arguments types 无论如何都是有限的。
我有一个 class 可以在调用某个系统调用之前进行一些功能处理。这是通过可变参数模板函数完成的:
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;
}
//...
};
这在“普通”系统调用上效果很好。系统调用的签名是从模板参数中推导出来的。 typename NoDeduce<PAR>::type... params
为实际参数强制使用非推导上下文。一旦我有一个支持 C++20 的编译器,这可能会被 std::type_identity 取代。下面是使用此函数获取 kill
系统调用权限的示例:
PrivilegesLinux::syscallProxy(CAP_KILL, kill, 1, SIGTERM);
但是,一旦我尝试使用签名中包含 ...
的系统调用(例如 ioctl
),我就会收到编译器错误(此处我尝试更改 MTU尺码):
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
*/
显然,作为参数包一部分的省略号导致了这里的问题。我想我可以通过使用 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)...);
//...
}
然而,这失败了:
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'}
有没有人建议使用单个模板函数来完成这项工作? 我目前的解决方法是定义另一个模板:
template<typename... SYSCALL_PAR, typename... PAR>
static int syscallProxy(cap_value_t capability,
int(*syscall)(SYSCALL_PAR..., ...), PAR... params)
{
// copy of the code above
}
如何反转推导:函数的通用类型(避免推导参数的类型)和推导参数(可能添加转发)?
我的意思是……
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;
}
或者,您可以通过添加额外的重载来显式处理 C-ellipsis:
template<typename... PAR, typename ... Ts>
static int syscallProxy(cap_value_t capability,
int(*syscall)(PAR..., ...),
typename NoDeduce<PAR>::type... params,
Ts... args) // ellipsis arguments
{
// Possibly check Ts... are "valid" type for ellipsis
int rc = 0;
// ... acquire capabilities
// Forward to system call
rc = (*syscall)(params..., args...);
// ... drop capabilities
return rc;
}
注:
int(*syscall)(PAR..., ...)
也可以写成int(*syscall)(PAR......)
Ts
是按值传递的,因为 ellipsis arguments types 无论如何都是有限的。