C++ : Linkage-to/relationship-with C


PART 16


Q106: How can I call a C function `f()' from C++ code?
A: Tell the C++ compiler that it is a C function:  extern "C" void f();
Be sure to include the full function prototype.  A block of many C functions
can be grouped via braces, as in:
	extern "C" {
	  void* malloc(size_t);
	  char* strcpy(char* dest, const char* src);
	  int   printf(const char* fmt, ...);
	}



Q107: How can I create a C++ function `f()' that is callable by my C code?
A: Use the same `extern "C" f()' construct as detailed in the previous
question, only then proceed to actually define the function in your C++ module.
The compiler will ensure that the external information sent to the linker uses
C calling conventions and name mangling (ex: preceded by a single underscore).
Obviously you can't make several overloaded fns simultaneously callable by a C
program, since name overloading isn't supported by C.

Caveats and implementation dependencies:
* your `main()' should be compiled with your C++ compiler.
* your C++ compiler should direct the linking process.



Q108: Why's the linker giving errors for C/C++ fns being called from C++/C fns?
A: See the previous two questions on how to use `extern "C"'.



Q109: How can I pass an object of a C++ class to/from a C function?
A: Here's an example of one that will work (be sure to read the tail of this
answer which details when such a scheme will *not* work):

	/****** C/C++ header file: X.h ******/
	#ifdef __cplusplus    /*`__cplusplus' is #defined iff compiler is C++*/
	  extern "C" {
	#endif

	#ifdef __STDC__
	  extern int c_fn(struct X*);		/* ANSI-C prototypes */
	  extern struct X* cplusplus_callback_fn(struct X*);
	#else
	  extern int c_fn();			/* K&R style */
	  extern struct X* cplusplus_callback_fn();
	#endif

	#ifdef __cplusplus
	  }
	#endif

	#ifdef __cplusplus
	  class X {
	    int a;
	  public:
	    X();
	    void frob(int);
	  };
	#endif

Then, in file `X.C':
	//X.C
	#include "X.h"
	X::X() : a(0) { }
	void X::frob(int aa) { a = aa; }
	X* cplusplus_callback_fn(X* x)
	{
	  x->frob(123);
	  return x;
	}

In C++ file `main.C':
	#include "X.h"
	int main()
	{
	  X x;
	  c_fn(&x);
	  return 0;
	}

Finally, in a C file `c-fn.c':
	/* C source file c-fn.c */
	#include "X.h"
	int c_fn(struct X* x)
	{
	  if (cplusplus_callback_fn(x))
	    do_one_thing();
	  else
	    do_something_else();
	  return something();
	}

Passing ptrs to C++ objects to/from C fns will FAIL if you pass and get back
something that isn't *exactly* the same pointer, such as passing a base class
ptr and receiving a derived class ptr (this fails when multiple inheritance is
involved, since C fails to do pointer-conversion properly).



Q110: Can my C function access data in an object of a C++ class?
A: Sometimes.

(First read the previous question on passing C++ objects to/from C functions.)
You can safely access a C++ object's data from a C function if the C++ class:
 * has no virtual functions (including inherited virtual fns)
 * has all its data in the same access-level section (private/protected/public)
 * has no fully-contained subobjects with virtual fns

If the C++ class has any base classes at all (or if any fully contained
subobjects have base classes), accessing the data will *technically* be
non-portable, since class layout under inheritance isn't imposed by the
language.  However in practice, all C++ compilers do it the same way: the base
class object appears first (in left-to-right order in the event of multiple
inheritance), and subobjects follow.

Furthermore you can often (but less than always) assume a `void*' appears in
the object at the location of the first virtual function.  This is trickier,
since the first virtual function is often in a different access specifier
section than the data members.  Even the use of a single pointer is not
required by the language (but this is the way `everyone' does it).

If the class has any virtual base classes, it is more complicated and less
portable.  One common implementation technique is for objects to contain an
object of the virtual base class (V) last (regardless of where `V' shows up as
a virtual base class in the inheritance DAG), with the rest of the object's
parts appearing in the normal order.  Every class that has V as a virtual base
class actually has a *pointer* to the V part of the final object.



Q111: Why do I feel like I'm `further from the machine' in C++ as opposed to C?
A: Because you are.  Being an OOPL, C++ allows you to directly model the
problem domain itself (obviously OOD is more complex than this, but a good
start at `finding the objects' is to model the nouns in the problem domain).
By modeling the problem domain, the system interacts in the language of the
problem domain rather than in the language of the solution domain.

One of C's great strengths is the fact that it has `no hidden mechanism'.  What
you see is what you get.  You can read a C program and `see' every clock cycle.
This is not the case in C++; overloaded operators are a case in point.  Old
line C programmers (such as many of us once were) are often ambivalent about
this feature, but soon we realize that it provides a level of abstraction and
economy of expression which lowers maintenance costs without destroying runtime
performance.

Naturally you can write assembly code in any language; using C++ doesn't
guarantee any particular level of quality, reusability, abstraction, or any
other measure of `goodness'.  C++ doesn't try to make it impossible for bad
programmers to write bad programs; it enables good programmers to write good
programs.