温馨提示:本文翻译自stackoverflow.com,查看原文请点击:templates - C++ Compile time check if a microcontroller pin is already initialized from other source file
c++ c++14 embedded metaprogramming templates

templates - C ++编译时间检查是否已从其他源文件初始化了微控制器引脚

发布于 2020-03-27 10:35:55

通常,微控制器的引脚可以通过端口号和引脚号来识别,两者都是编译时间常数。引脚可以具有多种功能,如果在大型项目中使用,则多个源文件可以初始化同一引脚并破坏其他模块中实现的功能。

我想实现一个编译时间列表,该列表最初为空,并且每次初始化引脚时,都会检查该列表中是否已存在该引脚,如果存在,它将给出一个静态的断言,否则它将在列表中插入引脚信息。运行时不需要列表。

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

查看更多

查看更多

提问者
sanjay
被浏览
214
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;
}