通常,微控制器的引脚可以通过端口号和引脚号来识别,两者都是编译时间常数。引脚可以具有多种功能,如果在大型项目中使用,则多个源文件可以初始化同一引脚并破坏其他模块中实现的功能。
我想实现一个编译时间列表,该列表最初为空,并且每次初始化引脚时,都会检查该列表中是否已存在该引脚,如果存在,它将给出一个静态的断言,否则它将在列表中插入引脚信息。运行时不需要列表。
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;
}
我没有做任何需要有关引脚的运行时信息的事情。例如,我初始化了sdram的引脚,而其他人则初始化了SPI(使用通用引脚)。在运行时,我们将发送或接收数据。我们不会直接使用PIN码。每个引脚(带有常数PORT,PIN)的初始化仅发生一次。第二件事是原子初始化对于微控制器而言太昂贵且闪存繁重。
@sanjay好吧,拿一个试图通过任何C ++构造初始化引脚的源文件。编译器如何确定程序是否已初始化?不能,因为它无权访问其他源文件。从理论上讲,该信息可以在链接时获得,但不能很快获得。
是的,跨翻译很难。但是问题是真实的,通过查看所有文件,我可以看到重复的初始化。应该有某种方式可以通过编译器或通过构建系统来处理它。
@sanjay原子初始化对您来说太昂贵了?另外,如果您在不同的文件中看到多个初始化,则显然您有代码重复。这违反了DRY原则!您应该将所有代码收集到一个函数中。这样可以避免错误,并作为副产品为您提供所需的中央引脚寄存器,
@ jan.sende使用原子增加闪存大小。一些微控制器只有8KB闪存甚至更少。