Is this C callback safe with C++ objects?

这一生的挚爱 提交于 2019-12-13 00:32:53

问题


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.


回答1:


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.




回答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.




回答3:


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


来源:https://stackoverflow.com/questions/26787464/is-this-c-callback-safe-with-c-objects

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!