(note, if you don't want to read the whole thing here, probably you'll want to have a look at this question here.)
First of all, I'll try to conduct this answer from the historical perspective, as you are asking a need that comes with time, but original design of neither C++ nor C# was to make them compatible languages. I shall involve C in the middle, as the design requirement of C++ to be compatible with C made a lot of design decisions on it to solve the problem (and this will make a lot of light on the subject). As such, it is the only language design on which source (and not binary) compatibility has been an original requirement. There are two reasons to follow this approach:
- C# was designed later than C++, so if any of these has some compatibility properties in its design, it is probable that C# has them, as C++ had no binary compatibility design decisions made to C# (it didn't exist at the time of C++ definition)
- As C++ was designed with "compatibility" requirements in mind, to be able to use the vast set of library routines available from C, it is a language that has some way to express this compatibility statements somehow, so it is possible to have some point of reference that allows you to search for, in the documentation.
Compiling a C program with a C compiler has some drawbacks when talking about interfacing C and C++ routines between each other. When C++ was designed, it was with C compatibility in mind. But the opposite is not true, nobody designed C with "being C++ compatible" in mind. The only way to be able to access C++ functions from C is to compile those with a C++ compiler and do the compatibility modifications to deal with the intrinsic incompatibilities of both languages (or use a C compiler, and then link the binaries together using a C++ linker) This last point is very important link them with a C++ linker (or to link in C++ mode)
Why is so important to use a C++ linker to link C and C++ objects together? Mainly because the C linker is not prepared to include the needs of the C++ language (global objects instantiation, calling their constructors, is not supported by the C linker) In the case of Java or C++, the object instances are always (at least in Java, cannot assure what happens in C#) managed by reference. C# was designed as a language for the .Net virtual machine specification (as Sun did also with Java) and as you have C++ compilers for that platform also, C++ was designed not thinking on a virtual target platform, and probably the possibility of such an arrangment of using C++ from C# will be at least difficult.
Let's talk about the C/C++ case:
First, for the C++ functions to be reachable from the C code, they must have "C"
calling interface (it is possible that some language extension is neccessary to make C++ method calls and functions compatible with C#). This means the C++ functions you'll call from C need to be declared as
extern "C" void my_cplusplus_function(type1 arg1, type2 arg2);
or put all in a
extern "C" {
...
}
block. The reason for this is that C++ compilers mangle external identifiers (including parameter types and other function attributes, as the const
attribute on methods...) to allow different versions for the same function name with different interfaces, and this is not compatible with the C calling interface. Of course, this is also true for C# (perhaps there's some extern "C#"
extension in Visual C++ compiler to make name mangling work with C# language definitions). If you don't follow this approach, the first thing you have is to check which one of the possible definitions with that name is the one you want, then check how the compiler mangles the complete prototype definition to know the final name the linker gets and is used to access the function, and finally, to respect the complete environment of stack frames, function activation records, etc. that will make the program to hang if not followed. If you have interest on the Java/C++ way (full documented in www.java.com) you'll see the limitations imposed by Java/C++ function calls (it is document under the epigraph JNI, probably if you look for a C sharp native interface, you'll get something) That is due to the different memory organisations of executables, even for the same target platform.
An example of the "C compatibility mode" (sorry, I don't have an equivalent for C# or Java at hand) will illustrate this:
main.c
#include <stdio.h> /* for printf */
#include <stdlib.h> /* for EXIT_SUCCESS */
#include <math.h> /* for normal sqrt() */
#include "cpp_sqrt.h" /* for the prototype of cpp_mysqrt() */
int main()
{
/* you are forced to use stdio here, as you are in plain C */
printf("cpp_sqrt(3.0) => %lg\n"
"sqrt(3.0) => %lg\n",
cpp_sqrt(3.0), /* <== this is a c++ function */
sqrt(3.0)); /* <== this is the math C function */
return EXIT_SUCCESS;
}
cpp_mysqrt.cc
#include "cpp_sqrt.h"
#include <iostream>
double cpp_sqrt(const double x)
{
/* you can use all C++ stuff here, as this is C++ */
std::cout << "in cpp_sqrt()" << std::endl;
double g = 1.0, /* geometric mean */
a = x; /* arithmetic mean */
const double epsilon = 1.0E-6;
while ((a - g) > epsilon) {
a = (a + g) / 2.0;
g = x / a;
}
return g;
}
cpp_mysqrt.h
/* this construction is required to be able to include this
* file in C and C++ source code. C++ compilers always define
* the __cplusplus macro, so they will include the
* extern "C" block, while C compilers don't. This makes it
* possible to use the same prototype header file in both C and
* C++ source files without problems.
*/
#ifdef __cplusplus
extern "C" {
#endif
double cpp_sqrt(double X);
#ifdef __cplusplus
} /* extern "C" */
#endif
If you erase the extern "C" {
and }
parts of the header file (or simply rename __cplusplus
to __cplusplus1
for C++ compiler not to use it), the C++ compiler will mangle the name for the cpp_sqrt(double)
function, making it unreachable from C code, as you'll see if you try to compile it:
$ ed cpp_sqrt.h <<'EOF'
1,$s/__cplusplus/__cplusplus1/g
w
EOF
$ make
cc -O -pipe -c main.c -o main.o
c++ -O -pipe -c cpp_sqrt.cc -o cpp_sqrt.o
c++ -o cppfromc main.o cpp_sqrt.o -lm
main.o: In function `main':
main.c:(.text+0x10): undefined reference to `cpp_sqrt'
c++: error: linker command failed with exit code 1 (use -v to see invocation)
*** Error code 1
Stop.
make: stopped in /home/user/cfromcpp
So finally, how is the problem you have solved in modern times? The developer that faces such a problem normally builds an "interface set of functions" that comply with the interface restrictions for both worlds and acts as glue code (sure, you have heard that last term) to make the interface possible. This makes it possible to have Java access raw sockets, or access lowlevel operating system functionality.
It also makes it possible that, if your library interface changes in the future, you only have to change the glue module to make it work again with the new version.
Note
Next is the Makefile
used to build the sample code, just execute make
to build the program:
Makefile
RM ?= rm -f
targets = cppfromc
toclean=$(targets)
cppfromc_objs = main.o cpp_sqrt.o
toclean += $(cppfromc_objs)
cppfromc_libs = -lm
all: $(targets)
clean:
$(RM) $(toclean)
# NOTE: you must use the C++ linker because C++ programs have a different memory map,
# to allow for constructors of global object instances to be constructed before main()
# is executed.
cppfromc: main.o cpp_sqrt.o
$(CXX) $(LDFLAGS) -o $@ $($@_objs) $($@_libs)
main.o cpp_sqrt.o: cpp_sqrt.h