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

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

通常可以使用端口号和引脚 number.Both 来标识微控制器引脚,这些引脚是编译时间常数。 一个 pin 可以有多个功能,如果在一个大项目中使用多个源文件可以初始化相同的 pin 和 break 功能在其他模块中实现。

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

我没有足够的元编程知识,如果有人能给我指导实现就太好了it.If已经有一些用于这种目的的库,请提供链接

你想要的是不可能的。 C++ 元编程没有 state,它更像是一种函数式语言而不是声明式语言。所以你不能有一个可变列表。唯一的状态可以通过创建新类型来引入,但是没有可用的语法来检查是否声明或定义了特定的 non-nested 名称。

多个源文件(编译单元)独立编译,肯定没有"global state",更不可能

此外,请注意,您所做的本质上是 run-time。编译器没有工具来检查您是否两次调用初始化函数。这些调用可能隐藏在某些 run-time if-else 决策的背后。而简单的写HAL_GPIO_Init();无论在整个程序中写多少次都不会报错

我能想到的最简单的事情是创建一个 C++ 单例 class 来负责与引脚通信。如果启用,您可以使用 error_codes 或异常来使用专用的 int init_GPIO 方法。而不是 static_assert 你将不得不依赖测试 - 单例工作正常并且 init_GPIO 的 return 值不会被忽略。

如果你真的不想打扰单例,这个函数模板也可以:

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

如果需要 thread-safe 初始化,则使用:

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

假设每个 driver 或 HAL 都有一个 header 文件,并且有一个包含所有这些 header 的 main.cpp,那么您可以使用pre-processor。

可选地,使用如下枚举创建 project-wide header "pintype.h":

// pintype.h

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

然后对每个header文件,写一个pre-processor检查,例如:

// 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

#error 严格来说是不需要的,因为如果发生冲突,编译器会抱怨同一翻译单元(main.cpp 的翻译单元)中有多个定义。

当编写 driver 的开发人员收到错误消息时,他们可以转到引脚的 pre-processor 定义并找出项目中的哪个其他模块已经声明了它,而无需挖掘 driver.

的内部实现