How do you avoid flexible array errors when you want to extend a structure with another in C/C++?

125 views Asked by At

I found this structure definition used to handle detection of process changes in Linux:

struct __attribute__((aligned(NLMSG_ALIGNTO))) event_message {
    nlmsghdr f_nl_hdr;
    struct __attribute__((__packed__)) {
        cn_msg f_cn_msg;
        proc_event f_proc_ev;
    } f_nl_msg;
};

That worked great.

Now that I'm moving forward to newer versions of the C++ compiler (g++12.2.0 as found in Ubuntu 23.04), I get an error saying that the flexible array member __u8 data[], found in cn_msg, is not the last member as it ought to be:

/usr/include/linux/connector.h: In member function ‘void ed::process_changed::listen_for_events()’:  
/usr/include/linux/connector.h:78:14: error: flexible array member ‘cn_msg::data’ not at end of ‘struct ed::process_changed::listen_for_events()::multicast_message’  
  78 |         __u8 data[];  
     |              ^~~~

At the moment, I can't think of anything other than the following which adds a malloc():

  1. Computing the total size:

    constexpr std::size_t size(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_event));
    
  2. Creating a simple buffer of uint8_t:

    std::vector<std::uint8_t> buffer(std::vector<std::uint8_t>(size));
    
  3. And finally casting each location to the corresponding structure:

    nlmsghdr * header = reinterpret_cast<nlmsghr *>(buffer.data());
    cn_msg * msg = reinterpret_cast<cn_msg *>(header + 1);
    proc_event * event = reinterpret_cast<proc_event(msg + 1);
    

    Note: reading the reinterpret_cast<T>() documentation, I do not think that these are UB because they match (currently) case 5 which says it is exactly equivalent to static_cast<cv T2*>(static_cast<cv void*>(expression)) and that is 100% safe as long as T1* (type of expression) has the same alignment requirements as T2* (and of course the bytes at location T2* represent a structure of type T2).

But that looks rather ugly and prone to many mistakes.

What other solution would there be to handle this situation?

Also correct me if I'm wrong, it does not look like this is a warning (I do use -Werror because so many warnings are errors, but could use a #pragma to cancel this one if it were a warning).

Note 1: I will not consider redefining the structures. Using the files found in /usr/include is important to make sure the current version of the system is used.

Note 2: The library is very specific to Linux and the code is not expected to run on other systems than Ubuntu compiling with g++. So portability is not an issue outside of that.

0

There are 0 answers