How to get a “simple” function pointer from a memb

2019-08-31 10:41发布

问题:

I'm having a problem with function pointers and nothing I found on the net helped me to solve this problem.

I have a function from a C API which take a pointer of a void function :

extern int APIFunction(int, void (*func)(int));

I have a class with the function I would like to put when I call the API function.

class MyClass
{
   public:
      void myFunction(int status, otherAPi arguments...);
};

Then, I created a pointer to my member function and created a new instance of my class

typedef  void (MyClass::*MyClassFunctionPointer)(int stat, otherAPi arguments...);
MyClassFunctionPointer fctPointer= &MyClass::myFunction; 
LicenseSecurity instance;  

I get an error when I try to call my APi function with the function pointer I created:

int stat = APIFunction(5, fctPointer ); // -> error 1
int stat = APIFunction(5, instance.*fctPointer ); // -> error 2

I got errors respectively in the first and second case:

E2034 Impossible to convert 'void (MyClass::*)(int, otherAPITypes...)' into 'void (*) (int, otherAPITypes...)'
E2342 Bad type correspondence in the parameter 'func' ('void (*)(int, otherAPITypes...)' desired, 'void(int, otherAPITypes...)' obtained)

I don't have access to the API function so I can't modify it. To summary the problem: how How to get a "simple" C function pointer to put in argument of a function from a member function of my class?

Thanks

回答1:

Unfortunately, you can't. Sorry.

Ideally, your API would accept something like std::function that would allow you to wrap free functions or member functions. But if you can't modify the API, then you have no choice but to provide a free function.



回答2:

You can't get a "simple" function pointer to a non-static member function because the function requires a this pointer when called. If you were to create a function pointer like that then when the function was called there would be no this pointer for it to reference.



回答3:

With an ancient C API like that, you unfortunately don't have any way to do this.

What you have to do is make a static or non-member function to take the callback, and then figure out which instance of the object to call the member on. Some C APIs allow a user data to be passed to the callback, and in that case you use that to store the this pointer in question. If that's not an option you can use a global or singleton object and only allow a single such callback to be registered.



回答4:

You can declare the callback as either a standalone function or as a static method of the class. The tricky part is accessing a class instance pointer inside the callback.

Ideally, a well-designed API allows you to specify a user-defined value to callbacks. That allows you to easily pass in a class instance and access it directly inside the callback. But it sounds like you are not working with such an API, so you need to use a workaround.

If you have only 1 class instance being used with the API at a time, you can store the instance pointer into a global variable, and have the callback use the global variable to access the instance.

But if you have multiple class instances being used at the same time, you are looking for a thunking solution, similar to the VCL's MakeObjectInstance() function, which allows TWndMethod-signatured class methods to be used as Win32 window procedure callbacks. Essentially, a block of executable memory is dynamically allocated, stub assembler code is written into the block, and the instance pointer and class method pointer are stored in the block as well. The block is then passed to the API as if it were a function pointer. When the API calls the "function", the stub code gets executed, which has to manipulate the call stack and CPU registers to call the stored class method pointer passing the stored instance pointer as its hidden this parameter, while preserving the semantics of other parameters, the call stack, function result, etc.

Nothing in C++ really accomplishes that kind of thunking natively. It is not difficult to implement manually, but it is not trivial either (have a look at the source code for MakeObjectInstance() in the VCL's Classes.pas source file). The hardest part is coming up with the necessary stub code that matches the semantics of your particular class method's signature.