How to link a fortran subroutine to a cpp main program? - c++

I am trying to compile a c++ program but it does not work.
At first, I should said that c++ is not a language I really know, I use Fortran. Anyway, the main c++ program calls a fortran subroutine. I can compile this subroutine but when I want to compile the c++ program there is a link error.
The real program is just huge and calls many subroutines, so I did a simple test (simple program calling just one subroutine) and it does not work as well! When I want to create the .x my subroutine is undefined.
Here is the c++ program (test-TQINIT.cpp) and the subroutine (TQINIT.f).
test-TQINIT.cpp:
#include <iostream>
using namespace std;
extern "C"
{
void TQINIT_(int*, int*);
}
main()
{ int NWG;
NWG=80000;
int *IWSG = new int[NWG];
TQINIT_(IWSG,&NWG);
}
TQINIT.f:
SUBROUTINE TQINIT(IWSG,NWG)
IMPLICIT NONE
INTEGER NWG
INTEGER IWSG(NWG)
LOGICAL TQG2ERR
INTEGER IERR
CALL TQRSERR
CALL TQINI(NWG,IWSG)
IF (TQG2ERR(IERR)) THEN
WRITE(6,*)'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
WRITE(6,*)'ERROR INITIALIZING (TQINIT) !!!'
WRITE(6,*)'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
STOP
ENDIF
END
I compile the subroutine using:
gfortran -c TQINIT.f
and get a TQINIT.o
Then I compile the main program using:
g++ -c test-TQINIT.cpp
and get a test-TQINIT.o.
Then for create the .x I use (TQINIT.f need the library):
g++ test-TQINIT.o TQINIT.o -L/usr/local/thermocalc/3.0/SDK/TQ8 -ltq-linux-x86_64-gfortran44-8 -o test-TQINIT.x
This is what I get:
test-TQINIT.o: In function `main':
test-TQINIT.cpp:(.text+0x33): undefined reference to `TQINIT_'
collect2: ld returned 1 exit status
I hope someone will be able to help me.
Thank you by advance.

Fortran is case-insensitive, and the name of the subroutines in the object file normally (as far as I know) end up in lowercase with an underscore, so yours is probably named tqinit_ and not TQINIT_, so your call in C++ should be lowercase.
You can verify the name in the object file with the objdump -t TQINIT.o command given in Alexander Vogt's answer, or the simpler nm TQINIT.o which is more terse.

If you use the ISO_C_BINDING in Fortran, you will have control over the precise routine names that will be seen by the other language, e.g., case, no underscores. You can also declare the arguments so that consistency with C/C++ will be guaranteed. For more on this topic, see https://stackoverflow.com/questions/tagged/fortran-iso-c-binding

I'm no expert on mixing C and Fortran, but I have always linked the Fortran Code directly using
g++ test-TQINIT.cpp TQINIT.o -L/usr/local/thermocalc/3.0/SDK/TQ8 \
-ltq-linux-x86_64-gfortran44-8 -o test-TQINIT.x
instead of compiling those two files separately and then linking... Perhaps this helps (although your way should work as well, at least it does with the simple example I tried).
BTW:
You can find out how the functions in your Fortran object are called by issuing
objdump -t TQINIT.o
This should give you the correct function name to call.

Related

Naming of symbols in Fortran shared library, intel vs gcc?

Is there a way to control the naming of symbols in shared libraries? Specifically, I've been using GCC for a project where we access shared library through C-Types in Python. This works great, however recently I've been working with a system where the Intel compiler is suggested. I can build the shared object just fine but I find the symbols have a slightly different naming convention as compared to intel. Specifically when I compile with gcc the symbol names look like:
__test_function_MOD_read_a_file
the Intel compiled shared object has the symbol names like:
test_function_mp_read_a_file__
Is there a way to force consistency of the naming or at least change the name of the symbols after the fact?
For example consider the following bit of code test_function.f90
MODULE test_function
CONTAINS
SUBROUTINE read_a_file
PRINT *,'I did a thing!'
END SUBROUTINE
END
The compile line should look something like
gfortran -fPIC -c test_function.f90
gfortran -shared -o libtest_function.so test_function.o
Both compilers are mangling the names of the subprograms contained in the module. The Fortran standard does not mandate any naming convention. You can prevent the name mangling by using the ISO C Binding feature of Fortran to give the subprogram a specific name. For example,
module bar
contains
function fun(x) bind(c, name='foo')
real fun, x
fun = x
end function fun
function goo(x)
real goo, x
goo = x
end function goo
end module bar
When compiled with gfortran the resulting object file contains
gfortran -c a.f90
nm a.o
00000000 T __bar_MOD_goo
00000013 T foo
Thus, you can reference functionfun as foo in the library. You'll probably also want to use the types defined by the iso_c_binding module.

