2

I'm working on a network program that will deal with packets having a header portion and a payload portion. The header is variable-length, with extension segments added or removed depending on network conditions. The user data portion is a plain byte buffer, although it may be treated as some sort of structure or other during packet construction or use.

I would like to have a base class that builds & manipulates the header, with the header bytes (possibly) shifted forwards or backwards if necessary to insert an extension field, and a derived class containing the user data. For example:

class hdr {
    public:
        const void *start( void ) { return uint8_t(pBuf) + sizeof(pBuf) - SIZEOF_HEADER - pExtBytes; }
    private:
        size_t   pExtBytes;
        uint32_t pBuf[16]; // header data goes here, shifted to the END of the buffer, adjacent the beginning of the derived class
        // NO PADDING HERE!
} ;

class payload1 : public hdr {
    public:
    private:
        uint8_t pData[128];
} ;

class payload2 : public hdr {
    public:
    private:
        uint16_t pData[12];
} ;

Is there a standard way to guarantee that there's no padding between hdr and payload1 or payload2, so that I can pass start() to write() and have the header and contiguous payload transmitted out the network? For example, if (sizeof(hdr) % BIG_STANDARD_ALIGNMENT) == 0) then derived classes of hdr will start without any extra padding?

An alternative is to use scatter/gather I/O, but that seems like a complicated setup, and I'm not sure it works to gather packet-fragments together for transmission in a single UDP packet.

Dave M.
  • 1,496
  • 1
  • 12
  • 30
  • there is no such way. Use a non-inherited struct with `char pBuf[]` as the last member and allocate it with corresponding sizes when needed. – Serge Dec 25 '18 at 19:31
  • IMHO, use a buffer, such as `uint8_t`, to transfer data. Write methods that load a `class` / `struct` from the buffer. Don't use classes or structures for direct transfer, due to padding, alignment, and vtable issues (among other possibilities). – Thomas Matthews Dec 25 '18 at 19:43
  • Thanks, @Serge. After more digging around, I think you're right: there's no language-guaranteed way to do this. (Of course, you can get it to work with any particular compiler, and maybe with a good range of compilers, but it's not required by the spec.) . So although it pains me to include `memcpy()` in the "prepare for transmission" code path, the alternative (custom assembly?) is worse. And CPUs are faster than networks these days anyway. – Dave M. Dec 26 '18 at 05:34
  • @DaveM you can use unions for array mapping. – Serge Dec 26 '18 at 15:35

0 Answers0