如何向函数指针数组中的每个函数添加标准例程?
How to add standard routine to every function in array of function pointers?
我正在用 C++ 为嵌入式系统创建应用程序。因此,我依赖于某些 C 库,无法自由使用大量标准功能。
我有回调签名:
typedef int (*callback_fn)(int a, int b);
包含指向已注册回调的指针的列表:
callback_fn my_callback_functions[10];
以及一个将回调添加到此列表的函数:
void add_callback(int function_iterator, callback_fn fn)
{
my_callback_functions[function_iterator] = fn;
}
然后将回调传递给该库,并在某些事件发生时调用回调。
现在我想为每个回调添加一个小例程,这样我就可以在每次调用时重置计时器:
myTimer.reset();
如何在不更改签名或远离函数指针(我需要与库交互)的情况下将其添加到每个调用?
我用模板尝试了一些东西,但找不到正确的方法来完成这项工作。
定义一个包含静态函数的结构。这将是您的回电之一。结构的名称并不重要,只是函数本身的名称必须是常量。例如:
struct MyCallback1 {
static int callback(int a, int b) {
//..
}
};
现在我们可以编写包装器了:
template<typename T>
int decorated_callback(int a, int b) {
myTimer.reset();
return T::callback(a, b);
}
并将其注册为回调:
add_callback(0, &decorated_callback<MyCallback1>);
让我们从天真开始,然后添加复杂。
添加例程的最简单方法是编辑回调函数。也就是说,如果您的回调之一是 fun1()
,您可以将行 myTimer.reset();
添加到函数体的开头。
除了...也许不是每次调用此函数都应该重置计时器(不要忘记允许将来更改)。我们可以不那么天真,创建一个包装函数来重置计时器然后调用 fun1()
。回调可以调用这个包装器,而其他调用使用原始函数。
int wrap1(int a, int b)
{
myTimer.reset();
return fun1(a, b);
}
除了...我很懒。我不想为每个回调都写一个包装器。我会把这个任务交给其他人,一个不会抱怨我懒惰的人,一个像 gcc
这样的人(或者你选择的任何编译器)。我宁愿写一个模板来展示如何编写这个包装器,然后让编译器根据需要生成包装器。唯一不同的是回调函数,因此它成为模板参数。
template <callback_fn F>
int wrap(int a, int b)
{
myTimer.reset();
return F(a, b);
}
现在您可以使用如下行添加回调。
add_callback(0, wrap<fun1>);
add_callback(1, wrap<fun2>);
这可以概括为处理 callback_fn
以外的指针类型,但我会推迟 over-engineering 解决方案。如果有人需要更复杂的知识,那个人总是可以提出一个引用这个问题的新问题。
我想到了另一种方法。在a related question中,添加了调用站点不能更改的限制。这让我开始思考为什么有人会施加这样的限制。一个潜在的动机是减轻 add_callback()
来电者的心理负担。如果调用者不必记住包装他们的函数,它会更健壮。如果插入的代码是为了调试,这不是什么大问题。然而,如果插入的代码用于生产——也许它是性能监控的一部分——如果有人忘记了包装器,这可能是一件大事。所以我开始考虑编译器如何提供帮助。
总的来说,让事情自动化是一个合理的目标。程序员需要记住的越少,出错的机会就越少。即使 OP 的动机不同,我也会提出这种方法供未来的读者考虑。
除了包装函数模板之外,还可以将 add_callback()
更改为函数模板。与其将所需函数作为 function 参数传递,不如将其作为 template 参数传递。
警告: 这确实意味着必须在编译时知道回调函数;像 condition ? fun1 : fun2
这样的表达式需要扩展为 full-blown if
语句。
此方法的第一部分与我之前的方法相同。创建包装函数模板。
// (Could be private if there was a class encompassing this functionality.)
template <callback_fn F>
int wrap(int a, int b)
{
myTimer.reset();
return F(a, b);
}
接下来,将add_callback
修改为一个模板参数只有一个函数参数的模板。
// The interface is now has one template parameter and one function parameter
// instead of two function parameters.
template <callback_fn F>
void add_callback(int function_iterator)
{
my_callback_functions[function_iterator] = wrap<F>;
}
最后,更改调用站点。现有调用看起来像 add_callback(0, fun1)
,现在将被编译器拒绝。没有漏接电话的危险。添加回调的新语法如下。
add_callback<fun1>(0);
add_callback<fun2>(1);
我正在用 C++ 为嵌入式系统创建应用程序。因此,我依赖于某些 C 库,无法自由使用大量标准功能。
我有回调签名:
typedef int (*callback_fn)(int a, int b);
包含指向已注册回调的指针的列表:
callback_fn my_callback_functions[10];
以及一个将回调添加到此列表的函数:
void add_callback(int function_iterator, callback_fn fn)
{
my_callback_functions[function_iterator] = fn;
}
然后将回调传递给该库,并在某些事件发生时调用回调。
现在我想为每个回调添加一个小例程,这样我就可以在每次调用时重置计时器:
myTimer.reset();
如何在不更改签名或远离函数指针(我需要与库交互)的情况下将其添加到每个调用?
我用模板尝试了一些东西,但找不到正确的方法来完成这项工作。
定义一个包含静态函数的结构。这将是您的回电之一。结构的名称并不重要,只是函数本身的名称必须是常量。例如:
struct MyCallback1 {
static int callback(int a, int b) {
//..
}
};
现在我们可以编写包装器了:
template<typename T>
int decorated_callback(int a, int b) {
myTimer.reset();
return T::callback(a, b);
}
并将其注册为回调:
add_callback(0, &decorated_callback<MyCallback1>);
让我们从天真开始,然后添加复杂。
添加例程的最简单方法是编辑回调函数。也就是说,如果您的回调之一是 fun1()
,您可以将行 myTimer.reset();
添加到函数体的开头。
除了...也许不是每次调用此函数都应该重置计时器(不要忘记允许将来更改)。我们可以不那么天真,创建一个包装函数来重置计时器然后调用 fun1()
。回调可以调用这个包装器,而其他调用使用原始函数。
int wrap1(int a, int b)
{
myTimer.reset();
return fun1(a, b);
}
除了...我很懒。我不想为每个回调都写一个包装器。我会把这个任务交给其他人,一个不会抱怨我懒惰的人,一个像 gcc
这样的人(或者你选择的任何编译器)。我宁愿写一个模板来展示如何编写这个包装器,然后让编译器根据需要生成包装器。唯一不同的是回调函数,因此它成为模板参数。
template <callback_fn F>
int wrap(int a, int b)
{
myTimer.reset();
return F(a, b);
}
现在您可以使用如下行添加回调。
add_callback(0, wrap<fun1>);
add_callback(1, wrap<fun2>);
这可以概括为处理 callback_fn
以外的指针类型,但我会推迟 over-engineering 解决方案。如果有人需要更复杂的知识,那个人总是可以提出一个引用这个问题的新问题。
我想到了另一种方法。在a related question中,添加了调用站点不能更改的限制。这让我开始思考为什么有人会施加这样的限制。一个潜在的动机是减轻 add_callback()
来电者的心理负担。如果调用者不必记住包装他们的函数,它会更健壮。如果插入的代码是为了调试,这不是什么大问题。然而,如果插入的代码用于生产——也许它是性能监控的一部分——如果有人忘记了包装器,这可能是一件大事。所以我开始考虑编译器如何提供帮助。
总的来说,让事情自动化是一个合理的目标。程序员需要记住的越少,出错的机会就越少。即使 OP 的动机不同,我也会提出这种方法供未来的读者考虑。
除了包装函数模板之外,还可以将 add_callback()
更改为函数模板。与其将所需函数作为 function 参数传递,不如将其作为 template 参数传递。
警告: 这确实意味着必须在编译时知道回调函数;像 condition ? fun1 : fun2
这样的表达式需要扩展为 full-blown if
语句。
此方法的第一部分与我之前的方法相同。创建包装函数模板。
// (Could be private if there was a class encompassing this functionality.)
template <callback_fn F>
int wrap(int a, int b)
{
myTimer.reset();
return F(a, b);
}
接下来,将add_callback
修改为一个模板参数只有一个函数参数的模板。
// The interface is now has one template parameter and one function parameter
// instead of two function parameters.
template <callback_fn F>
void add_callback(int function_iterator)
{
my_callback_functions[function_iterator] = wrap<F>;
}
最后,更改调用站点。现有调用看起来像 add_callback(0, fun1)
,现在将被编译器拒绝。没有漏接电话的危险。添加回调的新语法如下。
add_callback<fun1>(0);
add_callback<fun2>(1);