1

My purpose is to call some C function from my C++ code and pass some C++ objects. In fact I am using a integration routine from the GSL libray(written in C), see this link,

My code snippet:

// main.cpp

#include <stdio.h>
#include <gsl/gsl_integration.h>
#include <myclass.h>

/* my test function. */
double testfunction ( double x , void *param ) {
    myclass *bar=static_cast<myclass*>(param);

    /*** do something with x and bar***/ 

    return val;

    }

int main ( int argc , char *argv[] ) {

    gsl_function F;  // defined in GSL: double (* function) (double x, void * params)

    /* initialize.*/
    gsl_integration_cquad_workspace *ws = 
    gsl_integration_cquad_workspace_alloc( 200 ) ;    

    /* Prepare test function. */
    myclass foo{}; // call myclass constructor
    F.function = &testfunction;
    F.params =   &foo;


    /* Call the routine. */
    gsl_integration_cquad( &F, 0.0,1.0,1.0e-10,1.0e-10,ws, &res,&abserr,&neval); 


    /* Free the workspace. */
    gsl_integration_cquad_workspace_free( ws );

    return 0;

    }

In my case, direct calling gsl_integration_cquad seems OK, provided the header includes sth like "ifdef __cplusplus", my concern is about the callback F,originally defined in C, am I allowed to pass the testfunction and also the C++ foo object in this way ? .

or is there any better way to do this kind of stuff, maybe overloading and use a functor?

P.S. Am I allowed to do exeption handling within the callback function? (use try catch inside "testfunction"). It works in my case but not sure if it's legal.

Community
  • 1
  • 1
lorniper
  • 626
  • 1
  • 7
  • 29
  • your way is good enough - there is no limit to make things unnecessary complex. you can mix c and c++ in any way provided your linker manage to build final executable or shared object. – lowtech Nov 06 '14 at 19:12
  • The short answer is yes, this is fine. However, I would add `__cdecl` to `testfunction`'s declaration, just in case it's not the default on a given platform. – Cameron Nov 06 '14 at 19:18
  • @lowtech thanks, anyway, I frequently heard mixing c and c++ causes problems... – lorniper Nov 06 '14 at 19:18
  • @Cameron could you elaborate a little bit more? never heard of __cdecl, sigh... – lorniper Nov 06 '14 at 19:19
  • @lorniper: It specifies the calling convention (`__cdecl` means 'the C calling convention', but it's a compiler-specific keyword, not a standard one -- having said that, it's supported by most, and is typically placed between the return type and the function name). If the calling convention doesn't match what the library is expecting, it won't call the function the right way, with results ranging from a crash to more subtle stack corruption bugs. `__cdecl` is usually the default, but not always. – Cameron Nov 06 '14 at 19:22
  • @lorniper As is, his code isn't legal C++, and shouldn't compile. He needs to make `testfunction` `extern "C"`. – James Kanze Nov 06 '14 at 19:23
  • 3
    @JamesKanze NOPE, it is legal C++, no need to make it external "C". external "C" is used when you trying to link C object files and what to prevent name mangling on certain function – lowtech Nov 06 '14 at 19:25
  • 1
    @lowtech Not according to the standard. §7.5/1: "Two function types with different language linkages are distinct types even if they are otherwise identical." If the structure he's working with is declared in an `extern "C"` block, then the pointer to function is a pointer to an `extern "C"` function, and has a different type than that of his function. (And FWIW, I've used compilers where the calling conventions were different between C and C++.) – James Kanze Nov 06 '14 at 19:31

3 Answers3

6

I'm not familiar with the library in question, but in general, when passing a pointer to a callback and a void* to a C routine, which will call the callback back with the void*, there are two things you need to do to make it safe:

  • The function whose address you pass must be declared extern "C". You'll get away with not doing this with a lot of compilers, but it isn't legal, and a good compiler will complain.

  • The type you convert to the void* must be exactly the same type as the type you cast it back to in the callback. The classic error is to pass something like new Derived to the C function, and cast it back to Base* in the callback. The round trip Derived*void*Base* is undefined behavior. It will work some of the time, but at other times, it may crash, or cause any number of other problems.

  • And as cdhowie pointed out in a comment, you don't want to allow exceptions to propagate accross the C code. Again, it might work. But it might not.

