Michel Feinstein
Michel Feinstein

Reputation: 14286

Using c++ class method as a function callback

I am using a camera API that calls a callback function every time there is a new image. The callback function is passed as:

BOOL WINAPI StTrg_SetTransferEndCallback(HANDLE hCamera, funcTransferEndCallback func, PVOID pvContext);

Where funcTransferEndCallback is:

typedef void (WINAPI *funcTransferEndCallback)(HANDLE hCamera, DWORD dwFrameNo, DWORD dwWidth, DWORD dwHeight, WORD wColorArray, PBYTE pbyteRaw, PVOID pvContext);

I don't want to make a callback function but actually use a method in a class instead. I am calling this StTrg_SetTransferEndCallback() inside my classe's constructor, so I can initialize the camera and pass my classe's method to capture every new frame and process it.

My method is defined in my class .h as:

public class Camera
{
public:
.....
void TakePicture(string pictureFileName);
void StartRecording(string videoFileName);
void StopRecording(void);
 .....
private:
volatile bool takePictureFlag;
volatile bool startRecordingFlag;
volatile bool stoptRecordingFlag;
void __stdcall TransferEndCallback(HANDLE hCamera, DWORD dwFrameNo, DWORD dwWidth, DWORD dwHeight, WORD wColorArray, PBYTE pbyteRaw, PVOID pvContext);
};

If I try this:

StTrg_SetTransferEndCallback(cameraHandle, TransferEndCallback, NULL);

Or this:

StTrg_SetTransferEndCallback(cameraHandle, this->TransferEndCallback, NULL)

Visual Studio 2013 says:

error C3867: 'MyNamespace::MyClass::TransferEndCallback': function call missing argument list; use '&MyNamespace::MyClass::TransferEndCallback' to create a pointer to member

So I use it as suggested:

StTrg_SetTransferEndCallback(cameraHandle, &MyNamespace::MyClass::TransferEndCallback, NULL)

And I receive this error message:

error C2664: 'BOOL StTrg_SetTransferEndCallback(HANDLE,funcTransferEndCallback,PVOID)' : cannot convert argument 2 from 'void (__stdcall MyNamespace::MyClass::* )(HANDLE,DWORD,DWORD,DWORD,WORD,PBYTE,PVOID)' to 'funcTransferEndCallback' There is no context in which this conversion is possible

So, is it possible to pass a member method as a function? Is this a case for std::bind?

Upvotes: 0

Views: 1302

Answers (3)

dxiv
dxiv

Reputation: 17648

Usual pattern is to pass the instance pointer this into the PVOID pvContext parameter, then use it within the callback to call a member function.

public class Camera
{
// ...
void ThisTransferEndCallback(HANDLE hCamera, DWORD dwFrameNo, DWORD dwWidth, DWORD dwHeight, WORD wColorArray, PBYTE pbyteRaw);
static void __stdcall TransferEndCallback(HANDLE hCamera, DWORD dwFrameNo, DWORD dwWidth, DWORD dwHeight, WORD wColorArray, PBYTE pbyteRaw, PVOID pvContext);
};

Set the callback as

StTrg_SetTransferEndCallback(cameraHandle, TransferEndCallback, this);

Then write the static callback to dispatch it to the member function

void __stdcall Camera::TransferEndCallback(HANDLE hCamera, DWORD dwFrameNo, DWORD dwWidth, DWORD dwHeight, WORD wColorArray, PBYTE pbyteRaw, PVOID pvContext)
{
    ((Camera *)pvContext)->ThisTransferEndCallback(hCamera, dwFrameNo, dwWidth, dwHeight, wColorArray, pbyteRaw);
}

The above doesn't include error checking etc.

Upvotes: 2

Anon Mail
Anon Mail

Reputation: 4770

The interface requires a function callback. In C++ that means a non-member function or static function. That's because both of these types of functions are invoked without the need for an instance of an class object. Member functions of a class are invoked differently and require an instance of a class object.

If you need more explanation, others can elaborate on how, under the hood, member functions are call.

Upvotes: 2

Marc
Marc

Reputation: 413

StTrg_SetTransferEndCallback(cameraHandle, &MyNamespace::MyClass::TransferEndCallback, NULL)

That will not work, because there is no object to call the method on. As Anon Mail suggested, a static function is the best way to do that. But, you stated that you can't do that.

My suggestion is to refactor your class into a singleton and add a static forward-function. Example:

public class Camera
{
public:
static *Camera the()
{
  if (!singleton)
    singleton = new Camera;
  return singleton;
}
void destroy() { delete singleton; singleton = nullptr; }
 .....
private:
static void TransferEndCallbackForward(HANDLE hCamera, DWORD dwFrameNo, DWORD dwWidth, DWORD dwHeight, WORD wColorArray, PBYTE pbyteRaw, PVOID pvContext)
{
  assert(singleton != nullptr); // Implement your own error handling here
  singleton->TransferEndCallback(hCamera, dwFrameNo, dwWidth, dwHeight, wColorArray, pbyteRaw, pvContext);
}
void __stdcall TransferEndCallback(HANDLE hCamera, DWORD dwFrameNo, DWORD dwWidth, DWORD dwHeight, WORD wColorArray, PBYTE pbyteRaw, PVOID pvContext);
static Camera *singleton = nullptr;
};

I hope that can help you.

Upvotes: 1

Related Questions