|
||||
|
Section 32:
|
[32.8] How can I pass an object of a C++ class to/from a C function?
Here's an example (for info on extern "C", see the previous two FAQs). Fred.h:
/* This header can be read by both C and C++ compilers */
#ifndef FRED_H
#define FRED_H
#ifdef __cplusplus
class Fred {
public:
Fred();
void wilma(int);
private:
int a_;
};
#else
typedef
struct Fred
Fred;
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__STDC__) || defined(__cplusplus)
extern void c_function(Fred*); /* ANSI C prototypes */
extern Fred* cplusplus_callback_function(Fred*);
#else
extern void c_function(); /* K&R style */
extern Fred* cplusplus_callback_function();
#endif
#ifdef __cplusplus
}
#endif
#endif /*FRED_H*/
Fred.cpp:
// This is C++ code
#include "Fred.h"
Fred::Fred() : a_(0) { }
void Fred::wilma(int a) { }
Fred* cplusplus_callback_function(Fred* fred)
{
fred->wilma(123);
return fred;
}
main.cpp:
// This is C++ code
#include "Fred.h"
int main()
{
Fred fred;
c_function(&fred);
...
}
c-function.c:
/* This is C code */
#include "Fred.h"
void c_function(Fred* fred)
{
cplusplus_callback_function(fred);
}
Unlike your C++ code, your C code will not be able to tell that two pointers
point at the same object unless the pointers are exactly the same
type. For example, in C++ it is easy to check if a Derived* called
dp points to the same object as is pointed to by a Base*
called bp: just say if (dp == bp) .... The C++ compiler
automatically converts both pointers to the same type, in this case to
Base*, then compares them. Depending on the C++ compiler's
implementation details, this conversion sometimes changes the bits of a
pointer's value.
(Technical aside: Most C++ compilers use a binary object layout that causes this conversion to happen with multiple inheritance and/or virtual inheritance. However the C++ language does not impose that object layout so in principle a conversion could also happen even with non-virtual single inheritance.) The point is simple: your C compiler will not know how to do that pointer conversion, so the conversion from Derived* to Base*, for example, must take place in code compiled with a C++ compiler, not in code compiled with a C compiler. NOTE: you must be especially careful when converting both to void* since that conversion will not allow either the C or C++ compiler to do the proper pointer adjustments! The comparison (x == y) might be false even if (b == d) is true:
void f(Base* b, Derived* d)
{
if (b == d) {
As stated above, the above pointer conversions will typically happen with
multiple and/or virtual inheritance, but please do not look at
that as an exhaustive list of the only times when the pointer
conversions will happen.
You have been warned. If you really want to use void* pointers, here is the safe way to do it:
void f(Base* b, Derived* d)
{
void* x = b;
void* y = static_cast<Base*>(d); ← If conversion is needed, it will happen in the static_cast<>
if (x == y) {
|
|||