简体   繁体   中英

C++ Compile time check if a microcontroller pin is already initialized from other source file

Typically a microcontroller pin can be identified with with port number and pin number.Both are compile time constants. A pin can have multiple functions,if used in a big project multiple source file can initialize same pin and break functionality implemented in other module.

I want to implement a compile time list which is initially empty and each time a pin is initialized it will check if the pin is already present in that list, if its present it will give a static assert otherwise it will insert pin information in the list. List is not required at run time.

I don't have enough knowledge of meta programming, It would be great if someone can provide me direction to implement it.If there is already some library for this kind of purpose, please provide the links

What you want is not possible. C++ metaprogramming does not have a state , it's more akin to a functional language than declarative one. So you cannot have a mutable list. The only state can be introduced by creating new types, but there's no available syntax to check if a particular non-nested name is declared or defined.

Multiple source files (compilation units) are compiled independently so there's certainly no "global state" and that makes it more impossible.

Also, note that what you are doing is inherently run-time. The compiler has no tools to check if you are calling the initialization function twice. These calls might be hidden behind some run-time if-else decisions. And simply writing HAL_GPIO_Init(); no matter how many times in the whole program is not an error.

The simplest thing I can think of is creating a C++ singleton class that is responsible for communicating with pins. You can have a dedicated int init_GPIO method using error_codes or exceptions if they are enabled. Instead of static_assert you will have to rely on tests - that singleton works correctly and the return value of init_GPIO is not ignored.

If you really do not want to bother with singleton, this function template works too:

template<std::size_t GPIO, std::size_t port> int GPIO_init(GPIO_InitStruct& s){
    static bool initialized=false;
    if(initialized) return <already_called>;
    initialized=true;
    //Assuming that you want to propagate the return value.
    return HAL_GPIO_Init(GPIO, port, s);// Replace with the correct call.
}

If you require thread-safe initialization then use:

template<std::size_t GPIO, std::size_t port> int GPIO_init(GPIO_InitStruct& s){
    static std::once_flag initialized;
    int ret_val = <already_called>;
    auto call = [&](){ret_val = HAL_GPIO_Init(GPIO, port, s)};
    std::call_once(initialized, call);
    return ret_val;
}

Assuming that every driver or HAL has a header file, and there's a main.cpp which includes all those headers, then you can do this with the pre-processor.

Optionally, make a project-wide header "pintype.h" with an enum such as this:

// pintype.h

typedef enum
{
  PIN_GPIO,
  PIN_PWM,
  PIN_ADC,
  PIN_UART,
  ...
} pin_t;

Then for every header file, write a pre-processor check, for example:

// pwm.h, header of the pwm driver or HAL
#include "pintype.h"

#ifdef PIN9
  #error Pin 9 already taken
#else
  #define PIN9 PIN_PWM
#endif

The #error is strictly speaking not needed, because in case of conflicts the compiler will complain about multiple definitions in the same translation unit (that of main.cpp).

When the developer writing the driver gets an error message, they can go to the pre-processor definition of the pin and find out which other module in the project that has claimed it already, without digging inside the internal implementation of that driver.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM