Using function pointer as callback - c++

In some SDK I have a method which takes function pointer.
int AutoRead(nAutoRead aEventFun)
where parameter is:
typedef int (__stdcall *nAutoRead)(char *data);
Now I want to use this function in my code like this:
// First need to get pointer to actual function from DLL
CV_AutoRead AutoRead; // CV_AutoRead is typedef for using function pointer
AutoRead = (CV_AutoRead)GetProcAddress(g_hdll,"AutoRead");
// Now I want to use the SDK method and set callback function,
// but I get error on the next line
// error is: 'initializing' : cannot convert from 'int (__cdecl *)(char *)' to 'TOnAutoRead'
nAutoRead f = &callbackFunc;
if(0 == AutoRead(f)) // AutoRead - now refers to the SDK function shown initially
{
}
where callbackFunc is:
int callbackFunc(char *data)
{
}
Apparently I am doing something wrong. But what?
ps. This is typedef for CV_AutoRead
typedef int (CALLBACK* CV_AutoRead)(nAutoRead aEventFun);

This has to do with the calling convention specifier __stdcall that the callback requires. By default your callbackFunc uses __cdecl, causing an error.
To fix this problem, declare callbackFunc as follows:
int __stdcall callbackFunc(char *);
You also need to add __stdcall to the function definition.
See Argument Passing and Naming Conventions for more information on this subject.

Related

Cast function pointer to (void*)

I have a simple question which is breaking my mind.
On my solution, I'm working with a DLL that offers me the following function:
int RegisterCallback(TCallbackType CallbackType, void *pLLTProfileCallback, void *pUserData);
For more information, the parameter pLLTProfileCallback is a function type defined as:
typedef void (__stdcall *TNewProfile_s)(const unsigned char *pData,
unsigned int nSize, void *pUserData);
I'm trying to insert my function 'NewProfile' in this parameter as follows:
RegisterCallback(STD_CALL, (void*)NewProfile, m_pLLT1);
But it says: 'Error: invalid type conversion'.
The function definition for 'NewProfile' is:
void __stdcall NewProfile(const unsigned char* pucData, unsigned int uiSize, void* pUserData){...}
Where is my error exactly? What can I do in order to fix it?

Passing parameter(s) to function(s) withing different project files in C++

I just am getting confused about passing the parameter to function in C++ over different projects.
I have two different solutions, of which one is a DLL project and another is Console project. Within the first project I have a piece of code as follows:
class __declspec(dllexport) FormatLog
{
public:
void __cdecl ParseFormat(LPCWSTR);
};
The rest of codes are not important herein, whereas in the second project I have the header file consisting of the following code:
class FormatImpl
{
public:
typedef void (__cdecl * FORMAT) (LPCWSTR);
HINSTANCE hModule;
FORMAT Format;
FormatImpl()
{
hModule = LoadLibrary(L"FormatLog.dll");
if (hModule != NULL)
{
Format = (FORMAT) GetProcAddress(hModule, "?ParseFormat#FormatLog##XXXXXXXX#X");
}
}
~FormatImpl()
{
if (hModule != NULL)
{
FreeLibrary(hModule);
}
}
};
When I was calling this from the main function using the following code:
int main(int argc, char* argv[])
{
FormatImpl format;
format.Format(L"%t %m %i %l");
return 0;
}
the parameter became invalid in function void __cdecl ParseFormat(LPCWSTR format); as <Bad Ptr> while inspecting through the Visual Studio 2010.
My question is, if I use GetProcAddress or LoadLibrary to call a .dll file invoking any method, shouldn't I thereby be legitimate to pass any parameter apart from int, double, long or so on to the requested method?
You have a major problem in your code: ParseFormat is not a function taking a LPWSTR and returning void but an instance method of class FormatLog. The difference is that for an instance method, you have a hidden this parameter.
If you have control on first project, the simplest way IMHO is to just use a static method to get rid of the hidden parameter:
class __declspec(dllexport) FormatLog
{
public:
static void __cdecl ParseFormat(LPCWSTR);
};
If you have no control on first project, the correct type for FORMAT would be a pointer to member. Unfortunately I could never find a way to convert the result of GetProcAddress to a pointer to member. Hopefully, it is known that that you can simply get the address of the member function and call it directly, provided you add the hidden this as first parameter. The code would become:
class FormatImpl
{
public:
typedef void (__cdecl *FORMAT) (FormatLog *l, LPCWSTR);
HINSTANCE hModule;
FORMAT Format;
FormatImpl()
{
hModule = LoadLibrary(L"FormatLog.dll");
if (hModule != NULL)
{
Format = (FORMAT) (void *) GetProcAddress(hModule,
"?ParseFormat#FormatLog##QAAXPB_W#Z");
}
}
...
}
(after getting the mangled name in FormatLog.exp) and you will use it like that:
int main(int argc, char* argv[])
{
FormatImpl format;
FormatLog l;
format.Format(&l, L"%t %m %i %l");
return 0;
}
Anyway, I generally think that the mangled names should be an implementation detail and I only export extern "C" functions from a DLL if I later want to import them manually through GetProcAddress.
So my advice would be to add in first project:
extern "C" {
__declspec(dllexport) void __cdecl doParseFormat(LPWSTR str) {
static FormatLog flog;
flog.ParseFormat(str);
}
}
Now you can simply write:
Format = (FORMAT) GetProcAddress(hModule, "doParseFormat");
which I personnaly find more clean, and you can use it easily.