`undefined reference to `sgtsv_'` when compiling a tridiagonal solver

I'm working on a problem for one of my computing courses and I was getting stuck in an error after trying to work through it for some time.
Specifically, I'm working on this Fortran routine:
program linsolve_sgtsv
implicit none
integer, parameter :: lda=10,ldb=lda,nrhs=1
real :: dl(lda-1),d(lda),du(lda-1),b(ldb,nrhs)
integer :: n,info
n = 10
dl(1:n-1) = 1+(1/11); d(1:n) = 2; du(1:n-1) = 1-(1/11)
b(1:n,1) = (/1,1,1,1,1,1,1,1,1,1/)
call sgtsv(n,nrhs,dl,d,du,b,ldb,info)
if (info==0) then
print *, '--- x is ----------------'
print '(g22.16)', b(1:n,1)
else
print *, 'info = ',info
end if
end program linsolve_sgtsv
This routine solves linear systems using gaussian elimination when the matrices are tridiagonal.
However, when I try to create a makefile using:
gfortran linsolve_sgtsv.f90 -o linsolve.f90
I get the following errors:
tmp/ccCiRHve.o: In function `MAIN__':
linsolve_sgtsv.f90:(.text+0x100): undefined reference to `sgtsv_'
collect2: error: ld returned 1 exit status
I can't find any direct duplicate. Many people have similar problems, but they usually know a bit more and your question is more basic.
The tridiagonal solver sgtsv() is not a Fortran built-in subroutine. It is provided in a library called LAPACK and many other implementations (e.g., MKL). That means you must tell the compiler it should include (link) that library.
That is done by adding -llapack (-l means "link" and lapack is the name of the library which is stored in a file called liblapack.so or liblapack.a) at the end of the compile command.
You must have his library installed in your computer for the link flag to work. In many Linux distributions it is included in the repositories.
The correct line would be
gfortran linsolve_sgtsv.f90 -o linsolve -llapack
If the library (liblapack.a or liblapack.so) is installed in some directory the compiler can't see, you must use
gfortran linsolve_sgtsv.f90 -o linsolve -L/path/to/liblapack -llapack
See also gfortran LAPACK "undefined reference" error and your compiler's manual for more.
Notice I did -o linsolve instead of -o linsolve.f90. It is a very bad idea to name your executable file with a .f90 extension. Only Fortran source files should be named .f90.
There will be more problems in your code. They will appear when you start to test it. For example, yoy do
(1/11)
this will not return the mathematical result of 1.0/11.0, it will return 0. Learn about integer division in Fortran. See Why are the elements of an array formatted as zeros when they are multiplied by 1/2 or 1/3? and Simple program, that only produce zeros, bug?
If you will find more problems after you can run your code, you can ask a new question.
I recommend to compile your programs with -g -fcheck=all -Wall to find more bugs and errors more easily.

"no main" function for linking or execution in C++ [duplicate]

