Let's say I have an API:
// api.h - Others can #include this header
#include <cstdint>
class A {
public:
// Write data into an opaque type.
// The user shouldn't directly access the underlying bits of this value.
// They should use this method instead.
void WriteVal(uint32_t data);
private:
uint64_t opaque_value_;
};
// api.cpp
#include <cstdlib>
#include "api.h"
namespace {
// This is the underlying struct that the opaque value represents.
struct alignas(8) Impl {
uint32_t x;
uint32_t y;
};
} // namespace
void A::WriteVal(uint32_t data) {
uint64_t *opaque_ptr = &opaque_value_;
Impl *ptr = reinterpret_cast<Impl *>(opaque_ptr);
memcpy(&ptr->y, &data, sizeof(data));
}
Is there any undefined behavior in the A::WriteVal
method?
My guess would be NO for these reasons:
- Reinterpret casting between a
uint64_t *
andImpl *
on it's own is ok since the alignment of the pointee types is the same. - There is only UB if
ptr
were to be dereferenced explicitely since that would break strict aliasing rules. memcpy
can be safely used in substitution of explicit dereferencing regardless of the original type of the pointer.
Is my reasoning correct? If this is also considered UB, is there a nice C++ way of writing to an opaque type without illegal type punning methods.
My goal is to cleanly perform operations on an opaque value that, under the hood, represents a struct that users shouldn't know the details about.