Search code examples
cfortranmixed-programming

minimum modification to make a visual studio FORTRAN-C mixed code gfortran-gcc compatible


I'm trying to modify this code (gist as back up) to become gfortran-gcc compatible.

  • I removed the [VALUE] tags
  • used POINTER with -fcray-pointer flag for gfortran, instead of the [REFERENCE] tag
  • removed the __stdcall , because tried the #define __stdcall __attribute__((stdcall)) which caused warning: ‘stdcall’ attribute ignored [-Wattributes] now this is what I have:

C code CMAIN.C:

#include <stdio.h>

extern int FACT_(int n);
extern void PYTHAGORAS_(float a, float b, float *c);

main()
{
    float c;
    printf("Factorial of 7 is: %d\n", FACT_(7));
    PYTHAGORAS_(30, 40, &c);
    printf("Hypotenuse if sides 30, 40 is: %f\n", c);
}

the FORTRAN code FORSUBS.FOR:

  INTEGER*4 FUNCTION Fact (n)
  INTEGER*4 n 
  INTEGER*4 i, amt
  amt = 1
  DO i = 1, n
    amt = amt * i
  END DO
  Fact = amt
  END

  SUBROUTINE Pythagoras (a, b, cp)
  REAL*4 a
  REAL*4 b
  POINTER (cp, c)
  REAL*4 c
  c = SQRT (a * a + b * b)
  END

the Makefile:

all:
    gfortran -c FORSUBS.FOR -fcray-pointer
    gcc -c CMAIN.C
    gfortran -o result.out FORSUBS.o CMAIN.o  
    rm -rf *.o

clean :
    rm -rf *.out *~ *.bak *.o

However I still get the error:

CMAIN.o: In function `main':

CMAIN.C:(.text+0x1d): undefined reference to `FACT_(int)'

CMAIN.C:(.text+0x4c): undefined reference to `PYTHAGORAS_(float, float, float*)'

I would appreciate if you could help me know:

  • What is the issue and how can I resolve it?
  • what is the best method to modify the original code to become gcc-gfortran compatible with minimum change.

P.S.1. also shared on Reddit. P.S.2. operating system and compiler specifications are same as this question.


Solution

  • In addition to my top comments, Fortran passes by reference, so you have to modify the .c and the .for files.

    The code below works. There may be a simpler way to declare things, but this should [at least] get you further along. Caveat: I haven't done much fortran since Fortran IV days, so I'm a bit rusty. I'd defer to Vladimir's VALUE solution as a better way to go.


    #include <stdio.h>
    
    #if 0
    extern int fact_(int n);
    extern void pythagoras_(float a, float b, float *c);
    #else
    extern int fact_(int *n);
    extern void pythagoras_(float *a, float *b, float *c);
    #endif
    
    int
    main(void)
    {
        float c;
    #if 0
        printf("Factorial of 7 is: %d\n", fact_(7));
    #else
        int n = 7;
        printf("Factorial of 7 is: %d\n", fact_(&n));
    #endif
    
    #if 0
        pythagoras_(30, 40, &c);
    #else
        float a = 30;
        float b = 40;
        pythagoras_(&a, &b, &c);
    #endif
        printf("Hypotenuse if sides 30, 40 is: %f\n", c);
    
        return 0;
    }
    

        INTEGER*4 FUNCTION Fact (n)
        INTEGER*4 n
        INTEGER*4 i, amt
        amt = 1
        DO i = 1, n
            amt = amt * i
        END DO
        Fact = amt
        END
    
        SUBROUTINE Pythagoras (a, b, c)
        REAL*4 a
        REAL*4 b
        REAL*4 c
        c = SQRT (a * a + b * b)
        END
    

    Here is the program output:

    Factorial of 7 is: 5040
    Hypotenuse if sides 30, 40 is: 50.000000
    

    UPDATE:

    I get the same undefined reference error from your code!

    Aha!

    One thing I didn't mention [because I didn't think it would make a difference] is that I changed the source file names to use all lowercase letters (e.g. CMAIN.C --> cmain.c and FORSUBS.FOR --> forsubs.for)

    With that, the output of nm *.o produces:

     cmain.o:
                     U fact_
    0000000000000000 T main
                     U printf
                     U pythagoras_
    
    forsubs.o:
    0000000000000000 T fact_
    0000000000000045 T pythagoras_
    

    The change in the fortran source filename doesn't matter too much. But, the C source filename does!

    More to the point it's the filename suffix (i.e. .C changed to .c).

    This is because gcc will [try to be smart and] look at the suffix to determine which language the file is written in and compile accordingly. For example, gcc -c foo.cpp will compile the file as if it is written in c++ and not c, just as if the command were: g++ -c foo.cpp

    Although .cpp is the [more] usual suffix for a c++ filename, an alternate suffix for c++ files is: .C

    That is, most projects use the .cpp convention, but some use the .C convention. One of the reasons for preferring .cpp over .C is that Windows filesystems are case insensitive. So, .C and .c would appear the same. However, POSIX systems (e.g. linux, macOSX, iOS, android, etc.) have case sensitive filenames so either convention can be used.

    So, gcc -c CMAIN.C will compile as c++. This does c++ style "name mangling" of symbols--not what we want. In c++, the mangling is done to allow "overloading" of function names. That is, two [or more] different functions can have the same name, as long as they use different arguments. For example:

    void calc(int val);
    void calc(int val1,int val2);
    void calc(double fval);
    

    Here is the output of nm *.o if we use CMAIN.C:

     CMAIN.o:
    0000000000000000 T main
                     U printf
                     U _Z11pythagoras_PfS_S_
                     U _Z5fact_Pi
    
    FORSUBS.o:
    0000000000000000 T fact_
    0000000000000045 T pythagoras_
    

    Running that file through c++filt to "demangle" the c++ names we get:

     CMAIN.o:
    0000000000000000 T main
                     U printf
                     U pythagoras_(float*, float*, float*)
                     U fact_(int*)
    
    FORSUBS.o:
    0000000000000000 T fact_
    0000000000000045 T pythagoras_
    

    So, try to use lowercase filenames if possible [that is recommended best practice]. But, at a minimum, don't use .C