При создании библиотек для управления оборудованием на встроенных микропроцессорах обычной задачей является управление битами в определенных местах памяти для управления аппаратными функциями.
В процессорах AVR Atmel (теперь Microchip) предоставляет макросы, которые расширяются до чего-то вродеthis:
#define PORTA (*(volatile uint8_t *)(0x25))
, который включает такие вещи, как:
PORTA |= 1;
Теперь в C ++ 11 (и новее) желательно заменить практически любое использование #define
на constexpr
.
В более старых версиях компилятора GCC C ++ (4.9.2) компилировалось следующее:
#include <avr/io.h>
constexpr volatile uint8_t *const PortA = &PORTA;
В версии 8.2.0 вышеприведенное не компилируется и выдает ошибки:
error: reinterpret_cast from integer to pointer
Я не ищу объяснений, почему вы не можете использовать reinterpret_cast
в контексте constexpr
или почему преобразование целых в указатель является недопустимым.
Чтоправильный способ иметь указатель constexpr
на volatile
память в современном C ++?
Я видел предложения по сохранению адреса памяти PORTA
в constexpr uintptr_t
, а затемreinterprect_cast
это до volatile uint8_t * const
на руntime для битовых манипуляций.
Например, это работает и даже компилируется в одну sbi
инструкцию в avr-gcc
, как и ожидалось.
#include <stdint.h>
constexpr uintptr_t PortA = 0x25;
void set() { *((volatile uint8_t *)(PortA)) |= 1; }
Однако для использования Foo
в качестве указателя, которым он предназначен, требуется приличное количество уродливого шаблона.
Это также имеет проблему, которая кажется невозможнойиспользуйте макрос PORTA
напрямую.Вместо этого мы вынуждены жестко закодировать адрес памяти 0x25
, который нарушает некоторые желательные функции переносимости.
Такое ощущение, что я упускаю что-то очевидное, но мои поиски не принесли ничего полезного.
Например, это похоже на «выражение константы адреса», но, похоже, это относится к обращению к статически распределенным значениям const
, что не совсем то, что я хочу.
const char str[] = "FooBar";
constexpr const char * x = str + 2;