问题
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 likenew Derived
to the C function, and cast it back toBase*
in the callback. The round tripDerived*
→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