Error converting void(__cdecl MyClass::*)() to void *

I am trying to link to an external library in my QT application. The external library has a header file with the following relevant code I'm trying to call:
extern VGRABDEVICE_API bool V_AssignFrameSizeCallback(IGrabChannel* pChannel, void* pFunc);
In the demo C++ program provided, which has no problems compiling, the following relevant code is:
// in main.cpp
void _stdcall MyFrameSizeCallback(T x) {
do_stuff;
}
int main(int argc, char* argv[]) {
IGrabChannel* pChannel0 = something;
V_AssignFrameSizeCallback(pChannel0, MyFrameSizeCallback);
}
I am trying to incorporate this code into my QT application, but getting problems. In my mainwindow.cpp file:
void _stdcall MainWindow::MyFrameSizeCallback(T x) {
do_stuff;
}
void MainWindow::someFunction() {
IGrabChannel* pChannel0 = something;
V_AssignFrameSizeCallback(pChannel0, &MainWindow::MyFrameSizeCallback);
}
The error I'm getting is:
error: C2664: 'bool V_AssignFrameSizeCallback(IGrabChannel *,void *)' :
cannot convert argument 2 from 'void (__cdecl MainWindow::* )(T)' to 'void *'
There is no context in which this conversion is possible
What do I need to do? Thanks.
You have two problems. First, void* is a data pointer, not a function pointer. According to the C++ standard, casting between the two is not expected to work. Some platforms provide a stronger guarantee... for example Windows GetProcAddress and *nix dlsym mix the two.
Next, your &MainWindow::MyFrameSizeCallback is not a function pointer, it is a pointer-to-member-function. Calling it requires a MainWindow object, which the external library doesn't know anything about.
You need to provide an ordinary function, not a member function, to the library. If you have some way to get ahold of the MainWindow* object pointer, you can then call its member function to do the real work. Sometimes the library provides a "context" parameter which is passed to your callback; that's a great place to put the object pointer. Otherwise, you'll need to store your MainWindow* in a global variable. Easy if you have just one, while if you have more than one you might go with std::map<IGrabChannel*, MainWindow*>.
Code:
MainWindow* MainWindow::the_window;
void MainWindow::MyFrameSizeCallback(T x)
{
do_stuff;
}
void _stdcall MyFrameSizeCallbackShim(T x)
{
MainWindow::the_window->MyFrameSizeCallback(x);
}
void MainWindow::someFunction()
{
IGrabChannel* pChannel0 = something;
the_window = this;
V_AssignFrameSizeCallback(pChannel0, &MyFrameSizeCallbackShim);
}
If the parameter x isn't an IGrabChannel, change the map datatype and insertion logic accordingly. If the parameter x isn't some sort of unique predictable identifier, you may be limited to only doing callbacks to one MainWindow instance.

Calling a C callback function from C++ via a lambda

I have this code in a plain C static library:
extern "C" {
typedef void (__cdecl* VisitChildren)(Option*);
void __cdecl DoVisitChildren(Children* List, VisitChildren Visitor);
}
And I'm trying to use it from some C++ code (unit tests) using a lambda.
...
DoVisitChildren(children, [&] (Option* option) {
...
});
I'm getting the compiler error C2664 ... cannot convert parameter 2 from 'unittests::UnitTest1::TestBuild::<lambda_b286d160b9bab3e09ab93cd59fc49f0b>' to 'VisitChildren'
If I remove the capture '&' it compiles and works, but I need to capture some bits and bobs.
Is this possible?
A closure created by a lambda expression can be implicitly converted to a function pointer, but only if it does not capture any variables. Also, it will be converted to a pointer to an extern "C++" function, not an extern "C" function, and technically those are incompatible types.
So no, you can't do this.
A hacky workaround is to store your actual closure in a global variable and pass a callback which invokes it. This will only work if your program is single-threaded and the DoVisitChildren call does not store the callback for later use.
std::function<void(Option*)> callback;
extern "C" void __cdecl invoke_callback(Option* o) { callback(o); }
// ...
callback = [&] (Option* option) { /* ... */ };
DoVisitChildren(children, invoke_callback);

Can't use SDL Threads

I'm using Visual Studio 2012 with SDL and I'm doing a simple threading task but I always get these errors:
argument of type "int (mainGame::*)(void *ptr)" is incompatible with parameter of type "int (__cdecl *)(void *)"
The other error I'm getting:
error C3867: 'mainGame::gameEvents': function call missing argument list; use '&mainGame::gameEvents' to create a pointer to member
This is how the function is written:
int gameEvents(void *ptr){
//do things here.
return 0;
}
This is the code I'm using to call the function:
SDL_Thread* gh;
gh = SDL_CreateThread(gameEvents,NULL);
Since you're using C++, you need to declare your function with C linkage, since that's what SDL expects, being a C library itself:
extern "C" int gameEvents(void *ptr);
Change gameEvents() to static. Otherwise your member function will have a "hidden" first argument for the this pointer that C APIs like SDL don't know about.
If you need access to instance data do something like this:
static int mainGame::gameEvents(void *ptr)
{
mainGame* game = (mainGame*)ptr;
//do things here.
return 0;
}
...
mainGame game;
SDL_Thread* gh;
gh = SDL_CreateThread( mainGame::gameEvents, &game );