global barThe "Link With:" box (under "Options") tells NetRun to link together two different projects, in this case one in C++ and the other in assembly. The C++ code calls the assembly here.
bar:
add rdi,1000
mov rax,rdi
ret
extern "C" int bar(int param);
int foo(void) {
return bar(6);
}
You can call C++ code from assembly almost as easily, by making the
C++ code extern "C", using "extern someName" in assembly, and then call
the function normally.
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. This makes life a little simpler, but it means you can't overload functions in plain C or assembly.
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 */Definitely try these things out yourself:
extern "C" {
#endif
void one_assembly_routine(int x);
void another_assembly_routine(char c);
#ifdef __cplusplus /* only defined in C++ code */
}
#endif
int bar(int i,int j) {(executable NetRun link)
printf("bar(%d,%d)\n",i,j);
return i;
}
extern "C" int bar(int i,int j);(executable NetRun link)
int foo(void)
{
return bar(2,3);
}
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... |
extern "C" {From assembly, it looks pretty straightforward--the function pointer is passed like any another function argument (here, in rdx).
int careful_add(int a,int b,int (*errorfunction)(void));
};
extern "C"
int adderr(void)
{
std::cout<<"Program error detected: overflow during add.\n";
exit(0); // should stop entire program now.
}
int foo(void) {
int a=500000000,b=500000000,c=800000000,d=500000000;
int sum=careful_add(
careful_add(a,b,adderr),
careful_add(c,d,adderr),
adderr);
return sum;
}
; int careful_add(int a,int b,void *errfunction);
global careful_add ; it's a function
careful_add:
add edi,esi
jo somewhere
mov eax,edi
ret
somewhere:
call rdx
ret
When passing... |
In C/C++, you... |
In Fortran, you... |
an int |
pass the int |
pass a pointer to the integer |
an array |
pass a pointer to the first element of the array |
pass a pointer to the first element of the array |
a char |
pass an int containing the character's value |
pass a pointer to the character |
function foo()Here's a "do loop" (the Fortran equivalent of C/C++ "for"):
INTEGER foo
i = 7;
foo = i + 3;
end function
function foo()
INTEGER foo
do i=1,10
CALL print_int(i)
end do
foo = i + 3;
end function
Note that "print_int" is defined in NetRun's "inc.c" as:
CDECL void print_int__(int *i) {print_int(*i);}
Here,