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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With