Search code examples
c++arraysc++11castingstd

How to cast struct of std:array<char> to single std::array<char>


I'm packing messages to be send to another device. I'm using C++11. Usually the payload is a single ASCII encoded value, but this one command has a bit more parameters. I'm trying to pack the payload in a single array of chars. I'm looking for a clean solution, and I thought that the following (example) solution should work:

Message foo(Parameters parameters) {
    struct __attribute__((__packed__)) ParameterPayload {
        std::array<char,2> a;
        std::array<char,2> b;
        std::array<char,2> c; 
        std::array<char,4> d;
    }; // Actual struct has way more parameters
    ParameterPayload paramPayload;
    paramPayload.a = bar<2,10>(parameters.a);
    paramPayload.b = bar<2,10>(parameters.b);
    paramPayload.c = bar<2,10>(parameters.c);
    paramPayload.d = bar<4,16>(parameters.d);
    
    // This will not work, but I want something like this to work
    auto payload =  reinterpreted_cast<std::array<char, sizeof(ParameterPayload)>(paramPayload);

    return baz<sizeof(ParameterPayload)>(payload);
}

template<size_t size, int base>
std::array<char, size> bar(int input> {
    // ASCII encoding with a base (2, 10 or 16)
}

template<size_t payloadSize>
Message baz(std::array<char, payloadSize> payload) {
    // Some packing computation
}

This is a rough example, but I think it will get the message across. How do I cast the struct to a single std:array<char, N>? Is it even possible? I'm trying not to do multiple std::copies instead, because that will cost more resources and will worsen readability.

another solution I looked into is using

const char* const payload = reinterpret_cast<const char* const>(&paramPayload);

and going from there... I could do a single copy after that, but I'd like to avoid it.


Solution

  • That casting is pedantically UB (and even we don't have guaranty of exact size of std::array).

    I suggest to change to something like:

    Message foo(Parameters parameters) {
        std::array<char, 2+2+2+4> payload; // Actual struct has way more parameters
        int offset = 0;
        bar<2, 10>(parameters.a, payload.data() + offset, offset);
        bar<2, 10>(parameters.b, payload.data() + offset, offset);
        bar<2, 10>(parameters.c, payload.data() + offset, offset);
        bar<4, 16>(parameters.d, payload.data() + offset, offset);
    
        return baz(payload);
    }
    
    template<size_t size, int base>
    void bar(int input, char* out, int& offset) /* std::span<char, size> out (in C++20) */
    {
        offset += size;
        // ASCII encoding with a base (2, 10 or 16)
    }
    
    template<size_t payloadSize>
    Message baz(std::array<char, payloadSize> payload) {
        // Some packing computation
    }