0

I want to concatenate two structs. The problem is that I cannot convert that struct to a std::span, because span's constructor doesn't support void*. I'm using the same concat function to concatenate std::vector<uint8_t> as well, so any changes made to that function, shouldn't affect it. If it's possible to convert the struct to vector<uint8_t>, I will be able to change std::span<uint8_t> to vector of the same. How can I do that?

address_t address{};
memcpy(address.country, country, sizeof country);
memcpy(address.city, city, sizeof city);
memcpy(address.state, state, sizeof state);
address.zip_code = zip_code;

...

const auto& a = reinterpret_cast<void*>(&address);

auto c = utils::concat(a, b);

Snippet

__declspec(align(16))
typedef struct
{
    std::wchar_t country[30];
    std::wchar_t city[50];
    std::wchar_t state[50];
    std::uint32_t zip_code;
} address_t;

namespace utils
{
    inline std::vector<std::uint8_t> concat(const std::span<std::uint8_t>& a)
    {
        return std::vector<std::uint8_t>(a.data(), a.data() + a.size());
    }
    
    template <typename... Args>
    std::vector<std::uint8_t> concat(const std::span<std::uint8_t>& a, Args&... args)
    {
        auto vec = std::vector<std::uint8_t>(a.data(), a.data() + a.size());
        (vec.insert(vec.end(), args.begin(), args.end()), ...);

        return vec;
    }
}
4
  • a struct is not a vector, though. What do you think you'd get if you concatted one struct onto another, other than an access violation? Commented Apr 6, 2021 at 20:01
  • @GarrGodfrey, but a struct is literally bytes next to each other with some spacing in between which is the alignment. Why would it be not possible? Commented Apr 6, 2021 at 20:06
  • why not just reinterpret to uint8_t* instead of void*? But you'll need to provide the length of the data at some point. Commented Apr 6, 2021 at 20:10
  • @GarrGodfrey, works too! Commented Apr 6, 2021 at 20:22

1 Answer 1

1

You can provide a way to reinterpret an address as a span:

auto to_span(address const& a) -> std::span<uint8_t const> {
    return {(uint8_t const*)&a, sizeof(a)};
}

And then the rest of your stuff just works:

auto c = utils::concat(to_span(address), to_span(other_address));

This can be generalized to support trivially copyable types.


Your concat currently takes parameters as const std::span<std::uint8_t>&. You typically want to take span by value (it's a small, trivially copyable type, and you almost never care about the identity of the span). Additionally, when you're not modifying the contents of the span (as you're not here), you want to ensure that it's a span over const data.

That is, your parameters should have type std::span<uint8_t const>.

This ensures that you're not modifying through the span (which is important and allows you to pass immutable things, like my to_span above) whereas taking by const& only protects you from modifying the span itself (which is not important at all).

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you very much! Is there a difference between const std::vector<std::uint8_t>& and const std::vector<std::uint8_t const>& tho?
@nop Yes: the latter is ill-formed and the former is not (you can't have a vector<T const>)
thank you a lot! Summarized, these objects that we care about its value like std::span, they should be const std::span<std::uint8_t const> alike and the others that we want to prevent copies, they should be const std::vector<std::uint8_t>&, right?
@nop Yes. Because vector owns its data (copying is expensive,and it is deep const) and span does not (copying is cheap and it is shallow const)
Thank you once again! Great answer, great explanation!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.