Constexpr Pointer Value

How to set a constexpr pointer to a physical Address

Use this instead:

static uint16_t * const T30_CAL = reinterpret_cast<uint16_t *>(0x1FFFF7B8u);

GCC will store T30_CAL in flash on an ARM target, not RAM. The point is that the 'const' must come after the '*' because it is T30_CAL that is const, not what T30_CAL points to.

How to define a pointer pointing to a constexpr variable?

A constexpr object is an object just like any other. The fact that its value is computed at compile time does not alter this.

Often, the compiler will seek to avoid actually emitting code to create const values and objects if it knows that they will never be needed , for example when objects are static const.

By taking the address of an object, whether constexpr, static const or an auto variable, the compiler is forced to actually create the object.

So:

constexpr int i = 5;    // need not be actually created

const int* pi = &i; // but now it must be, because we took its address

constexpr const int* pi2 = &i; // constexpr pointer to const object - we took its address so it must exist

const void emit(int);

int main()
{
emit(i);
emit(*pi);
emit(*pi2);
}

results in:

main:
subq $8, %rsp
movl $5, %edi <-- compiler knows it's a fixed value
call emit(int)

movq pi(%rip), %rax <-- compiler dereferences the pointer
movl (%rax), %edi
call emit(int)

movl $5, %edi <-- compiler knows it's a fixed value
call emit(int)

xorl %eax, %eax
addq $8, %rsp
ret
pi:
.quad i
i:
.long 5

Constexpr pointer value

As Luc Danton notes, your attempts are blocked by the rules in [expr.const]/2 which say that various expressions are not allowed in core constant expressions, including:

-- a reinterpret_cast
-- an operation that would have undefined behavior [Note: including [...] certain pointer arithmetic [...] -- end note]

The first bullet rules out your first example. The second example is ruled out by the first bullet above, plus the rule from [expr.cast]/4 that:

The conversions performed by [...] a reinterpret_cast [...] can be performed using the cast notation of explicit type conversion. The same semantic restrictions and behaviors apply.

The second bullet was added by WG21 core issue 1313, and clarifies that pointer arithmetic on a null pointer is not permitted in a constant expression. This rules out your third example.

Even if these restrictions did not apply to core constant expressions, it would still not be possible to initialize a constexpr pointer with a value produced by casting an integer, since a constexpr pointer variable must be initialized by an address constant expression, which, by [expr.const]/3, must evaluate to

the address of an object with static storage duration, the address of a function, or a null pointer value.

An integer cast to pointer type is none of these.

g++ does not yet strictly enforce these rules, but its recent releases have been getting closer to them, so we should assume that it will eventually fully implement them.

If your goal is to declare a variable for which static initialization is performed, you can simply drop the constexpr -- both clang and g++ will emit a static initializer for this expression. If you need this expression to be part of a constant expression for some reason, you have two choices:

  • Restructure your code so that an intptr_t is passed around instead of a pointer, and cast it to pointer type when you need to (outside of a constant expression), or
  • Use __builtin_constant_p((int*)0xFF) ? (int*)0xFF : (int*)0xFF. This exact form of expression (with __builtin_constant_p on the left-hand-side of a conditional operator) disables strict constant expression checking in the arms of the conditional operator, and is a little-known, but documented, non-portable GNU extension supported by both gcc and clang.

constexpr initializing with pointers

Make them static to fix their addresses:

int main()
{
constexpr int *np = nullptr; // np is a constant to int that points to null;
static int j = 0;
static constexpr int i = 42; // type of i is const int
constexpr const int *p = &i; // p is a constant pointer to the const int i;
constexpr int *p1 = &j; // p1 is a constant pointer to the int j;
}

This is known as an address constant expression [5.19p3]:

An address constant expression is a prvalue core constant expression
of pointer type that evaluates to the address of an object with static
storage duration, to the address of a function, or to a null pointer
value, or a prvalue core constant expression of type std::nullptr_t.

c++ constexpr pointer and constexpr comparison

Comparing the addresses of two unrelated objects has an unspecified result

[expr.rel]

3 Comparing unequal pointers to objects is defined as follows:

  • If two pointers point to different elements of the same array, or to subobjects thereof, the pointer to the element with the higher
    subscript compares greater.

  • If two pointers point to different non-static data members of the same object, or to subobjects of such members, recursively, the
    pointer to the later declared member compares greater provided the two
    members have the same access control and provided their class is not a
    union.

  • Otherwise, neither pointer compares greater than the other.


4 If two operands p and q compare equal, p<=q and p>=q both yield
true and p<q and p>q both yield false. Otherwise, if a pointer p
compares greater than a pointer q, p>=q, p>q, q<=p, and q<p all yield
true and p<=q, p<q, q>=p, and q>p all yield false. Otherwise, the
result of each of the operators is unspecified.

And a relational expression with an unspecified result is explicitly forbidden from appearing in a constant expression

[expr.const]

2 An expression e is a core constant expression unless the
evaluation of e, following the rules of the abstract machine, would
evaluate one of the following expressions:

  • ...
  • a relational or equality operator where the result is unspecified; or
  • ...

A compiler is required to issue a diagnostic when it encounters something forbidden in the evaluation of a constant expression. Hence you get an error.

Always bear in mind that the C++ standard defines things in terms of an abstract machine. Constant expressions are those which have well-defined semantics in that abstract machine, and so may be evaluated "at compile time" when all arguments are known. If something is unspecified in the abstract machine, it cannot produce a well-defined constant expression, so it's prohibited.



Related Topics



Leave a reply



Submit