1

I am developing an embedded project (on STM32). I currently use GCC 4.9.2, but I would like to switch to newer version of my toolchain. Unfortunately my code which succesfully compiles on gcc 4.9.2, throws reinpreted_cast errors on version 6.2.0 or 7.2.0 and I have no idea why. It looks like that newer gcc sees some problems when casting int to pointer and back to int - which I think should be quite normal operation.

Error message thrown:

1>STM32L4\CMSIS\stm32l4a6xx.h(1567,30): error : 'reinterpret_cast<ADC_TypeDef*>(1342439424)' is not a constant expression
1>          #define ADC1                ((ADC_TypeDef *) ADC1_BASE)
1>                                       ^
1>         Sources\CAdc.cpp(31,35): note:  in expansion of macro 'ADC1'
1>             case reinterpret_cast<uint32_t>ADC1: u32DMAChannel = LL_DMA_CHANNEL_1; break;

Here is part of my code which error refers to:

switch ((uint32_t)adc)
{
case (uint32_t)ADC1: u32DMAChannel = LL_DMA_CHANNEL_1; break;
case (uint32_t)ADC2: u32DMAChannel = LL_DMA_CHANNEL_2; break;
case (uint32_t)ADC3: u32DMAChannel = LL_DMA_CHANNEL_3; break;
}

And adc declaration:

private:
   ADC_TypeDef *adc;

Here are all macros definitions:

#define PERIPH_BASE           (0x40000000UL) /*!< Peripheral base address */
#define AHB2PERIPH_BASE       (PERIPH_BASE + 0x08000000UL)
#define ADC1_BASE             (AHB2PERIPH_BASE + 0x08040000UL)
#define ADC1                  ((ADC_TypeDef *) ADC1_BASE)

So for compiler, my in-switch cast looks like this:

(uint32_t)((ADC_TypeDef *) (((0x40000000UL)+ 0x08000000UL)+ 0x08040000UL))

Simple cast of unsigned long to some struct pointer and back to unsigned long. What is wrong with it? Whould should I do to get rid of this error? Any macro edition is impossible for me because these are BSP libraries.

legier
  • 129
  • 1
  • 9
  • if .. else if is not perfect replacement for every switch.Verfy often swtich is compiled very different than if .. else if construction. This is subtitution proposal, but I would like to understand what is going on with this newer gcc. – legier Jan 05 '19 at 22:38
  • 1
    @legier "_I would like to understand what is going on with this newer gcc._" As explained [here](https://www.gnu.org/software/gcc/projects/cxx-status.html), the default standard used, in all GCC versions prior to 6.1 is C++98. GCC 6.1 and above compiles with C++14 set as default. So, most likely, something related to what's used in that `switch`, changed between the standards. One can compile with `-std=c++98` compiler flag to force it to use C++98. – Algirdas Preidžius Jan 05 '19 at 22:58
  • It is not the reinterpret that is failing, but rather the reinterpreted value is not regarded as a constant so therefore cannot be used as a case label. I am not sure why the compiler is translating a C style cast into a C++ reinterpret but I suspect that is the cause of the problem. Do you need to compile this code as C++ - the fragment you have posted is valid C? – Clifford Jan 06 '19 at 12:41
  • Whole project is written in C++, but provided libraries for embedded peripherals usage are written in C. – legier Jan 06 '19 at 20:29

2 Answers2

3

It is not the reinterpret that is failing, but rather the reinterpreted value is not regarded as a constant so therefore cannot be used as a case label.

Case labels must be a constant expression, and a constant expression is any expression that can be evaluated at compile time. In C++ evaluation of a reinterpret_cast expression is not a constant expression.

The compiler behaviour change here appears that the compiler translates the previously brute-force C-style cast into a more restrictive C++ reinterpret_cast. Testing at https://www.onlinegdb.com/ shows that this behaviour occurs when C++17 compilation is used but not C++14, so the simple "no code change" solution is to set the compilation to the earlier standard - arguably a sensible approach for legacy code in any case to avoid any other surprises or gotchas.

It seems however that the stm32l4a6xx.h header already provides two representations of the ADC base address one an integer (ADCx_BASE) and the other a pointer (ADCx). It is generally better to avoid casts at all and use the appropriate representation of the purpose. In this case:

switch ((uint32_t)adc)
{
    case ADC1_BASE: u32DMAChannel = LL_DMA_CHANNEL_1; break;
    case ADC2_BASE: u32DMAChannel = LL_DMA_CHANNEL_2; break;
    case ADC3_BASE: u32DMAChannel = LL_DMA_CHANNEL_3; break;
}
Clifford
  • 88,407
  • 13
  • 85
  • 165
1

You might change code to avoid cast in cases of switch:

switch ((uint32_t)adc)
{
    case ADC1_BASE: u32DMAChannel = LL_DMA_CHANNEL_1; break;
    case ADC2_BASE: u32DMAChannel = LL_DMA_CHANNEL_2; break;
    case ADC3_BASE: u32DMAChannel = LL_DMA_CHANNEL_3; break;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302