微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何向函数指针数组中的每个函数添加标准例程?

如何解决如何向函数指针数组中的每个函数添加标准例程?

我正在用C ++为嵌入式系统创建一个应用程序。因此,我与某些C库绑定在一起,并且没有使用大量std功能的自由。

我有一个回调签名:

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以外的指针类型,但是我将推迟过度设计解决方案。如果某人需要更多的技巧,那么他总是可以提出一个新的问题来引用该问题。

,

我想到了另一种方法。在a related question中,添加了一个限制,即不得更改呼叫站点。这让我开始思考为什么有人会施加这样的限制。一种潜在的动机是减少add_callback()的呼叫者的心理负担。如果调用者不必记住包装它们的函数,它将更加健壮。如果插入的代码用于调试,则不必担心。但是,如果插入的代码用于生产-可能是性能监视的一部分-如果有人忘记了包装程序,那可能是一件大事。因此,我开始考虑编译器如何提供帮助。

通常,使事情自动化是一个合理的目标。程序员需要记住的越少,出错的机会就越少。即使OP的动机有所不同,我还是提出了这种方法供以后的读者考虑。

除了包装函数模板之外,还可以将add_callback()更改为函数模板。与其将所需的函数作为 function 参数传递,还可以是 template 参数。
注意事项::这确实意味着必须在编译时知道回调函数。像condition ? fun1 : fun2这样的表达式需要扩展为完整的if语句。

此方法的第一部分与我之前的方法相同。创建包装函数模板。

// (Could be private if there was a class encompassing this functionality.)
template <callback_fn F>
int wrap(int 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);

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。