使用变量模板转发Linux系统调用的代理

问题描述

我有一个类,该类在调用特定系统调用之前会进行一些功能处理。这是通过可变参数模板函数完成的:

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,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省略号:

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仍然受到限制。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...