Search code examples
c++cnumerical

Is this C callback safe with C++ objects?


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.


Solution

  • 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.