Casting a constexpr value to a pointer via reinterpret cast

Viewed 105

Coming from C and embedded background, register addresses are often used and cast to pointers:

#define REG_A_ADDR       0x80000000

uint32_t ptr_reg_a = (uint32_t*) REG_A_ADDR;

However, C++ emphasizes usage of constexpr for compile time constants and that is more typesafe.

The following is the equivalent version I came up with but it doesn't compile, since reinterpret_cast can't be used with constexpr, seemingly:

constexpr uint32_t reg_a_addr = 0x80000000;

constexpr uint32_t *ptr_reg_a = reinterpret_cast<uint32_t*>(reg_a_addr); // constexpr variable 'reg_a_addr' must be initialized by a constant expression

So, since the above snippet errors out, what's the practical approach around it? Using const compiles fine but is it practical?

1 Answers

Although I'm not entirely sure what you mean by "type safe"1,2 in this context, you can use const with the appropriate ...ptr_t type to ensure that the (constant) value you provide is valid for the pointer.

Take the following code, for example:

#include <iostream>
#include <cstdint>

// I added an extra zero to your constant - now it's too big for a 32-bit pointer
constexpr uintptr_t reg_a_addr = 0x800000000; // uintptr_t is platform-specific
uint32_t* const ptr_reg_a = reinterpret_cast<uint32_t* const>(reg_a_addr);
// ^ Note that "const unit32_t* ptr_reg_a ..." declares a pointer to a constant;
// the version I have given defines a constant pointer to (potentially) writable memory.

int main()
{
    std::cout << ptr_reg_a << "\n";
    return 0;
}

When targeting the x64 platform with MSVC, this compiles without warning and produces the expected output:

0000000800000000

However, when targeting the x86 (32 bit) platform, the compiler will issue warnings that the value is too big:

warning C4305: 'initializing': truncation from '__int64' to 'const uintptr_t'
warning C4309: 'initializing': truncation of constant value

And, indeed, the output will be the truncated value:

00000000

As to whether or not it is practical – well, it compiles but trying to use a constexpr with reinterpret_cast doesn't, so I guess it is!


Note that the clang-cl compiler in Visual Studio 2022 (when targeting 32 bits) gives an 'equivalent' warning for the constant being too big:

warning : implicit conversion from 'long long' to 'const uintptr_t' (aka 'const unsigned int') changes value from 34359738368 to 0 [-Wconstant-conversion]


1 Maybe, by "type safe," you mean that reassigning a new value to that pointer will be prevented at compile time … in which case: Yes, it is type safe, as code like the following will not compile:

    uint32_t q = 42;
    ptr_reg_a = &q;

error : cannot assign to variable 'ptr_reg_a' with const-qualified type 'uint32_t *const' (aka 'unsigned int *const')

2 If you are asking whether or not using const rather than constexpr will make such a pointer any less strictly typed, then: No, it won't. However, (IIRC) the C++ Standard does impose stricter requirements for diagnostics on potential misuse of constexpr values … but many (if not most) mainstream compilers will, if warnings are fully enabled, provide suitable diagnostics for cases of (say) violation of strict aliasing rules, or other usage of such a pointer that exhibits undefined behaviour.

Related