For the exact example you posted, the only thing you need to do is to declare testfunction as extern "C", and you're all right. If you later start working with polymorphic objects, however, beware of the second point.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • I've been trying with [Ideone c++11](http://ideone.com/3pxksi) and was surprised. – πάντα ῥεῖ Nov 06 '14 at 19:24
  • thanks, I use g++ and seems evenif I do not specify the testfunction as "extern C", there's no problem, as long as the header of gsl_integration_cquad includes "ifdef __cplusplus extern C". – lorniper Nov 06 '14 at 19:29
  • It may be worth pointing out that a C++ exception could cause the stack to unwind over C code, which may not be prepared for this possibility. That is, a C++ exception in the context of the callback could leave the workspace object in a bad state. – cdhowie Nov 06 '14 at 19:35
  • @lorniper Both g++ and VC++ are buggy here. A good compiler will at least give you a warning. I've worked on platforms where it made a difference. (Microsoft uses a non-standard mechanism for specifying the calling conventions, even thought the C++ standard provides what is needed. This is probably because they supported different conventions long before they supported C++.) – James Kanze Nov 06 '14 at 19:35
  • @cdhowie Good point. It will work with most compilers, but there's absolutely no guarantee. – James Kanze Nov 06 '14 at 19:36
  • @JamesKanze Well, my comment has more to do with the behavior of the C code. Since you don't skip stack frames in C unless you are using setjmp/longjmp, the C code could be in the middle of something that will be interrupted. This could cause things like memory leaks, file handle leaks, or leave objects in a bad state. It has nothing to do with the compiler, just the interplay between a language with exceptions and a language without exceptions sharing a stack. (Another case where there is no reason it wouldn't compile, but where the runtime behavior might be crazy.) – cdhowie Nov 06 '14 at 19:38
  • @cdhowie But in fact, a C compiler may not do whatever is needed to ensure that stack walkback works correctly. (And of course, there's no reason for C code to be exception safe either. But then, there's a lot of C++ code which isn't either.) – James Kanze Nov 06 '14 at 19:41
  • @JamesKanze Indeed. But even if the compiler does, it still won't know to `free()` stuff in C code that was `malloc()`ed but never put somewhere where it will be freed later. So either (1) the compiler is bad and stuff blows up, (2) the compiler is good and the C code was in the middle of something that didn't get rolled back, so stuff gets leaked and/or is probably going to blow up later, or (3) the compiler is good and the C code wasn't in the middle of anything, nothing is leaked, and everything is in a good state. In this case I'd say (2) is very likely. – cdhowie Nov 06 '14 at 19:46
  • Basically my point is that even if the compiler walks the stack back correctly in this case, the C code is likely to be in the middle of a mutation that causes the state of an object to become bad when the mutation is aborted in the middle. – cdhowie Nov 06 '14 at 19:47
  • @cdhowie I just tested that, within the body of C callback function, I can still use try catch to handle exceptions, is that odd? maybe because this C callback is opaque to the true C library function? – lorniper Nov 07 '14 at 16:43
  • @lorniper It's not odd at all; your code is C++ so it can use C++ constructs. Callbacks are always opaque in the sense that the library calling it doesn't have any idea what it will do. – cdhowie Nov 07 '14 at 17:36
2

You can use

myclass *bar=static_cast<myclass*>(param);

with void*.

If you meant something like transporting a c++ class pointer through a c callback's void* pointer, yes it's safe to do a static_cast<>.

There's no kind of losing c++ specific attributes of this class pointer, when passed through c code. Though passing a derived class pointer, and static casting back to the base class, won't work properly as @James Kanze pointed out.

Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • Many C library provide such void* interfaces, and I frequently use the static cast, seems to me quite safe. maybe you can explain more, because I simply can't change the interface to C library function, then how am I supposed to feed the C callback with C++ objs??? – lorniper Nov 06 '14 at 19:13
  • [This answer](http://stackoverflow.com/questions/310451/should-i-use-static-cast-or-reinterpret-cast-when-casting-a-void-to-whatever) says to prefer `static_cast`. – Barry Nov 06 '14 at 19:19
  • @πάνταῥεῖ I'd avoid the `reinterpret_cast`; it will work, but it gives the wrong message to the person reading the code. – James Kanze Nov 06 '14 at 19:24
  • @Barry I'd also say to prefer `static_cast`, but the answer you cite is completely wrong with regards to the standard. (`void` _is_ an object type. It's an incomplete type which can't be completed, but a `void*` counts as a pointer to object.) – James Kanze Nov 06 '14 at 19:27
1

The void* will likely just be passed trough by the C library without looking at the pointed-to data, so it's not a problem if this contains a C++ class. As log as you cast the void* to the correctly there shouldn't be any problems.

To make sure the callback function itself is compatible, you can declare it as extern "C". Additionally you should make sure that no exceptions are thrown from the callback function, since the C code calling the callback won't expect those.

All together I would split up the code into one function that does the real work and another function that is used as the callback and handles the interface with the C library, for example like this:

#include <math.h>

double testfunction ( double x ,myclass *param ) {
    /*** do something with x and bar***/ 
    return val;
}

extern "C" double testfunction_callback ( double x , void *param ) {
    try {
       myclass *bar=reinterpret_cast<myclass*>(param);
       return testfunction(x, bar);
    }
    catch(...) {
       std::cerr << "Noooo..." << std::endl;
       return NAN;
    }
}
sth
  • 222,467
  • 53
  • 283
  • 367