用于丑陋的C结构分配的现代C++模式

Bri*_*and 6 c c++11

我正在ioctl从C++调用一个我不拥有/维护的驱动程序,并且我正在尝试理清是否有一个干净的"安全"机制来处理所需的一些丑陋的结构分配.

涉及一些结构的细长版本

// IOCTL expects an instance of this structure "first"
typedef struct {
   int param1;
   int param2;
} s_ioctl_request;

//... followed by an instance of this.  If attr_length
// is > sizeof(s_attr_header), more data is allowed to follow.
typedef struct {
   uint32_t attr_length;
   uint32_t attr_type;
} s_attr_header;

// Example that uses more data than just the header.
typedef struct {
   s_attr_header   hdr;
   uint32_t attr_param;
} s_attr_type1;

// Another example.
typedef struct {
   s_attr_header   hdr;
   uint32_t attr_param1;
   uint32_t attr_param2;
} s_attr_type2;
Run Code Online (Sandbox Code Playgroud)

ioctl要求s_ioctl_request紧跟一个s_attr_header或包含它的其他结构,其中attr_length设置为外部结构的大小(以字节为单位).

C,为它写一个包装器ioctl将通过这些方面的东西来完成:

int do_ugly_ioctl(int fd, int p1, int p2, s_attr_header * attr)
{  
   int res;      
   // Allocate enough memory for both structures.
   s_ioctl_request *req = malloc( sizeof(*req) + attr->hdr.attr_length );

   // Copy the 2nd, (variable length) structure after the first.
   memcpy( ((char*)req) + sizeof(*req), attr, attr->hdr.attr_length);

   // Modify params as necessary
   req->param1 = p1;
   req->param2 = p2;

   // Make the driver call, free mem, and return result.
   res = ioctl(fd, SOME_IOCTL_ID, req);
   free(req);
   return res;
}

// Example invocation.
s_attr_type1 a1;
a1.hdr.attr_length = sizeof(a1);
a1.hdr.attr_type   = 1;
do_ugly_ioctl(fd, 10, 20, &a1);
Run Code Online (Sandbox Code Playgroud)

我想到的几个选项是:

  1. 抛出现代C++ - 窗外的主义,并完全按照我上面所示的那样做.

  2. 使用std :: vector分配存储,然后使用生成的std :: vector :: data()指针进行丑陋的转换,这样至少我没有做new[]/ delete[]malloc/ free.

  3. 为每个s_attr_type*使用自己的"特殊"结构的人创建一个独特的包装器方法.这似乎是"最安全的",即包装方法的用户最不可能将其搞砸.和奖励积分,允许pass-by-ref.

方法#3示例:

int do_ugly_ioctl(fd, int param1, int param2, s_attr_type2& attr){
  struct RequestData {
      s_ioctl_request ioreq;
      s_attr_type2    attr;
  };
  RequestData r;
  r.ioreq.param1 = param1;
  r.ioreq.param2 = param2;
  r.attr         = attr;
  r.attr.hdr.attr_length = sizeof(attr); // Might as well enforce this here.
  ioctl(fd, SOME_IOCTL_ID, (void*) &r);
}
Run Code Online (Sandbox Code Playgroud)

所以我想这里的一些问题是:

  • 它对C++来说是否"值得" - 这个问题的解决方案是什么?(而不是依赖于更容易出错的C impl).

  • 如果我使用方法#3或类似方法,是否有任何我可以<type_traits>用来制作此函数的模板并且只接受带有s_attr_header第一个成员的结构?

  • 还有其他好主意吗?

dau*_*ama 3

完全值得,而且你的解决方案非常好。您可能希望将结构声明为packed(有编译器扩展可以实现此目的),以避免在组合多个结构时出现额外的填充。

您还可以在构造函数中设置结构的大小。

struct RequestData
{
      RequestData() : ioreq{}, attr{}
      {
        attr.hdr.attr_length =  sizeof(attr);
      }
      s_ioctl_request ioreq;
      s_attr_type2    attr;
 };
Run Code Online (Sandbox Code Playgroud)

关于你的第二个问题,你可以将作业分成两部分,这不太好,但很容易,如果你传递的内容没有正确的标头,则会导致编译器错误:

template<typename Attr>
int do_ugly_ioctl(fd, int param1, int param2, Attr& attr){
  struct RequestData {
      s_ioctl_request ioreq;
      Attr    attr;
  };
  RequestData r;
  r.ioreq.param1 = param1;
  r.ioreq.param2 = param2;
  s_attr_header hdr = Attr.hdr; //this will lead to compilation error if the type is not what we expect
  (void) hdr;
  r.attr     = attr;
  r.attr.hdr.attr_length = sizeof(attr); // Might as well enforce this here.
  ioctl(fd, SOME_IOCTL_ID, (void*) &r);
}
Run Code Online (Sandbox Code Playgroud)