2024-09-19 11:44 PM - last edited on 2024-09-21 01:32 AM by Andrew Neil
Hi everyone,
I’ve noticed that STM32 drivers make extensive use of macros, and I’m curious about the reasoning behind this. Is the primary motivation is to increase performance / improve maintainability / are there other factors involved?
2024-09-22 09:06 PM
All I want to achieve with the HAL is MISRA compliance, but these macros seem to be one of the obstacle in reaching that goal.
@TDK wrote:At the least, execution would be slower and compile/link time would increase, while the functionality would remain the same.
Converting them to normal functions appears to be the most plausible trade-off for achieving MISRA compliance.
2024-09-22 11:49 PM
I’m considering this from a safety and defensive programming standpoint. In that case, choosing normal functions over macros seems like a better idea. Normal functions provide better type safety, prevent unintended side effects, and are easier to debug compared to macros
2024-09-23 12:06 AM - edited 2024-09-23 12:06 AM
@sarun wrote:are easier to debug compared to macros
Is that (necessarily) true?
2024-09-23 12:19 AM
It really depends on the macros.
For example, for the "do-while" macros which have usually a little more code inside, might be replaced with functions without a drawback,
But - I think, no statistics on my side - the mostly used macros in STM32 HAL are those to write / read register / bits.
And replacing these with functions would really bloat the HAL even more.
These would be better replaced by "normal" register access (like: TIM1->CR |= TIM1_CR_EN; ).
2024-09-23 12:29 AM
Macros do not allow step-through debugging like normal functions. When debugging, you cannot step into a macro, as it is expanded by the preprocessor before compilation. This is one of the reasons I considered using functions, which provide better debugging support.
@Andrew Neil wrote:Is that (necessarily) true?
Am i missing something here?
2024-09-23 12:48 AM
@sarun wrote:you cannot step into a macro, as it is expanded by the preprocessor before compilation.
But that's essentially the same when you use inline functions.
ISTR have the same problem.
2024-09-23 06:44 AM
Macros also allow the ability to redefine the macro in one spot to affect all of them, unlike functions. Consider this:
assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));
along with its definition:
#ifdef USE_FULL_ASSERT
/**
* @brief The assert_param macro is used for function's parameters check.
* @PAram expr: If expr is false, it calls assert_failed function
* which reports the name of the source file and the source
* line number of the call that failed.
* If expr is true, it returns no value.
* @retval None
*/
#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
void assert_failed(uint8_t *file, uint32_t line);
#else
#define assert_param(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */
If USE_FULL_ASSERT is not defined, the code is simply not there at all--zero overhead. There is no way to replace this with a function and retain the same functionality.
Since HAL runs on all chips, even the lowly ones with only 16 kB FLASH, this can be necessary. No way to do that if it was defined as a function. So if it were changed, you would be breaking a lot of things.
MISRA doesn't flat out forbid macros, or function-like macros.
2024-09-23 07:02 AM
@sarun Yes this is a valid point and IIRC some software guidelines such as MISRA mention this aspect.
So if you build coding guidelines for your own project do whatever looks right for you, or what the customer demands. ST has their own considerations.
2024-09-23 07:07 AM
@TDK wrote:the code is simply not there at all--zero overhead. There is no way to replace this with a function and retain the same functionality..
Is that true?
Are the tools not smart enough to spot an empty function, and emit no code at all?
But that does bring us back to an earlier point: it becomes dependent on the particular compiler (and its settings).
2024-09-23 07:17 AM
Not sure I follow. If you replaced the macro with a function, why would the function be empty?
To the larger question: I imagine all common compilers will eliminate an empty function entirely under reasonable optimization levels. However, if the function is in a different compilation unit, which it would be here, it would almost certainly require link-time-optimization, which can be especially slow.