How to have a constexpr pointer to CMSIS defined hardware peripheral?

181 Views Asked by At

I want to use constexpr instead of #defines wherever possible, for type safety and namespace features.

Unfortunately, I get this error: 'reinterpret_cast<SPI_TypeDef*>(1073756160)' is not a constant expression when trying.

#include <stm32f0xx.h> // #defines SPI2 as pointer to a struct of volatile unsigned ints

constexpr auto myPort = SPI2;

I'm not looking for an explanation of why reinterperet_cast cannot be used in a constexpr.

What is the modern C++ way to have a constexpr pointer to some memory mapped hardware?

One reason for this is to use these constexpr values in templated code.

3

There are 3 best solutions below

3
On

constexpr code wasn't invented so that you could avoid using #defines; it's there so that you can do certain things with expressions that you couldn't otherwise. You can pass constexpr pointers or integers as template parameters, for example, since they are constant expressions.

Basically, the primary purpose of any constexpr variable or function is to be able to be used within compile-time programming. So to declare a variable to be constexpr is to say "this is a compile-time constant and it's reasonable to use it at compile-time."

A pointer whose value is an arbitrary address cannot be used at compile-time. So marking such a pointer as a compile-time value is a contradiction. So you're not allowed to do it. constexpr pointers are required to be real pointers, which are either null pointers or point to actual objects. Not to arbitrary addresses.

0
On

It's not possible. Even std::bit_cast, which can in certain cases emulate reinterpret_cast at compile-time, loses its constexpr-ness when one of the involved types is a pointer.

2
On

Like other answers already state, it is not possible for arbitrary pointers.

If it is "for type safety and namespace features [...] to some memory mapped hardware" you are asking for, why you don't just use

// assumed preconditions, since not provided in question
typedef struct {
  volatile unsigned int a;
  volatile unsigned int b;
} SPI_TypeDef;

SPI_TypeDef* SPI2 = (SPI_TypeDef*)0x12345678;

// actual answer
SPI_TypeDef* const myPort = SPI2;

This way your pointer myPort to some data of type struct SPI_TypeDef is const, but not the pointed-to struct.

const keyword is generally "left-assigning".