Modern C ++ template for ugly C structure highlighting

I make an ioctl call from C ++ to a driver that I don’t own / don't support, and I'm trying to figure out if there is a clean, "safe" mechanism for working with some of which requires an ugly distribution of the structure.

Broken version of some involved structures

 // 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; 

s_ioctl_request immediately followed by s_attr_header or another structure containing it, where attr_length is the size of the external structure in bytes.

In C , to write a wrapper for ioctl , this will be done through something in these lines:

 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); 

A couple of options that I think of are as follows:

  • Throw modern C ++ - is being swept out of the window and do what I showed above.

  • Allocate storage with std :: vector, then do ugly throws with the resulting pointer std :: vector :: data (), at least I don't do new[] / delete[] or malloc / free .

  • Create a unique wrapper method for each s_attr_type* , which uses its own "special" structure. This seems to be the β€œsafest,” that is, the least likely for the user to wrap the method to ruin it. And bonus points, allows you to skip the link.

Example of method No. 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); } 

So, I think some questions are here:

  • Is it "worth it" for C ++ to solve the problem? (as opposed to relying on a more error-prone C impl).

  • If I go with method # 3 or similar, is there anything I can do with <type_traits> to create a template for this function and only accept structs with s_attr_header as the first member?

  • Any other brilliant ideas?

+6
source share
1 answer

All in all, it's worth it, and your decision is pretty nice. You might want to declare your structures as packed (there are extensions for the compiler for this) to avoid additional additions when combining multiple structures.

You can also set the size of the structure inside the constructor.

 struct RequestData { RequestData() : ioreq{}, attr{} { attr.hdr.attr_length = sizeof(attr); } s_ioctl_request ioreq; s_attr_type2 attr; }; 

regarding your second question, you can divide the assignment into two parts, this is not too pleasant, but it is easy, and if you pass something without the correct header, this will lead to a compiler error:

 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); } 
+3
source

All Articles