But the problems do not end there. When you write some constexpr-function, which will then often use, it would be good to return a readable error. Here you can mistakenly assume that static_assert is suitable for this. But static_assert cannot be used, since the parameters of the functions cannot be constexpr, which is why the values of the parameters are not guaranteed to be known at the compilation stage.And since exceptions are not supported in the constexpr methods, we will simply get an error that using throw in constexpr is impossible. If we were processing something in a loop, then we can never know which element we fell on (when checking which element we caused an exception).
How to display errors? The only more or less normal way I found is to throw an exception:
/* * . */ struct __attribute__( ( packed ) ) pin_config_t { EC_PORT_NAME port; // // ( : EC_PORT_NAME::A ). EC_PORT_PIN_NAME pin_name; // // ( : EC_PORT_PIN_NAME::PIN_0 ). EC_PIN_MODE mode; // // ( : EC_PIN_MODE::OUTPUT ). EC_PIN_OUTPUT_CFG output_config; // // ( : EC_PIN_OUTPUT_CFG::NOT_USE ). EC_PIN_SPEED speed; // // ( : EC_PIN_SPEED::MEDIUM ). EC_PIN_PULL pull; // // ( : EC_PIN_PULL::NO ). EC_PIN_AF af; // // ( : EC_PIN_AF::NOT_USE ). EC_LOCKED locked; // // // global_port // ( EC_LOCKED::NOT_LOCKED ). EC_PIN_STATE_AFTER_INIT state_after_init; // // ( , ). // ( EC_PIN_STATE_AFTER_INIT::NO_USE). };
/********************************************************************** * enum class-. **********************************************************************/ /* * . */ enum class EC_PORT_PIN_NAME { PIN_0 = 0, PIN_1 = 1, PIN_2 = 2, PIN_3 = 3, PIN_4 = 4, PIN_5 = 5, PIN_6 = 6, PIN_7 = 7, PIN_8 = 8, PIN_9 = 9, PIN_10 = 10, PIN_11 = 11, PIN_12 = 12, PIN_13 = 13, PIN_14 = 14, PIN_15 = 15 }; /* * . */ enum class EC_PIN_MODE { INPUT = 0, // . OUTPUT = 1, // . AF = 2, // . ANALOG = 3 // . }; /* * . */ enum class EC_PIN_OUTPUT_CFG { NO_USE = 0, // . PUSH_PULL = 0, // "-". OPEN_DRAIN = 1 // " ". }; /* * . */ enum class EC_PIN_SPEED { LOW = 0, // . MEDIUM = 1, // . FAST = 2, // . HIGH = 3 // }; /* * */ enum class EC_PIN_PULL { NO_USE = 0, // . UP = 1, // . DOWN = 2 // . }; /* * , . */ enum class EC_PIN_AF { AF_0 = 0, NO_USE = AF_0, SYS = AF_0, AF_1 = 1, TIM1 = AF_1, TIM2 = AF_1, AF_2 = 2, TIM3 = AF_2, TIM4 = AF_2, TIM5 = AF_2, AF_3 = 3, TIM8 = AF_3, TIM9 = AF_3, TIM10 = AF_3, TIM11 = AF_3, AF_4 = 4, I2C1 = AF_4, I2C2 = AF_4, I2C3 = AF_4, AF_5 = 5, SPI1 = AF_5, SPI2 = AF_5, I2S2 = AF_5, AF_6 = 6, SPI3 = AF_6, I2S3 = AF_6, AF_7 = 7, USART1 = AF_7, USART2 = AF_7, USART3 = AF_7, AF_8 = 8, UART4 = AF_8, UART5 = AF_8, USART6 = AF_8, AF_9 = 9, CAN1 = AF_9, CAN2 = AF_9, TIM12 = AF_9, TIM13 = AF_9, TIM14 = AF_9, AF_10 = 10, OTG_FS = AF_10, AF_11 = 11, ETH = AF_11, AF_12 = 12, FSMC = AF_12, SDIO = AF_12, AF_13 = 13, DCMI = AF_13, AF_14 = 14, AF_15 = 15, EVENTOUT = AF_15 }; /* * set_locked_key_port * set_locked_keys_all_port global_port. * ! global_port. * - . * - . */ enum class EC_LOCKED { NOT_LOCKED = 0, // . LOCKED = 1 // . }; /* * * ( , ). */ enum class EC_PIN_STATE_AFTER_INIT { NO_USE = 0, RESET = 0, SET = 1 };
class pin { public: constexpr pin ( const pin_config_t* const pin_cfg_array ); void set ( void ) const; void reset ( void ) const; void set ( uint8_t state ) const; void set ( bool state ) const; void set ( int state ) const; private: constexpr uint32_t p_bsr_get ( const pin_config_t* const pin_cfg_array ); constexpr uint32_t set_msk_get ( const pin_config_t* const pin_cfg_array ); constexpr uint32_t reset_msk_get ( const pin_config_t* const pin_cfg_array ); const uint32_t p_bsr; const uint32_t bsr_set_msk, bsr_reset_msk; };
/* * <<1>>, * . */ void pin::set ( void ) const { *M_U32_TO_P(this->p_bsr) = this->bsr_set_msk; } /* * <<0>>, * . */ void pin::reset ( void ) const { *M_U32_TO_P(this->p_bsr) = this->bsr_reset_msk; } /* * , * . */ void pin::set ( uint8_t state ) const { if ( state ) { this->set(); } else { this->reset(); } } void pin::set ( bool state ) const { this->set( static_cast< uint8_t >( state ) ); } void pin::set ( int state ) const { this->set( static_cast< uint8_t >( state ) ); }
// uint32_t uint32_t. // . #define M_U32_TO_P(point) ((uint32_t *)(point))
/********************************************************************** * constexpr . **********************************************************************/ /* * "1" BSR. */ constexpr uint32_t pin::set_msk_get ( const pin_config_t* const pin_cfg_array ) { return 1 << M_EC_TO_U8(pin_cfg_array->pin_name); } /* * "0" BSR. */ constexpr uint32_t pin::reset_msk_get ( const pin_config_t* const pin_cfg_array ) { return 1 << M_EC_TO_U8( pin_cfg_array->pin_name ) + 16; } /* * BSR, . */ constexpr uint32_t pin::p_bsr_get( const pin_config_t* const pin_cfg_array ) { uint32_t p_port = p_base_port_address_get( pin_cfg_array->port ); return p_port + 0x18; }
// enum class uint8_t. #define M_EC_TO_U8(ENUM_VALUE) ((uint8_t)ENUM_VALUE)
/* * - * . */ constexpr uint32_t p_base_port_address_get( EC_PORT_NAME port_name ) { switch( port_name ) { #ifdef PORTA case EC_PORT_NAME::A: return 0x40020000; #endif #ifdef PORTB case EC_PORT_NAME::B: return 0x40020400; #endif #ifdef PORTC case EC_PORT_NAME::C: return 0x40020800; #endif #ifdef PORTD case EC_PORT_NAME::D: return 0x40020C00; #endif #ifdef PORTE case EC_PORT_NAME::E: return 0x40021000; #endif #ifdef PORTF case EC_PORT_NAME::F: return 0x40021400; #endif #ifdef PORTG case EC_PORT_NAME::G: return 0x40021800; #endif #ifdef PORTH case EC_PORT_NAME::H: return 0x40021C00; #endif #ifdef PORTI case EC_PORT_NAME::I: return 0x40022000; #endif } }
/********************************************************************** * constexpr . **********************************************************************/ constexpr pin::pin ( const pin_config_t* const pin_cfg_array ): p_bsr ( this->p_bsr_get( pin_cfg_array ) ), bsr_set_msk ( this->set_msk_get( pin_cfg_array ) ), bsr_reset_msk ( this->reset_msk_get( pin_cfg_array ) ) {};
/********************************************************************** * template . **********************************************************************/ template < EC_PORT_NAME PORT, EC_PORT_PIN_NAME PIN_NAME, EC_PIN_MODE MODE, EC_PIN_OUTPUT_CFG OUTPUT_CONFIG, EC_PIN_SPEED SPEED, EC_PIN_PULL PULL, EC_PIN_AF AF, EC_LOCKED LOCKED, EC_PIN_STATE_AFTER_INIT STATE_AFTER_INIT > class pin_config_check_param : public pin_config_t { public: constexpr pin_config_check_param(): pin_config_t( { .port = PORT, .pin_name = PIN_NAME, .mode = MODE, .output_config = OUTPUT_CONFIG, .speed = SPEED, .pull = PULL, .af = AF, .locked = LOCKED, .state_after_init = STATE_AFTER_INIT } ) { /* * . */ #if defined(STM32F205RB)|defined(STM32F205RC)|defined(STM32F205RE) \ |defined(STM32F205RF)|defined(STM32F205RG) static_assert( PORT >= EC_PORT_NAME::A && PORT <= EC_PORT_NAME::H, "Invalid port name. The port name must be A..H." ); #endif static_assert( PIN_NAME >= EC_PORT_PIN_NAME::PIN_0 && PIN_NAME <= EC_PORT_PIN_NAME::PIN_15, "Invalid output name. An output with this name does not" "exist in any port. The output can have a name PIN_0..PIN_15." ); static_assert( MODE >= EC_PIN_MODE::INPUT && MODE <= EC_PIN_MODE::ANALOG, "The selected mode does not exist. " "The output can be set to mode: INPUT, OUTPUT, AF or ANALOG." ); static_assert( OUTPUT_CONFIG == EC_PIN_OUTPUT_CFG::PUSH_PULL || OUTPUT_CONFIG == EC_PIN_OUTPUT_CFG::OPEN_DRAIN, "A non-existent output mode is selected. " "The output can be in the mode: PUSH_PULL, OPEN_DRAIN." ); static_assert( SPEED >= EC_PIN_SPEED::LOW && SPEED <= EC_PIN_SPEED::HIGH, "A non-existent mode of port speed is selected. " "Possible modes: LOW, MEDIUM, FAST or HIGH." ); static_assert( PULL >= EC_PIN_PULL::NO_USE && PULL <= EC_PIN_PULL::DOWN, "A non-existent brace mode is selected." "The options are: NO_USE, UP or DOWN." ); static_assert( AF >= EC_PIN_AF::AF_0 && AF <= EC_PIN_AF::AF_15, "A non-existent mode of the alternative port function is selected." ); static_assert( LOCKED == EC_LOCKED::NOT_LOCKED || LOCKED == EC_LOCKED::LOCKED, "Invalid port lock mode selected." ); static_assert( STATE_AFTER_INIT == EC_PIN_STATE_AFTER_INIT::NO_USE || STATE_AFTER_INIT == EC_PIN_STATE_AFTER_INIT::SET, "The wrong state of the output is selected." "The status can be: NO_USE, UP or DOWN." ); }; };
const pin_config_check_param< EC_PORT_NAME::C, EC_PORT_PIN_NAME::PIN_4, EC_PIN_MODE::OUTPUT, EC_PIN_OUTPUT_CFG::PUSH_PULL, EC_PIN_SPEED::MEDIUM, EC_PIN_PULL::NO_USE, EC_PIN_AF::NO_USE, EC_LOCKED::LOCKED, EC_PIN_STATE_AFTER_INIT::SET > lcd_res;
const constexpr pin pin_lcd_res( &lcd_res );
void port_test ( void ) { pin_lcd_res.reset(); pin_lcd_res.set(); }
/* * bit_banding * , . */ constexpr uint32_t pin::bb_p_idr_read_get ( const pin_config_t* const pin_cfg_array ) { uint32_t p_port = p_base_port_address_get( pin_cfg_array->port ); uint32_t p_idr = p_port + 0x10; return M_GET_BB_P_PER(p_idr, M_EC_TO_U8(pin_cfg_array->pin_name)); } /* * bit banding , * . */ constexpr uint32_t pin::odr_bit_read_bb_p_get ( const pin_config_t* const pin_cfg_array ) { uint32_t p_port = p_base_port_address_get( pin_cfg_array->port ); uint32_t p_reg_odr = p_port + 0x14; return M_GET_BB_P_PER(p_reg_odr, M_EC_TO_U8(pin_cfg_array->pin_name)); }
//********************************************************************* // , . //********************************************************************* #define BIT_BAND_SRAM_REF 0x20000000 #define BIT_BAND_SRAM_BASE 0x22000000 // RAM Bit Banding . #define MACRO_GET_BB_P_SRAM(reg, bit) \ ((BIT_BAND_SRAM_BASE + (reg - BIT_BAND_SRAM_REF)*32 + (bit * 4))) #define BIT_BAND_PER_REF ((uint32_t)0x40000000) #define BIT_BAND_PER_BASE ((uint32_t)0x42000000) // Bit Banding . #define M_GET_BB_P_PER(ADDRESS,BIT) \ ((BIT_BAND_PER_BASE + (ADDRESS - BIT_BAND_PER_REF)*32 + (BIT * 4)))
/********************************************************************** * constexpr . **********************************************************************/ constexpr pin::pin ( const pin_config_t* const pin_cfg_array ): p_bsr ( this->p_bsr_get( pin_cfg_array ) ), p_bb_odr_read ( this->odr_bit_read_bb_p_get( pin_cfg_array ) ), bsr_set_msk ( this->set_msk_get( pin_cfg_array ) ), bsr_reset_msk ( this->reset_msk_get( pin_cfg_array ) ), p_bb_idr_read ( this->bb_p_idr_read_get( pin_cfg_array ) ) {};
/* * , * . */ void pin::invert( void ) const { if (*M_U32_TO_P_CONST(p_bb_odr_read)) { // 1, 0. this->reset(); } else { this->set(); } } /* * . */ int pin::read() const { return *M_U32_TO_P_CONST(p_bb_idr_read); }
// uint32_t uint32_t. // , ( ). #define M_U32_TO_P_CONST(point) ((const uint32_t *const)(point))
class pin { public: constexpr pin ( const pin_config_t* const pin_cfg_array ); void set ( void ) const; void reset ( void ) const; void set ( uint8_t state ) const; void set ( bool state ) const; void set ( int state ) const; void invert ( void ) const; int read ( void ) const; private: constexpr uint32_t p_bsr_get ( const pin_config_t* const pin_cfg_array ); constexpr uint32_t set_msk_get ( const pin_config_t* const pin_cfg_array ); constexpr uint32_t reset_msk_get ( const pin_config_t* const pin_cfg_array ); constexpr uint32_t odr_bit_read_bb_p_get ( const pin_config_t* const pin_cfg_array ); constexpr uint32_t bb_p_idr_read_get ( const pin_config_t* const pin_cfg_array ); const uint32_t p_bsr; const uint32_t bsr_set_msk, bsr_reset_msk; const uint32_t p_bb_odr_read, p_bb_idr_read; };
template < EC_PORT_NAME PORT, EC_PORT_PIN_NAME PIN_NAME > class pin_config_adc_check_param : public pin_config_check_param< PORT, PIN_NAME, EC_PIN_MODE::INPUT, EC_PIN_OUTPUT_CFG::NO_USE, EC_PIN_SPEED::LOW, EC_PIN_PULL::UP, EC_PIN_AF::NO_USE, EC_LOCKED::LOCKED, EC_PIN_STATE_AFTER_INIT::NO_USE > { public: constexpr pin_config_adc_check_param() {}; };
const pin_config_adc_check_param< EC_PORT_NAME::B, EC_PORT_PIN_NAME::PIN_1 > adc_left;
Source: https://habr.com/ru/post/331468/
All Articles