C/C++, FORTRAN, underscores, and GNU Autotools

2020-07-20 13:17发布

问题:

I have a question about mixed-language programming (C/C++ and FORTRAN) using gcc and gfortran. I've searched plenty of "mixing fortran with language X" and haven't been able to resolve this.

I'm not sure if this is a linking problem or a compiler problem, or both.

I've created three files and I'm using GNU Autotools to build the crude application, but should be able to build the app from command line independently.

The C File (main.c) will be the driving app, that calls several FORTRAN functions:

/* this is a simple program */

#include <stdio.h>

/* add the extern function definition */
#include "fooonly.h" 

// this is not working for the mixed language programming stuff yet... 

/* this is the main program section */
int main( int argc, char *argv[] )
{
   int a = 36;
   int b = 24;
   int c = 0;

   printf( "hi mom\n" );

   /* now call a FORTRAN function from C */
   c = NGCD( &a, &b );
   printf( "NGCD(%d,%d)=%d\n", a, b, c );

   return 0;

}

The fortran function which will most often contain FORTRAN 77 (but can also include FORTRAN90/95 code too), looks like:

c$$$      The following introductory example in FORTRAN 77 finds the
c$$$     $     greatest common divisor for two numbers A and B using a
c$$$     $     verbatim implementation of Euclid's algorithm.

      FUNCTION NGCD(NA, NB)
      IA = NA
      IB = NB
 1    IF (IB.NE.0) THEN
         ITEMP = IA
         IA = IB
         IB = MOD(ITEMP, IB)
         GOTO 1
      END IF
      NGCD = IA
      RETURN
      END

Using Dev. Studio 6/Compaq Digital Fortran 6.0, this works fine. In fact, I don't have to use the #define ifdef __cplusplus/#endif and can simply create a C file that looks like:

/* this is a simple program */

#include <stdio.h>

/* add the extern function definition */
extern "C" int __stdcall NGCD( int *a, int *b );

/* this is the main program section */
int main( int argc, char *argv[] )
{
   int a = 36;
   int b = 24;
   int c = 0;

   printf( "hi mom\n" );

   /* now call a FORTRAN function from C */
   c = NGCD( &a, &b );
   printf( "NGCD(%d,%d)=%d\n", a, b, c );

   return 0;
}

and compile it with the FORTRAN listing above, the application links, runs, and generates the correct results.

C:\fooonly>fooonly.exe
hi mom
NGCD(36,24)=12

C:\fooonly>

When I try top repeat this process using GNU Autotools on MinGW or OSX, I continue to get the following errors:

macbook:makecheck $ make
gcc -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE=\"fooonly\" -DVERSION=\"1.0.2\" -I.     -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c
mv -f .deps/main.Tpo .deps/main.Po
gfortran  -g -O2   -o fooonly main.o ngcd.o  
Undefined symbols for architecture x86_64:
  "_NGCD", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
make: *** [fooonly] Error 1
macbook:makecheck $ 

where the Makefile (generated by GNU Autotools), basically contains the following commands:

macbook:makecheck $ gcc -c main.c
macbook:makecheck $ gfortran -c ngcd.f
macbook:makecheck $ gcc -o fooonly main.c ngcd.o
Undefined symbols for architecture x86_64:
  "_NGCD", referenced from:
      _main in cc9uPBWl.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
macbook:makecheck $ 

My configure.in script contains nothing more than:

AC_INIT(main.c)
AM_INIT_AUTOMAKE(fooonly, 1.0.2)

## C/C++ compiler section
AC_PROG_CC

## fortran section
AC_PROG_F77

## output section
AC_OUTPUT(Makefile)

which is essentially,

macbook:makecheck $ gcc -c main.c
macbook:makecheck $ gfortran -c ngcd.f
macbook:makecheck $ gcc -o fooonly main.c ngcd.o

right?

I'm trying to build this on multiple platforms (Linux, Win32/64, OSX, etc.) and wish to use GNU Autotools, and I know this is done with other open-source projects, but the configure.in scripts for those projects are way beyond my GNU Autotools newbie chops and I get a little overwhelmed trying to decode them.

I'm guessing this has something to do with:

1) The definitions I've used in the configure.in script, 2) I'm not including some magical set of switches (i.e. -fno-second-underscore?), or 3) Some combination of the two?

Am I close and if so, how do I get the app to build?

回答1:

As long as you have a compiler newer then the past several years, I recommend using the ISO C Binding to mix Fortran with other languages. Then you can skip the name mangling with underscores and similar compiler/platform dependent issues. If you have legacy FORTRAN 77 code that you don't want to alter, you could write a small Fortran 2003 glue routine between the C and the FORTRAN 77. Older instructions describe the previous method which required more understanding of the internal interfaces and was more compiler/platform dependent. For the new method, look at the gfortran manual chapter "Mixed Language Programming" and previous questions/answers here.

With Fortran code it is easier to link with gfortran because that brings in the Fortran runtime libraries. I think that the same applies to C++, so if you have both you will have to explicitly include the runtime library of one or the other.

P.S. Here is an example using the Fortran ISO C Binding:

function NGCD (na, nb) bind (C, name="NGCD")
   use iso_c_binding
   implicit none
   integer (c_int) :: ngcd
   integer (c_int), intent (in) :: na, nb
   integer (c_int) :: ia, ib, itemp
   ia = na
   ib = nb

   do while (ib /= 0)
      itemp = ia
      ia = ib
      ib = mod(itemp, ib)
   end do

   ngcd = ia

   return
end function NGCD

Compile/link with:

gcc -c main.c
gfortran main.o ngcd.f90


回答2:

I never got a sufficient answer, so I cobbled together a temporary solution (ignores case in the FORTRAN code) and posted it on my forufus blog. I will have to work out the compiler switches in the future.