1

I'm working on a cross-platform data structure and trying to define a compact union-based layout that allows atomic access to a 64-bit word, while also optionally accessing the lower 32-bit fields.

I want to support:

  • MSVC x64 and ARM64
  • Clang x64 and ARM64
  • On Windows, macOS, and Linux

What I want:

I would like to define a structure like this:

struct tTWUnion { union { std::atomic<uint64_t> uValue; struct { std::atomic<uint32_t> uID; std::atomic<uint32_t> uSwitchPointID; }; }; }; 

Then use it like:

tTWUnion tun; tun.uValue.store(100); // or tun.uID.store(42); tun.uSwitchPointID.store(58); 

But this fails to compile because std::atomic is non-trivial, and placing multiple std::atomic inside a union causes the compiler to delete the default constructor (which I want to avoid manually overriding for portability reasons).

If I instead do:

struct tTWUnion { union { std::atomic<uint64_t> uValue; struct { uint32_t uID; uint32_t uSwitchPointID; }; }; }; 

Then I can compile it, but now only uValue is atomic — and accessing uID or uSwitchPointID breaks atomicity guarantees, and could lead to tearing or data races.

What I don't want:

  • I don’t want to use placement new and manual destructors (which makes code harder to maintain across platforms).
  • I don’t want to rely on undefined behavior or non-portable hacks, but I also want to avoid duplicating data if possible.

My question:

  • Is there a standard and portable way to define such a union layout where both full 64-bit atomic access and partial 32-bit atomic fields can coexist?

  • Why exactly does placing multiple std::atomic inside a union lead to constructor issues?

  • Are there any known methods for MSVC/Clang that can allow this safely?

Edit:

As an alternative idea, I’m wondering if something like the following structure could work safely and portably:

struct tTWUnion { union { uint64_t uValue; struct { uint32_t uID; uint32_t uSwitchPointID; }; }; }; 

And then use it like:

std::atomic<tTWUnion> atomicUnion; tTWUnion value = {}; value.uValue = 100; atomicUnion.store(value); tTWUnion value2 = {}; value2.uID = 42; value2.uSwitchPointID = 99; atomicUnion.store(value2); 

Is using std::atomic like this valid if sizeof(tTWUnion) == 8?

18
  • 5
    Your whole intention is UB. Writing to one union member and reading another is Undefined Behaviour Commented Aug 4 at 10:02
  • 1
    Do you want both members to be active at once, like with a 64-bit .store being read by a 32-bit atomic .load of one half? (That's is fine in asm, but C++ doesn't have a way to express it without technically being UB). Or is this just for reuse of space in different phases of your program? Commented Aug 4 at 10:32
  • 1
    I won’t be accessing both members simultaneously - it’s just for reusing the same memory in different phases of the program. Commented Aug 4 at 10:44
  • 1
    in my case, one thread writes using std::atomic<uint64_t> and later may read either the full 64-bit value or individual 32-bit halves (uID, uSwitchPointID) in the same thread (or after join). No concurrent access across the different views - just structured reuse. Would that still be UB, or is there a safe way to express this in C++20? Commented Aug 4 at 11:00
  • 2
    How about using a single uint64_t and accessing its "parts" using bit masks? Commented Aug 4 at 11:47

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.