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

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

Related

Can we define a C++ method in a C file?

Assume that we have a C++ project. We can use a function which defined in a C file in the C++ project through extern "C" keyword. Example:
C code: cfile.c
#include <stdio.h>
void f(void)
{
printf("\n This is a C code\n");
}
Cpp Code: cppfile.cpp
#include <iostream>
extern "C" {
void f();
}
int main(void)
{
f();
return 0;
}
So, can we do that for the opposite case:
Cpp code: cppCode.cpp
struct MyClass
{
virtual void f(const char* text);
void func(const char* text);
};
Can we implement f method and func method in a C file (for instance: cCode.c) ??? Is that possible ? (Just the question for my personal purpose)
You can't do this directly. However, there is a workaround (of sorts). You can have a function with "C" linkage which returns the pointer to the object of the class - presumably dynamically allocated - converted to void*.
Than you can implement functions which would map to functions inside the class, but would take one additional argument - void*, which would be the address obtained from the call to creation function above. They would simply convert this void* to the type of the class and call it's member.
In the end, you'd need a dispose function which would corectly delete the pointer.

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.

Using function pointer as callback

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.

Assign C++ instance method to a global-function-pointer?

Greetings,
My project structure is as follows:
\- base (C static library)
callbacks.h
callbacks.c
paint_node.c
.
.
* libBase.a
\-app (C++ application)
main.cpp
In C library 'base' , I have declared global-function-pointer as:
in singleheader file
callbacks.h
#ifndef CALLBACKS_H_
#define CALLBACKS_H_
extern void (*putPixelCallBack)();
extern void (*putImageCallBack)();
#endif /* CALLBACKS_H_ */
in single C file they are initialized as
callbacks.c
#include "callbacks.h"
void (*putPixelCallBack)();
void (*putImageCallBack)();
Other C files access this callback-functions as:
paint_node.c
#include "callbacks.h"
void paint_node(node *node,int index){
//Call callbackfunction
.
.
putPixelCallBack(node->x,node->y,index);
}
I compile these C files and generate a static library 'libBase.a'
Then in C++ application,
I want to assign C++ instance method to this global function-pointer:
I did something like follows :
in Sacm.cpp file
#include "Sacm.h"
extern void (*putPixelCallBack)();
extern void (*putImageCallBack)();
void Sacm::doDetection()
{
putPixelCallBack=(void(*)())&paintPixel;
//call somefunctions in 'libBase' C library
}
void Sacm::paintPixel(int x,int y,int index)
{
qpainter.begin(this);
qpainter.drawPoint(x,y);
qpainter.end();
}
But when compiling it gives the error:
sacmtest.cpp: In member function ‘void
Sacm::doDetection()’:
sacmtest.cpp:113: error: ISO C++
forbids taking the address of an
unqualified or parenthesized
non-static member function to form a
pointer to member function. Say
‘&Sacm::paintPixel’ sacmtest.cpp:113:
error: converting from ‘void
(Sacm::)(int, int, int)’ to ‘void
()()’
Any tips?
This is answered in the C++ FAQ, [1]. This doesn't work, because the pointer isn't associated with a particular object instance. The solution is given there too, create a global function that uses a particular object:
Sacm* sacm_global;
void sacm_global_paintPixel(int x,int y,int index)
{
sacm_global->paintPixel(x, y, index);
}
void Sacm::doDetection()
{
putPixelCallBack = &sacm_global_paintPixel;
//call somefunctions in 'libBase' C library
}
You have to somehow setup the global variable properly.
You cannot convert an instance method pointer to a normal function pointer. A workaround is to use another global variable to hold the instance and a global wrapper function that is used as the callback and then in turn calls the instance method:
Sacm *callbackSacm;
extern "C" // since it sounds like it's called from a C library
void call_paintPixel(int x, int y, int index) {
callbackSacm->paintPixel(x, y, index);
}
void Sacm::doDetection() {
callbackSacm = this;
putPixelCallBack = call_paintPixel;
}
You can alternatively use a static member function. The address of a static member function can be taken and assigned to a regular function pointer, because no this pointer is implicitly passed to it -- under the hood, these functions operate just like regular non-member functions. But they have advantages over non-member functions:
A static method still has access to the private and protected members of any object of its class type.
A static method can be private or protected so access to it can be controlled.
Using a static method lets you and group functionality inside the class, where it belongs.

C++: namespace conflict between extern "C" and class member

I stumbled upon a rather exotic c++ namespace problem:
condensed example:
extern "C" {
void solve(lprec * lp);
}
class A {
public:
lprec * lp;
void solve(int foo);
}
void A::solve(int foo)
{
solve(lp);
}
I want to call the c function solve in my C++ member function A::solve. The compiler is not happy with my intent:
error C2664: 'lp_solve_ilp::solve' : cannot convert parameter 1 from 'lprec *' to 'int'
Is there something I can prefix the solve function with? C::solve does not work
To call a function in the global namespace, use:
::solve(lp);
This is needed whether the function is extern "C" or not.
The C functions are in the global namespace. So try
::solve(lp)
Please try ::solve
Simply ::solve(lp). Note you also need a semicolon after your class declaration.