本文介绍了向传递给函数的函数指针添加标准例程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下内容:

typedef int (*callback_function)(int a, int b);

class receiving_callbacks_class
{
public: 
  static register_callback(int iterator, callback_function fn)
  {
    function_list[iterator] = fn;
  }

private:
  static callback_function function_list[10];
}

C库使用此 function_list ,因此它可以在某些事件上进行回调.

This function_list is used by a C library, so it can do call backs on certain events.

我现在想向通过此函数注册的每个回调添加一个例程.这样它就这样被调用:

I now would like to add a routine to every callback that gets registered via this function. So that it gets called like this:

default_routine();
fn();

我尝试使用模板执行此操作,但是找不到在 register_callback 函数内部 中执行此操作的方法.使用模板执行此操作的每种方法都意味着包装该函数以在调用register_callback之前 进行注册.我想将其包含在这个特定的类中.

I tried doing it with templates, but I could not find a way to do that inside the register_callback function. Every way to do it with templates meant wrapping the function to register before calling register_callback. I would like to contain this inside this particular class.

这不同于通过添加要求在调用 register_callback 时不能进行任何更改的要求,如何向函数指针数组中的每个函数添加标准例程?所有更改必须在 receiving_callbacks_class 之内.

This differs from How to add standard routine to every function in array of function pointers? by adding the requirement that no changes can be made where register_callback is called; all changes must be within receiving_callbacks_class.

这是针对我没有自由使用 std 的嵌入式系统的.传递给 register_class 函数的所有函数都是静态的.

This is for an embedded system where I don't have the freedom to use std. All functions passed to the register_class function will be static.

这可能吗?

推荐答案

首先,我想添加一个序言,解释为什么这通常很困难.存在涉及存储在C库中的信息量的问题.其他C回调系统每个回调存储两条信息:要调用的函数的地址和强制转换为 void * 的任意数据.除了回调性质需要的参数之外,该函数还必须接受 void * 参数.这种额外"是指参数接收任意数据.将其强制转换回其原始类型可根据需要访问尽可能多的额外数据.如果不需要额外的数据,则可以为null.(在这种情况下,它将被投射回 callback_function .)

First, I would like to add a preamble explaining why this is difficult in general. There is a problem involving the amount of information being stored in the C library. Other C callbacks systems store two pieces of information per callback: the address of the function to call and arbitrary data cast to void*. The function is required to accept a void* argument in addition to the arguments required by the nature of the callback. This "extra" argument receives the arbitrary data. Casting this back to its original type allows access to as much extra data as needed; it could be null if no extra data is needed. (In this case, it would be cast back to callback_function.)

但是,在当前上下文中,存储在C库中的唯一信息是要调用的函数的地址.如果要调用不同的回调,则此信息必须不同,因此必须有不同的功能.不能更改呼叫站点的要求意味着 receiving_callbacks_class 需要提供不同的功能,但是当 receiving_callbacks_class 的用户编写新的回调时,这些功能需要适应

In the current context, however, the only information stored in the C library is the address of a function to call. This information must be different if different callbacks are to be invoked, hence there must be different functions. The requirement that the call site not be changed means that the different functions need to be provided by receiving_callbacks_class, yet these functions need to adapt when new callbacks are written by users of receiving_callbacks_class.

我可以想到在C ++中生成多个相似函数的四种主要技术,但是其中一种使用宏,因此我们将其称为三种:lambda,模板和复制粘贴.捕获实际回调地址的lambda不再可转换为函数指针,因此不再可用于C库.模板将使用实际回调的地址作为模板参数,这意味着该地址必须是编译时常量.但是,C ++没有提供 require 要求函数参数为编译时常量的方法,这使得该方法不适合在 register_callback()内部使用.这样就留下了复制粘贴,这通常很麻烦,但是当回调数小到10时可能是可以接受的.

I can think of four main techniques for generating multiple similar functions in C++, but one of them uses macros, so let's call it three: lambdas, templates, and copy-paste. A lambda that captures the address of the real callback is no longer convertible to a function pointer, so no longer usable with the C library. A template would use the address of the real callback as the template parameter, which means that address would have to be a compile-time constant. However, C++ does not provide a way to require that a function argument be a compile-time constant, making the approach unsuitable for use inside register_callback(). That leaves copy-paste, which normally is a pain, but might be acceptable when the number of callbacks is as small as 10.

