How to constexpr construct C++ object containing reinterpret_cast pointer?

mark_r via gcc-help
Tue Feb 11 10:34:00 GMT 2020

On 2/10/20 3:39 PM, Jonathan Wakely wrote:
> Do you really need constant initialization, or just static initialization?

Modulo any subtle differences I don't understand (likely) between the 
two, just the latter. I need the address of the pointed-to S* const 
object to be set in the bits for the struct/class instance in the .data 
segment of the compiled ELF binary.

> It seems all you require is there's no dynamic initialization at run
> time, you don't actually *require* constexpr to be used here.

With the caveats above, yes, correct.

> That's a
> means to an end, which in this case doesn't actually work.

Yes, as I've been wrestling with for two years now. I only recently 
discovered the fact that my initialization not being a constexpr was the 
root of the problem, and I still don't understand why reinterpret_cast 
needs to be disallowed in a constexpr (i.e. what would break if it was 
allowed). Nor likewise why reinterpret_cast to a pointer type is illegal 
(I get one or both errors, depending on compiler and details of the 
attempted definition). But you've provided a lot of tutorial explanation 
already, so this is more of a comment than a request for more.

> You don't need the extra member function, you just chose to use that.
> You could also write:
>    void set() const { *reinterpret_cast<S*>(_u)->u = 0x87654321; }
> I'm not sure why struct S is needed here at all:
>    void set() const { *reinterpret_cast<unsigned*>(_u) = 0x87654321; }

All true. This was a stripped-down, minimal example for the purpose of 
illustration. In the real code the struct S is far more complex, in fact 
likely one of the heavily templated objects from

Likewise, the gratuitous extra member function was because the actual 
enclosing class will be using the pointed-to object many times, in many 
different methods. If I have to use this workaround I'd much rather have 
a (I hope and trust) inline method to access the pointer vs sprinkling 
verbose reinterpret_casts throughout the code.

> You can't make the member private, which you can't do in C either. I
> fail to see how that makes C++ inferior. It seems equal in this
> respect, if not better (you can still use member functions on the
> type).
> If being unable to make the member private makes the solution inferior
> to the C version, then surely the C version is inferior to itself.
> That's a silly requirement.
Yes, of course there's no private type specifier in C. My point was that 
in C, using C semantics and programming practices, the implementation is 
simple and straightforward. In C++ it seems I have to use one form of 
trickery or another to work around what, IMHO, is a failing of the 
language: That I can't use simple/obvious/common C++ semantics/practices 
and have both a private Object* const pointer and also static 
initialization, the "defined behavior" of which I claim is, while not 
portable across machine architectures, completely unambiguous.

The relative superiority/inferiority is a matter of subjective taste. 
For myself I'll almost certainly stick with C++ even if it requires 
using the workarounds. I wish it was otherwise, and also want to 
understand better why it isn't (and/or can't be changed).

> Anyway, this works:
> struct Addr
> {
>    constexpr Addr(unsigned p) : u(p) { }
>    void set() const { *reinterpret_cast<unsigned*>(u) = 0x87654321; }
> private:
>    uintptr_t const     u;
> };
> constinit Addr addr( 0x40000000 );
> int main()
> {
>    addr.set();
> }
> And this works if you're OK with type-punning via a union:
> struct Addr
> {
>    constexpr Addr(unsigned v) : u(v) { }
>    void set() const { *p = 0x87654321; }
> private:
>    union {
>      uintptr_t u;
>      unsigned* p;
>    };
> };
> constinit Addr addr( 0x40000000 );
> int main()
> {
>    addr.set();
> }
> In both these cases, the C++20 'constinit' keyword ensures the object
> is statically initialized, not at runtime. For pre-C++20 you can just
> omit the 'constinit' and the result is the same.

Once again (as per the Bugzilla posts) thanks for your clever (that's 
intended as a true compliment, not sarcasm) ways of getting around the 
problem. The first gets right back to requiring the per-use 
reinterpret_casts or helper method, and the second uses union 
type-punning, as you point out. I'm a caveman and will use anything I 
need to get the job done, but from what I've read, using unions for this 
purpose is almost more frowned-upon than anything else. Regardless, it's 
once again an extra level of conceptual indirection, extra code, 
potential for errors, and just a workaround in general. My C++-hating C 
coder friends point at the complexity and verbosity of C++ and I counter 
with the advantages it brings. I find it hard to do so in this case.

I don't have a C++20-compliant version of GCC yet, and hadn't heard of 
constinit until seeing it here in your post. It seems very new and not 
heavily documented, but from what I read it's more a static_assert kind 
of thing (I'm speaking generally, not rigorously) that makes sure an 
object is static initializable. Is it more than that, and by using it 
would the following code compile and be static initialized?

class C {
     constexpr C(
     unsigned* const u)
     :   _u(u)
     void set() const { *_u = 0x87654321; }
     unsigned* const     _u;

// or c = ...
// or c {...}
// or whatever
constinit C   c(reinterpret_cast<unsigned*>(0x40000000));

More information about the Gcc-help mailing list