【发布时间】:2009-09-28 02:58:15
【问题描述】:
我正在编写一个用于嵌入式的大型 C 程序。该程序中的每个模块都有一个 init() 函数(如构造函数)来设置其静态变量。
问题是我必须记住从main() 调用所有这些初始化函数。如果我出于某种原因将它们注释掉,我还必须记住将它们放回去。
我有什么聪明的办法来确保所有这些函数都被调用吗?类似于在每个 init 函数中放置一个宏,当您稍后调用 check_inited() 函数时,如果没有调用所有函数,则会向 STDOUT 发送警告。
我可以增加一个计数器,但我必须在某处保持正确数量的 init 函数,这也容易出错。
想法?
以下是我决定的解决方案,这个帖子中有几个人的意见
我的目标是确保我的所有 init 函数都被实际调用。我想要做 这无需维护多个文件中的模块列表或计数。我不能打电话 正如 Nick D 建议的那样,它们会自动调用,因为它们需要按特定顺序调用。
为此,每个模块中包含的宏使用 gcc constructor 属性来
将 init 函数名称添加到全局列表中。
init 函数主体中包含的另一个宏更新全局列表以创建一个 请注意,该函数实际上已被调用。
最后,在所有初始化完成后,在main() 中调用一个检查函数。
注意事项:
我选择将字符串复制到数组中。这不是绝对必要的,因为 传递的函数名在正常使用中始终是静态字符串。如果记忆短暂 你可以只存储一个指向传入字符串的指针。
我的可重用实用函数库称为“nx_lib”。因此所有的“nxl”名称。
这不是世界上最高效的代码,但它只被称为引导时间,因此 对我来说没关系。
每个模块需要添加两行代码。如果其中一个被省略, 检查功能会让你知道。
您或许可以将构造函数设为静态,这样就无需为其指定一个在整个项目中唯一的名称。
此代码仅经过轻微测试,而且已经很晚了,因此请在信任它之前仔细检查。
感谢:
pierr 向我介绍了constructor 属性。
Nick D 演示了 ## 预处理器技巧并为我提供了框架。
tod Frye 了解一种可以与许多编译器一起使用的基于链接器的巧妙方法。
其他人提供帮助并分享有用的花絮。
nx_lib_public.h
这是我的库头文件的相关片段
#define NX_FUNC_RUN_CHECK_NAME_SIZE 20
typedef struct _nxl_function_element{
char func[NX_FUNC_RUN_CHECK_NAME_SIZE];
BOOL called;
} nxl_function_element;
void nxl_func_run_check_add(char *func_name);
BOOL nxl_func_run_check(void);
void nxl_func_run_check_hit(char *func_name);
#define NXL_FUNC_RUN_CHECK_ADD(function_name) \
void cons_ ## function_name() __attribute__((constructor)); \
void cons_ ## function_name() { nxl_func_run_check_add(#function_name); }
nxl_func_run_check.c
这是被调用以添加函数名称并稍后检查它们的库代码。
#define MAX_CHECKED_FUNCTIONS 100
static nxl_function_element m_functions[MAX_CHECKED_FUNCTIONS];
static int m_func_cnt = 0;
// call automatically before main runs to register a function name.
void nxl_func_run_check_add(char *func_name)
{
// fail and complain if no more room.
if (m_func_cnt >= MAX_CHECKED_FUNCTIONS) {
print ("nxl_func_run_check_add failed, out of space\r\n");
return;
}
strncpy (m_functions[m_func_cnt].func, func_name,
NX_FUNC_RUN_CHECK_NAME_SIZE);
m_functions[m_func_cnt].func[NX_FUNC_RUN_CHECK_NAME_SIZE-1] = 0;
m_functions[m_func_cnt++].called = FALSE;
}
// call from inside the init function
void nxl_func_run_check_hit(char *func_name)
{
int i;
for (i=0; i< m_func_cnt; i++) {
if (! strncmp(m_functions[i].func, func_name,
NX_FUNC_RUN_CHECK_NAME_SIZE)) {
m_functions[i].called = TRUE;
return;
}
}
print("nxl_func_run_check_hit(): error, unregistered function was hit\r\n");
}
// checks that all registered functions were called
BOOL nxl_func_run_check(void) {
int i;
BOOL success=TRUE;
for (i=0; i< m_func_cnt; i++) {
if (m_functions[i].called == FALSE) {
success = FALSE;
xil_printf("nxl_func_run_check error: %s() not called\r\n",
m_functions[i].func);
}
}
return success;
}
solo.c
这是一个需要初始化的模块示例
#include "nx_lib_public.h"
NXL_FUNC_RUN_CHECK_ADD(solo_init)
void solo_init(void)
{
nxl_func_run_check_hit((char *) __func__);
/* do module initialization here */
}
【问题讨论】:
-
.. 如果您忘记将宏放在特定的 init 函数中?然后你又回到了原点。
-
嗯,是的,但至少这是我在编写函数时可以做的事情,记住这样做比同步另一个文件中的内容更容易。
-
把错误写到stderr不是更好吗?
-
stdout 转到仅用于调试的串口,不使用 stderr。
-
PS Jonathan 我在你删除它之前看到了你的帖子,我认为这是一个非常有趣的技术,即使它不适合这种情况。