What C ++ functions can I put in a C function pointer?

I have a C library that uses function pointer constructs for callbacks. Callbacks will be called from C code.

extern "C" { typedef struct callbacks_t { void (*foo) (const char*); int (*bar) (int); } callbacks_t; }// extern C 

What C ++ functions can I safely place in these function pointers for calling from the C library? Static member functions? Fully defined template features? Doesn't capture Lambda?

g ++, apparently, allows me to use all of the above, but I doubt safety when various call conventions and language bindings are used for C and C ++ functions.

+6
source share
2 answers

I have a C library that uses a function pointer structure for callbacks. Callbacks will be called from C code.

The AC library only understands C. Thus, you can only transfer things that are explicitly supported and understood by C.

Since C ++ does not define call conventions for any type of function, you cannot explicitly pass C ++ functions. You can only return the C function (explicitly declared with extern "C" ) and guarantee its compatibility.

Like all undefined behavior, this might work (for example, returning normal C ++ functions or static members). But, like any vague behavior, this allows you to work. You simply cannot guarantee that it is truly correct or portable.

 extern "C" { typedef struct callbacks_t { void (*foo) (const char*); int (*bar) (int); } callbacks_t; // Any functions you define in here. // You can set as value to foo and bar. }// extern C 

What kinds of C ++ functions can I safely place in those function pointers that will be called from the C library?

Static member functions?

No, but it works on many platforms. This is a common mistake, and it will bite people on some platforms.

Fully defined template features?

No.

Not capturing lambdas?

No.

g ++ seems to allow me to use all of the above,

Yes. But it’s assumed that you are moving on to objects created using the C ++ compiler (which would be acceptable). The correct calling convention will be used as C ++ code. The problem is that you are passing these things to the C library (pthreads comes to mind).

+5
source

In general, if you are not using a cast, you should trust g ++.

Although it is true that none of the types of functions you are talking about can be exported for use inside C, this is not what you are asking for. You ask what functions you can pass as a function pointer.

To answer what you can convey, I think it is more constructive to understand that you cannot pass. You cannot pass anything that requires additional arguments not explicitly specified in the argument list.

So, non-static methods. They need an implicit "this". C will not know to pass it. Again, the compiler will not allow you.

No lambda capture. They require an implicit argument with the actual lambda body.

What you can pass are pointers to functions that do not require an implicit context. In fact, you went ahead and pointed them out:

  • Function pointer. It does not matter if it is a standard function or a template if the template is fully enabled. It's not a problem. Any syntax you write that leads to a function pointer will automatically fully resolve the pattern.
  • Does not capture lambda. This is a special work introduced by C ++ 11 when it introduced lambda. Since this is possible, the compiler performs the explicit conversion necessary to execute it.
  • Static methods. Since they are static, they do not skip implicit this , so they are fine.

The latter continues to expand. Many C callback mechanisms get a function pointer and void * opaq. The following is a standard and fairly safe possibility of using them with the C ++ class:

 class Something { void callback() { // Body goes here } static void exported_callback(void *opaq) { static_cast<Something*>(opaq)->callback(); } } 

And then do:

 Something something; register_callback(Something::exported_callback, &something); 

Edited to add: The only reason this works is that the C ++ calling convention and the C calling convention are identical when implicit arguments are not passed. There is a difference in name manipulation, but it doesn’t matter when you pass a pointer to a function, since the only purpose of name manipulation is to allow the linker to find the correct address of the function.

If you tried this trick with a pending callback, for example, a standard call to stdcall or pascal, this scheme would fall on his face.

This, however, is not unique to static methods, lambdas functions, and templates. Even the standard function does not work under such circumstances.

Unfortunately, when you define a pointer to a stdcall type, gcc ignores you:

 #define stdcall __attribute__((stdcall)) typedef stdcall void (*callback_type)(void *); 

leads to:

 test.cpp:2:45: warning: 'stdcall' attribute ignored [-Wattributes] typedef stdcall void (*callback_type)(void *); 
+2
source

All Articles