3

I really need your help! I'm on a deadline and I'm trying to learn just enough to get some work done. It's been well over a week now that I'm dealing with what appears to be a straightforward issue but I haven't been able to successfully implement solutions online.

Long story short: I need to call C++ code from F77. I'm compiling with g++ and gfortran. I'm a complete newb to makefiles. When these codes are compiled as part of their respective programs, they are bug free (I'm taking a function from my C++ code, not main(), and trying to use it with the fortran code). Here's what I've got:

C++ Code:

#include <cmath>
#include <vector>
using namespace std;

extern"C" double ShtObFun(double x[], int &tp)
{
    return //double precision awesomeness
}

Fortran Code:

    subroutine objfun(nv, var, f, impass)
    implicit real(8) (a-h,o-z), integer (i-n)
c   initializations including tp, used below

    f = ShtObFun(var, tp)

    return
    end

Makefile (shows only files listed above):

all:
    g++ -c Objective_Functions.cpp
    gfortran -c -O3 opcase1.f
    gfortran opcase1.o Objective_Functions.o -fbounds-check -lstdc++ -g -o Program.out
    rm *.o

Error:

opcase1.o: In function 'objfun_':
opcase1.f:(.text+0xbd): undefined reference to 'shtobfun_'
collect2: ld returned 1 exit status

I have tried this a variety of other ways and they did not work. I can list those later if requested. Does anyone see the issue here?

The sites I've checked:

calling C++ function from fortran not C, Linking fortran and c++ binaries using gcc, , Calling C Code from FORTRAN, Cookbook - Calling C from Fortran, YoLinux - Using C/C++ and Fortran together

Edit (response to first answer):

If I rewrite C++ code as:

#include <cmath>
#include <vector>
using namespace std;

double ShtObFun(double x[], int &tp)
extern"C" double shtobfun_(double *x, int *tp) {
    return ShtObFun(x, *tp);
}
{
    cout << "reached tp = " << tp << endl;
    exit(1);
}

I get this error: error: expected initializer before 'extern' error: expected unqualified-id before '{' token

If I rewrite C++ code as:

#include <cmath>
#include <vector>
using namespace std;

double ShtObFun(double x[], int &tp);

extern"C" double shtobfun_(double *x, int *tp) {
    return ShtObFun(x, *tp);
}

double ShtObFun(double x[], int &tp)
{
    cout << "reached tp = " << tp << endl;
    exit(1);
}

The code will compile but the result I get is "reached tp = 0", while it should say "reached tp = 1" because I initialized tp to 1 in the fortran code (integer tp = 1). And I get the same issue if I simply declare the function as:

extern"C" double shtobfun_(double *x, int *tp)
{
     //cout, etc
}
4

2 回答 2

9

declare or alias

extern"C" double ShtObFun(double x[], int &tp)

as

extern"C" double shtobfun_(double x[], int &tp)

see http://gcc.gnu.org/onlinedocs/gcc/Weak-Pragmas.html

That's you first step. Second step is to recognize that Fortran has no idea about references, moreover it passes all arguments as a pointer. so you F77 interface should be declared as:

extern"C" double shtobfun_(double x[], int *tp);

Putting it all together:

double ShtObFun(double x[], int &tp)
extern"C" double shtobfun_(double *x, int *tp) {
    return ShtObFun(x, *tp);
}
于 2013-08-28T15:18:56.030 回答
3

I would recommend using the Fortran 2003 features for interoperability with C, as described here:

http://gcc.gnu.org/onlinedocs/gfortran/Interoperable-Subroutines-and-Functions.html

The Fortran interface would then look like this:

      interface
         function shtobfun(x,tp) bind(C, name="ShtObFun")
           use iso_c_binding, only: c_double, c_int
           real(c_double) :: shtobfun
           real(c_double) :: x(*)
           integer(c_int) :: tp
         end function shtobfun
      end interface

For most current compilers, this should be fine, even if the rest of your routine is in a Fortran 77 style (as long as you are consistent about fixed/free form source). Using bind(C) allows you to avoid changing your C++ code to deal with caps and underscores.

Edit:

The following subroutine compiled for me using gfortran with -ffixed-form. By adding iso_c_binding at the very top, I've made sure that anything implicitly declared in this function is a C-compatible double or int, which may or may not be what you want.

However, using implicit declarations is a very bad idea in the first place. You should really use implicit none and explicitly declare every variable you use.

      subroutine objfun(nv, var, f, impass)
      use iso_c_binding, only: c_double, c_int
      implicit real(c_double) (a-h,o-z), integer(c_int) (i-n)
      interface
         function shtobfun(x,tp) bind(C, name="ShtObFun")
           import :: c_double, c_int
           real(c_double) :: shtobfun
           real(c_double) :: x(*)
           integer(c_int) :: tp
         end function shtobfun
      end interface

      dimension :: var(1)
      integer(c_int) :: tp

      f = ShtObFun(var, tp)

      return
      end

One more thing: notice that var has to be an array; I don't know its actual dimensions, so I just made it size 1 in this example. However, it can be any rank.

于 2013-08-29T02:19:06.577 回答