简体   繁体   中英

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. 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. 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).

Casting a virtual member function to void (*)(void *) sounds like a harsh approach.

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 . If that is correct, then you should:

  1. Register a static member of ClassA to the callback mechanism whose signature is:

     void (*)(ClassA& ref); 
  2. Add virtual member function that does the real work.

     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 .

     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 . 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. 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++.

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) :

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

    Use -Wno-pmf-conversions to remove the warning on use of the extension.
    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:

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

Selectively disabling GCC warnings for only a small code-segment :

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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