Warm tip: This article is reproduced from stackoverflow.com, please click
c++ c++14 embedded metaprogramming templates

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

发布于 2020-03-27 10:18:19

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

Questioner
sanjay
Viewed
111
Quimby 2019-06-28 16:32

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;
}