我可以以 function_list 的定义为基础.传递给C库的回调的数量有一个很小的限制,并且可以在给定索引的情况下在恒定时间内调用它们.如果将阵列替换为不支持索引访问的容器,则此方法的吸引力可能会降低.随着容器尺寸的增加,它的吸引力肯定会减弱.如果容器的大小没有编译时限制,则可能无法使用.

I can base an approach on the definition of function_list. There is a smallish limit on the number of callbacks passed to the C library and they can be invoked in constant time, given their index. This approach might be less appealing if the array is replaced by a container that does not support indexed access. It definitely becomes less appealing as the size of the container increases. It might be unusable if there is not a compile-time limit on the size of the container.

对于 receiving_callbacks_class ,我可能会添加两个静态数据:第二个函数指针数组,以及两个数组的大小.

To receiving_callbacks_class I might add two pieces of static data: a second array of function pointers, and a size for both of the arrays.

class receiving_callbacks_class
{
public: 
    // Always use a symbolic constant when a magic number is needed more than once.
    static constexpr unsigned MAX_CALLBACKS = 10;
    
    // Looks the same (but with a return type), and called the same as before.
    static void register_callback(int iterator, callback_function fn)
    {
        function_list[iterator] = fn;
    }

private:
    // Old array using the new symbolic constant
    static callback_function function_list[MAX_CALLBACKS];
    // A new array, probably should be `const`.
    static const callback_function wrapper_list[MAX_CALLBACKS];
};

新数组用于存储指向包装函数的指针,这些函数将调用 default_routine()后跟真正的回调.这个数组,而不是旧的数组,应该提供给C库.但是,它仍然需要初始化.新数组的初始化是复制粘贴的开始.我将其留给读者决定在认为无法管理之前,它们允许 MAX_CALLBACKS 获得多大的大小.我个人认为大小为10时复制粘贴是合理的.

The new array is for storing pointers to wrapper functions, functions that will call default_routine() followed by the real callback. This array, not the old one, is what should be given to the C library. It still needs to be initialized, though. The initialization of the new array is where copy-paste comes in. I leave it to the reader to decide how large they would let MAX_CALLBACKS get before this is considered unmanageable. Personally, I found the copy-paste to be reasonable when the size is 10.

// Initialize the new array
const callback_function receiving_callbacks_class::wrapper_list[MAX_CALLBACKS] = {
    [](int a, int b) -> int { default_routine(); return function_list[0](a, b); },
    [](int a, int b) -> int { default_routine(); return function_list[1](a, b); },
    [](int a, int b) -> int { default_routine(); return function_list[2](a, b); },
    [](int a, int b) -> int { default_routine(); return function_list[3](a, b); },
    [](int a, int b) -> int { default_routine(); return function_list[4](a, b); },
    [](int a, int b) -> int { default_routine(); return function_list[5](a, b); },
    [](int a, int b) -> int { default_routine(); return function_list[6](a, b); },
    [](int a, int b) -> int { default_routine(); return function_list[7](a, b); },
    [](int a, int b) -> int { default_routine(); return function_list[8](a, b); },
    [](int a, int b) -> int { default_routine(); return function_list[9](a, b); },
};

在适当的 function_list 条目上添加null检查可能是适当的;我不知道您如何处理少于10个回调.请注意,这些lambda不会捕获,因此它们确实会转换为函数指针.它们之所以不能捕获,是因为它们的数目在编译时是固定的.

It might be appropriate to add a null check on the appropriate function_list entry; I do not know how you handle having fewer than 10 callbacks.Note that these lambdas are non-capturing, so they do convert to function pointers. They can be non-capturing only because their number is fixed at compile-time.

最后要更改的部分是我之前提到的内容.调用C库将使用新数组而不是旧数组.该代码未包含在示例代码中,因此我将设计一个名称和参数列表.

The last piece to change is what I mentioned before. The call into the C library would use the new array instead of the old. This was not included in the sample code, so I'll devise a name and parameter list.

    //c_library(function_list, 10);          // <-- replace this
    c_library(wrapper_list, MAX_CALLBACKS);  // <-- with this

这不是我的首选设置,但确实符合问题中的限制.

Not my preferred setup, but it does meet the restrictions in the question.

这篇关于向传递给函数的函数指针添加标准例程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-23 01:15