A reasonable use case is the manipulation of volatile variables. The following is a very simplified case from embedded programming using GNU tools (g++, ld):
To make an address of a peripheral a constexpr
, you need to put it at a fixed location. This must be done in a linker script:
⋮
/* Define output sections */
SECTIONS
{
GPIO 0x48000000 (NOLOAD) : { *(.GPIO) }
⋮
Now, the .GPIO
section is at the fixed address 0x48000400
. Peripherals can be modeled by PODs containing volatile members. In the following example, the POD is named gpio_t
and has just a single member: mode
. The member can be set up in a constexpr
function. Of course, there is no benefit to use a function to set a variable to a constant value. But in real use cases values and addresses have to be calculated. Think, e.g., of setting a divider for a Baud rate.
struct gpio_t {
volatile std::uint32_t mode;
};
__attribute__ ((section (".GPIO"))) gpio_t Gpio = {0};
static constexpr gpio_t *port {&Gpio};
static constexpr void init () {
port->mode = 42u;
};
void main {
init ();
⋮
};
Note: The C-style idiom of casting integers to addresses does not work because it reinterpret_cast<>
does not qualify for creating constexpr
pointers (see Since C++14 illegal). The following fails:
constexpr gpio_t *port {(gpio_t *) 0x48000400};