简体   繁体   English

如何在C ++中传递通用函数指针?

[英]How can I pass generic function pointer in c++?

What I am trying to accomplish is registering a callback in an embedded environment. 我要完成的工作是在嵌入式环境中注册回调。 This callback will be in one of two forms: 该回调将采用以下两种形式之一:

void (*cb) (void *ctxt); or
void ClassA::VirtualFn (void);

This code will only run on an ARM platform using GCC. 该代码只能在使用GCC的ARM平台上运行。 The register callback function MUST be virtual due to some dynamic binding done at runtime. 由于在运行时完成了一些动态绑定,因此注册回调函数必须是虚拟的。 Both the above functions are equivalent at the assembly level (they both take a single pointer). 以上两个函数在汇编级别都是等效的(它们都使用一个指针)。 Furthermore the callback mechanism is in assembly for performance purposes because it occurs in ISR context so I don't have to worry about that. 此外,出于性能目的,回调机制处于汇编状态,因为它发生在ISR上下文中,因此我不必为此担心。 All I really need is a function that takes either of the above and stores the passed function pointer and context pointer. 我真正需要的是一个采用以上任何一种并存储传递的函数指针和上下文指针的函数。 ie: 即:

void isr_cb (void *ctxt) {}
gpio->RegisterIsr (isr_cb, cptr);
gpio->RegisterIsr (&ClassA::IsrHandler, this);

I've tested this by casting the virtual member function to (void (*) (void *)) and indeed everything works as expected (apart from the compiler warnings). 我已经通过将虚拟成员函数强制转换为(void (*) (void *)) ,实际上一切工作都按预期进行(除了编译器警告)。

Casting a virtual member function to void (*)(void *) sounds like a harsh approach. virtual成员函数强制转换为void (*)(void *)听起来很苛刻。

You said: 你说:

The register callback function MUST be virtual due to some dynamic binding done at runtime. 由于在运行时完成了一些动态绑定,因此注册回调函数必须是虚拟的。

I take it to mean that you have an object of type ClassA . 我认为这意味着您具有ClassA类型的对象。 If that is correct, then you should: 如果正确,那么您应该:

  1. Register a static member of ClassA to the callback mechanism whose signature is: ClassAstatic成员注册到其签名为的回调机制中:

     void (*)(ClassA& ref); 
  2. Add virtual member function that does the real work. 添加执行实际工作的virtual成员函数。

     void doStuff(); 
  3. Let's say the registered callback function is: 假设已注册的回调函数为:

     static void foo(Class& ref); 

    In the implementation of foo , call doStuff on ref . foo的实现中,对ref调用doStuff

     static void foo(Class& ref) { ref.doStuff(); } 

You will have a much easier time if you can make the utilize either std::bind , in c++11 or boost::bind . 如果您可以利用c ++ 11中的std::bindboost::bind ,则将拥有更加轻松的时间。 Both will let you mix member function pointers and free functions with each other if the interface (ie actual output type and actual input parameter type are the same) eg 如果接口(即实际输出类型和实际输入参数类型相同),则两者都可以让您将成员函数指针和自由函数彼此混合使用,例如

int f(double val)
{

}

class A {

   int memberf(double val);

}

A instanceOfA;
auto f1 = std::bind(f, std::placeholders::_1);
auto f2 = std::bind(&A::memberf, &instanceOfA, std::placeholders::_1);

f1 and f2 are now both functions that take a double and return an int , independent of the fact that one is a member function and the other is a free function, there is no cast needed. 现在, f1f2都是带double并返回int函数,而与以下事实无关:一个是成员函数,另一个是自由函数,因此不需要强制转换。 I have to admit I can't write the actual type from memory. 我不得不承认我不能从内存中写出实际的类型。

First things first, there is no such thing as a generic function-pointer in C nor C++. 首先,在C和C ++中都没有通用的函数指针。

Still, a fully-conformant and high-performant solution is possible. 完全合格和高性能的解决方案仍然是可能的。

As the non-member-function is already of the proper type, it is trivial. 由于非成员函数已经是正确的类型,因此它是微不足道的。

  1. If you know the full set of non-virtual member-functions and virtual-function slots you might want to pass, define a simple forwarder for each, along the lines of: 如果您知道可能要传递的全套非虚拟成员功能和虚拟功能插槽,请为每个定义一个简单的转发器,方法如下:

     void forward_to_IsrHandler(void* p) { ((ClassA*)p)->IsrHandler(); } gpio->RegisterIsr (forward_to_IsrHandler, (void*)this); 
  2. If you don't know, or really want to sacrifice standard-conformity at the altar of performance, you can just convert your member-function-pointer to a normal function-pointer (gcc extension, other compilers have their own) : 如果您不知道或真的想在性能方面牺牲标准一致性,则可以将成员函数指针转换为普通函数指针(gcc扩展,其他编译器也有自己的)

     void (*tmp)(ClassA*) = (void(*)(ClassA*))&ClassA::IsrHandler; 

    Use -Wno-pmf-conversions to remove the warning on use of the extension. 使用-Wno-pmf-conversions删除使用扩展名的警告。
    Because a call to a function void(*)(void*) and void(*)(ClassA*) look exactly the same on ARM, you can then use it thus, even though it is strictly speaking UB: 由于在ARM上对函数void(*)(void*)void(*)(ClassA*)的调用看起来完全相同,因此即使严格地说是UB,也可以使用它:

     gpio->RegisterIsr ((void(*)(void*))tmp, (void*)this); 

Selectively disabling GCC warnings for only a small code-segment : 仅针对较小的代码段选择性地禁用GCC警告

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-pmf-conversions"
    // Do the foul deed here
#pragma GCC diagnostic pop

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM