1

Say we have the following union:

union Color{
    int rgba;//assuming 32 bit int
    struct{
        unsigned char r;
        unsigned char g;
        unsigned char b;
        unsigned char a;
    }ColorComp;
};

It is indeed undefined behaviour (only in c++, not in c) to access an inactive element of the union(setting say rgba and trying to access r). Is there any way to have this type of behaviour(NOTE: must be well-defined by standard) where types or combinations of types can read/write to the same memory locations as other different types-i.e type-punning- in c++?

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
Matias Chara
  • 921
  • 6
  • 21
  • 1
    undefined in C as well. very bad practice to do something like this. – old_timer Jul 14 '20 at 14:25
  • trivial to do with masking and shifting and is reliable and portable and generates the same output. – old_timer Jul 14 '20 at 14:26
  • 1
    @old_timer I dont think its undefined, here is a question on this site that confirms c supports this kind of stuff since some time already: https://stackoverflow.com/questions/11442708/type-punning-and-unions-in-c – Matias Chara Jul 14 '20 at 14:26
  • C++ basically allows no type punning. Really the only thing you can do is inspect the initial common sequence of standard layout classes in a union. `memcpy` is the non-UB way to copy bytes from one object to another. – NathanOliver Jul 14 '20 at 14:32
  • 1
    In addition, C++ doesn't have anonymous structs, as used in the example. – eerorika Jul 14 '20 at 14:35
  • @eerorika it does have anonymous structs though, take a look at this answer(at the end of the selected answer): https://stackoverflow.com/questions/2253878/why-does-c-disallow-anonymous-structs – Matias Chara Jul 14 '20 at 14:37
  • 2
    @MatiasChara The selected answer states that C++ doesn't have anonymous structs. – eerorika Jul 14 '20 at 14:39
  • @MatiasChara well you can read the spec yourself and find the facts not just some SO comments, if you go by SO statements and "works on my machine", then you will find the truth at the least opportune time. There are ways to write reliable code, and ways to write risky/questionable code. Why gamble? if you have to ask the question, are not sure, etc go with the reliable way not the questionable way. – old_timer Jul 14 '20 at 14:43
  • If you have to ask the question then you likely shouldnt be doing it. – old_timer Jul 14 '20 at 14:44
  • Then of course with respect to endianness you can using the same safe technique write code that is unaffected by endiannes. – old_timer Jul 14 '20 at 14:46

1 Answers1

3

Yes, there is a way. Copy the bytes onto an object of type that you want to read the bytes as. There is a standard function for this: memcpy.

Example:

struct Color{
    unsigned char r;
    unsigned char g;
    unsigned char b;
    unsigned char a;
};

static_assert(sizeof(Color) == sizeof(std::int32_t));
std::int32_t rgba = some_value;
Color c;
std::memcpy(&c, &rgba, sizeof c);
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Doesn't this depend on the endienness of the platform? In particular the endienness of how `some_value` will be stored into `rgba` in memory? – Wyck Jul 14 '20 at 14:45
  • I guess structure padding could also make this much more complicated, any solutions to that? – Matias Chara Jul 14 '20 at 14:46
  • 2
    @Wyck the union code in OP's answer would also have the same endianness concern as well -- so this is no different. – Human-Compiler Jul 14 '20 at 14:50
  • This is the correct answer, however it should be noted that the input and output type of `memcpy` should be trivially copyable, and should also be the same size. If using C++20, it would be better to use [`std::bit_cast`](https://en.cppreference.com/w/cpp/numeric/bit_cast) instead – Human-Compiler Jul 14 '20 at 14:51
  • 1
    @MatiasChara I deleted my comment thinking I was fast enough... I figured that as soon as I mentioned the padding. For more complex structs it's mostly a case of ensuring trivial copyability and the size being the same. Generally if you are trying to type-pun in any way, you probably already have an idea of the padding, size, and alignment – Human-Compiler Jul 14 '20 at 14:52
  • 1
    @MatiasChara: Type punning, regardless of mechanism, *requires* that you make assumptions about the layout of data structures. Punning an `int` to a `float` requires that you assume that both of these are the same size, that they have the same endian, and that the `float` is encoded in a specific way. Struct padding is no exception. – Nicol Bolas Jul 14 '20 at 15:36