4

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?

10
  • The key word in your narrative "is more typesafe". Does this whole thing look typesafe to you? Commented Jul 19, 2022 at 0:17
  • stackoverflow.com/questions/10369606/constexpr-pointer-value Commented Jul 19, 2022 at 0:23
  • @SamVarshavchik I don't see anything wrong with the types being used here. Commented Jul 19, 2022 at 0:25
  • 2
    This might answer your question. I presume that standard doesn't want to allow a source of UB (reinterpret_cast) to be handled in compile time. In another way, it might be UB in runtime, so how to implement it in compile time which doesn't allow UB in all the cases? Commented Jul 19, 2022 at 0:51
  • 1
    "using const compiles fine but is it practical?" -- You might have to define what you mean by "practical". I'd be inclined to say that using const is practical because it compiles fine, combined with it being not excessively long. Apparently you have a different definition of "practical" than me, so it would help if you included your definition in the question. Commented Jul 19, 2022 at 1:39

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.