Interfacing C++, C, Assembly, Fortran and Other Languages

CS 301 Lecture, Dr. Lawlor

Name Mangling and extern "C"

C++ "mangles" the linker names of its functions to include the data types of the function arguments. This is good, because it lets you overload function names; but it's bad, because plain C and assembly don't do anything special to the linker names of functions.

In C or assembly, a function "foo" shows up as just plain "foo" in the linker. In C++, a function foo shows up as "foo()" or "foo(int,double,void *)". (Check out the disassembly to be sure how your linker names are coming out.)

So if you call C or assembly code from C++, you have to turn off C++'s name mangling by declaring the C or assembly routines 'extern "C"', like this:

extern "C" void some_assembly_routine(int param1,char *param2);
or wrapped in curly braces like this:
extern "C" {
void one_assembly_routine(int x);
void another_assembly_routine(char c);
}

In fact, it's common to provide a "magic" header file for C code that automatically provides 'extern "C"' prototypes for C++, but just works normally in plain C:

#ifdef __cplusplus /* only defined in C++ code */
extern "C" {
#endif
void one_assembly_routine(int x);
void another_assembly_routine(char c);
#ifdef __cplusplus /* only defined in C++ code */
}
#endif
Definitely try these things out yourself:

Plain C bar routine:
int bar(int i,int j) {
printf("bar(%d,%d)\n",i,j);
return i;
}
(executable NetRun link)

C++ foo routine that calls bar:
extern "C" int bar(int i,int j);
int foo(void)
{
return bar(2,3);
}
(executable NetRun link)

Try:
Code written in
With name
Has linker name
C++
int bar(int a,int b)
bar(int,int)    <- But "mangled" to be alphanumeric...
C++
extern "C" int bar(int a,int b) bar
C
int bar(int a,int b)
bar
Assembly
global bar
bar:
bar
Fortran
SUBROUTINE bar()
bar_, BAR, BAR_, bar__, or some such.  Disassemble to be sure...

Bottom line: to call code written in anything else (C, Assembly, Fortran) from C++, or to call C++ from anything else, add extern "C" to the C++ code!

Argument Passing

C and C++ are kinda asymmetrical, because "int" parameters are placed directly on the stack (like "push 3"), while arrays are always passed via pointer (like "push my_array", which pushes the *address* of the array, not the actual integers in the array).  C/C++ do this because you can cheaply copy an "int", but copying an array might take a lot of time and memory.

Fortran, curiously, passes *everything* via pointer--if a Fortran function takes an int parameter, what gets pushed on the stack is a *pointer* to an int, not the int itself!

To summarize:
When passing...
In C/C++, you...
In Fortran, you...
an int
push the int
push a pointer to the integer
an array
push a pointer to the first element of the array
push a pointer to the first element of the array
a char
push an int containing the character's value
push a pointer to the character

Fortran 1D arrays are indexed using round brackets, like "myarr(i)".  And the index of the first array element in Fortran is "myarr(1)", not "myarr[0]" like C/C++.  But beyond those small differences, arrays work exactly the same in Fortran as in C/C++, and in fact it's not always possible from looking at the generated assembly code whether the original code was written in C, C++, Fortran, or Assembly!

Fun With Fortran!

CS 301 isn't a computer languages course, but it is interesting to look at old-school Fortran.  Note how this little function returns 10, like you'd expect.  And the assembly code is pretty much exactly what you'd get from C/C++!
       function foo()
INTEGER foo

i = 7;
foo = i + 3;

end function

(executable NetRun link)

Here's a "do loop" (the Fortran equivalent of C/C++ "for"):
       function foo()
INTEGER foo

do i=1,10
CALL print_int(i)
end do
foo = i + 3;

end function

(executable NetRun link)

Note that "print_int" is defined in NetRun's "inc.c" as:
    CDECL void print_int__(int *i) {print_int(*i);}
Here,

This sort of Fortran/C/C++ interfacing is really common in big projects.