This question already has answers here:
How to change entry point of C program with gcc?
(4 answers)
Closed 5 years ago.
I am trying to compile a function (not called main) that can be integrated in another code or directly executed (after linking).
I try it one my mac, and work well.
I finally test it on Linux (CentOS and ubuntu). However, the task looks harder as expected on Linux.
The source code is the following one (just to explain the problem)
test.cpp:
#include <cstdio>
#ifdef __cplusplus
extern "C" {
#endif
int test(int argc, char const *argv[]);
#ifdef __cplusplus
}
#endif
int test(int argc, char const *argv[]) {
fprintf(stderr, "%s\n", "test");
return 0;
}
Compilation line on MacOS
g++ -c test.cpp -o test.o && g++ test.o -o test -e _test
and on Linux
g++ -c test.cpp -o test.o && g++ test.o -o test -e test
I try on my MacOS with clang, g++ and Intel compiler, all 3 works fine.
And I try with g++ and the Intel compiler on Linux, always, the same error.
usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
Any advice, explanation or solution, on what I am doing wrong or missing would be very helpful.
Thanks
Edit:
Currently, I have a "define" to create a main, but if we have lots of function we are obligated to do two compilations each time (one for the function version and one for the execution) and make finally the code heavier.
Like discussed in this topic is there a GCC compiler/linker option to change the name of main?
To don't do a XY I inherited from a bunch of small programs that I want to put to gather, that it is easier to use (for remote execution ...). However, each one need to be able to be executed independently if needed, for debugging,... I hesitate, between using "execv" and just convert each main as a function. I probably take the bad chose.
Edit:
The final goal is to be able to have independent programs. But that we can call from an external software too.
Solution:
The solution looks to be, to a call to the main through a dlopen
You cannot do that (and even if it appears to work on MacOSX it is implementation specific and undefined behavior).
Linux crt0 is doing more complex stuff that what you think.
The C standard (e.g. n1570 for C11) requires a main function for hosted implementations (ยง5.1.2.2.1) :
The function called at program startup is named main. The implementation declares no prototype for this function.
And the C++ standard also requires a main and strongly requires some processing (e.g. construction of static data) to be done before main is running and after it has returned (and various crt0 tricks are implementing that feature on Linux).
If you want to understand gory details (and they are not easy!), study the ABI and the (free software) source code of the implementation of the crt0.
I am trying to compile a function (not called main) that can be integrated in another code
BTW, to use dynamically some code (e.g. plug-ins) from another program, consider using the dynamic linker. I recommend using the POSIX compliant dlopen(3) with dlsym(3) on position-independent code shared libraries. It works on most Unix flavors (including MacOSX & Linux & Solaris & AIX). For C++ code beware of name mangling so read at least the C++ dlopen mini howto.
Read also the Program Library HowTo.
Problems with libraries, they cannot be executed, no ?
I don't understand what that means. You certainly can load a plugin then run code inside it from the main program dlopen-ing it.
(and on Linux, some libraries like libc.so are even specially built to also work as an executable; I don't recommend this practice for your own code)
You might take several days to read Drepper's How To Write Shared Libraries (but it is advanced stuff).
If you want to add some code at runtime, read also this answer and that one.
The final goal is to be able to have independent program. But that we can call from an external software too
You can't do that (and it would make no sense). However, you could have conventions for communicating with other running programs (i.e. processes), using inter-process communication such as pipe(7)-s and many others. Read Advanced Linux Programming first and before coding. Read also Operating Systems : Three Easy Pieces
The solution looks to be, to a call to the main through a dlopen
Calling the main function via dlopen & dlsym is forbidden by the C++ standard (which disallows using a pointer to main). The main function has a very specific status and role (and is compiled specially; the compiler knows about main).
(perhaps calling main obtained by dlsym would appear to work on some Linux systems, but it certainly is undefined behavior so you should not do that)

Linking legacy Fortran95 library to C++ with ifort/icpc

Background: I am in a situation where I have to make use of an old Fortran95 library in a new C++ project. The F95 library is extensive, has tons of small modules, poorly documented, and it was mostly auto-generated about a decade ago by some obscure computer algebra system (by people on a different continent). So basically heirloom code, but it works and it is currently irreplaceable.
Luckily I have the source code and it can be compiled with current versions of ifort, but I am not too familiar with Fortran, and would rather not touch the old code in any significant way.
So suppose I have this Fortran code (pes_shell.f90):
subroutine pes_init()
use pes,wp=>pes_wp
implicit none
real,parameter::auang=0.5291772083
call pes0_init (dir='coef')
call pes1_init (pes_x3y1z1u1_sysall)
return
end subroutine pes_init
The functions pes0_init(...) and pes1_init(...) lead into the abyssal depths of the Fortran library and they are contained in the pes module.
I can compile this to an object file, if I give ifort the path for the modules:
ifort -r8 -O2 -c pes_shell.f90 -I/home/debianuser/PES/PES_library/lib/mod
My POC C++ code, calling pes_init():
extern "C"{
void pes_init_();
}
int main(){
pes_init_();
return 0;
}
This can also be compiled to an object file, with icpc:
icpc -c PEStest.cpp
However I cannot figure out how to link the two object files, plus the truckload of fortran modules, into a final executable.
I have tried just simply using icpc, but it cannot seem to be able to find the Fortran functions, even if I give it the location of the module files:
icpc -I/home/debianuser/PES/PES_library/lib/mod -o test.x pes_shell.o PEStest.o
pes_shell.o: In function `pes_shell_mp_f_':
pes_shell.f90:(.text+0x595): undefined reference to `pes_x3y1z1u1_mp_pes_x3y1z1u1_pot_'
pes_shell.o: In function `pes_shell_mp_pes_init_':
pes_shell.f90:(.text+0x5f0): undefined reference to `pes0_mp_pes0_init_'
pes_shell.f90:(.text+0x603): undefined reference to `pes1c_mp_pes1_init_'
PEStest.o: In function `main':
PEStest.cpp:(.text+0x2b): undefined reference to `pes_init_'
EDIT:
Pointing the linker to the directory where libpes.a can be found eliminates the problem of locating the function that was referenced in the c++ code, but icpc still cannot find the fortran functions that are being called from fortran codes:
icpc -I/home/debianuser/PES/PES_library/lib/mod -L/home/debianuser/PES/PES_library/lib/pes-xyz -lpes -o test.x PEStest.o pes_shell_new.o
pes_shell_new.o: In function `f_':
pes_shell_new.f90:(.text+0x585): undefined reference to `pes_x3y1z1u1_mp_pes_x3y1z1u1_pot_'
pes_shell_new.o: In function `pes_init_':
pes_shell_new.f90:(.text+0x5e0): undefined reference to `pes0_mp_pes0_init_'
pes_shell_new.f90:(.text+0x5f3): undefined reference to `pes1c_mp_pes1_init_'

error occured when fortran code called c code

I have used a method in a reference book about fortran codes calling c codes. The method is that if your function name in the c codes are capitalized, you may not make additional changes in your fortran code. The following is my code.
Source1.f90:
program main
implicit none
call c_subprint()
endprogram
ccode.c:
#include <stdio.h>
#include <string.h>
#ifdef _cplusplus
extern "C" {
#endif
void _stdcall C_SUBPRINT()
{
int a;
printf("%s\n","kk");
scanf("%d",&a);
}
#ifdef _cplusplus
}
#endif
The error message is that the main function cannot find out _C_SUBPRINT(LNK2019). But I have already added the .lib generated by the c code to the fortran code. And my code is almost the same as that in the reference book. what is wrong?
Different Fortran compilers generate calls differently. Many add underscores to routine names to avoid name conflicts with C-code. Its very hard to say whether or not you should exactly follow the example of an unnamed reference book. In the past, to mix Fortran and C, you have to understand some of the compiler internals. Here the Fortran compiler appears to have added an underscore to the start of the name. You could probably fix the problem by adding that in your C++ code. But that won't be portable and in principle could change with compiler versions. There is a better way: the Fortran ISO_C_Binding. This is part of the Fortran language standard and therefore standard and portable. Examples are given in the gfortran manual in the Chapter "Mixed-Language Programming" and in other questions on Stackoverflow.
From the inclusion of _stdcall I deduce you are probably using a Windows platform. Unfortunately I'm working on a Mac, so things may not be exactly the same. However, the following works for me (small changes from your code) to get a mix of Fortran and C code working together. Note - on my Mac, the gcc compiler does not include Fortran, so I used a separate Fortran compiler gfortran which I downloaded from a link at http://hpc.sourceforge.net - where instructions for installation could also be found.
program src1.f90:
program main
implicit none
call C_SUBPRINT()
endprogram
program ccode.c:
#include <stdio.h>
#include <string.h>
void c_subprint_()
{
printf("hello world!\n");
}
Note I took out the #ifdef sections - I was using pure C, so it was not needed; more importantly, note the underscore after the function name
I compiled these two modules as follows:
gcc -c ccode.c -o ccode.o
gfortran src1.f90 ccode.o -o hello
After which I can run
./hello
And get the expected output:
hello world!
Without the underscore, the linker complains about unknown symbols. There is an option in the compiler to turn off "name mangling" with the underscore. You can check your specific compiler to see how that would be done - but that's almost certainly the problem you are having.