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
}
Community
  • 1
  • 1
Eric Inclan
  • 327
  • 1
  • 5
  • 14

2 Answers2

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);
}
Anycorn
  • 50,217
  • 42
  • 167
  • 261
  • Beautiful! I tried something like that before but I guess I didn't control my programming experiments properly. Too many bugs going on at once. THANK YOU!! – Eric Inclan Aug 28 '13 at 15:24
  • Actually, I have a follow-up question @Anycorn: I tried replacing my line "extern"C" double ShtObFun(double x[], int &tp)" with your final suggestion, and it did not compile. Can you please rephrase how this should be implemented? Also, why do you have ShtObFun(x, *tp); inside the extern function declaration? – Eric Inclan Aug 28 '13 at 18:29
  • I believe that @Anycorn is incorrect about references; an extern "C" function that takes a reference in C++ has the same interface as a C function that takes a pointer. – Sean Patrick Santos Aug 29 '13 at 02:21
  • Never mind. It looks like treating a C++ reference as a C pointer is the usual behavior for compilers, but is not technically guaranteed to work, so you should probably use a pointer for whatever function has extern "C". But the issue is C/C++ interoperability rather than Fortran/C interoperability. – Sean Patrick Santos Aug 29 '13 at 02:37
  • @user1837009 Yes, i missed that. – Anycorn Aug 29 '13 at 02:50
  • @EricInclan sorry, was away for day. what is the error you are seeing? – Anycorn Aug 29 '13 at 02:51
  • @EricInclan the reason for `ShtObFun(x, *tp)` is to dereference pointer and pass it to the c++ proper function (which takes reference to int as argument). You don't particularly want to deal with pointers in c++ directly so fortran interface needs to take care of that. – Anycorn Aug 29 '13 at 03:00
  • Hi @Anycorn - I edited the question so you can see the errors. Let me point out that in the declaration: extern"C" shtobfun_(double *x, double *tp) I dereference the pointer so that my cout statement behaves properly. Thanks for the feedback. – Eric Inclan Aug 29 '13 at 11:32
  • 1
    @EricInclan be careful passing integer types from f77 to c++ and vice versa - your f77 types could easily be 8 bytes. run your program thru gdb and print values AND types. – Anycorn Aug 29 '13 at 13:33
  • This is one reason that I prefer to use ISO_C_BINDING in the Fortran code when possible; the "c_int" kind allows you to declare a Fortran integer that's the same size as you would get in C, regardless of whether or not the default integer is the right size. – Sean Patrick Santos Aug 29 '13 at 19:35
  • @SeanPatrickSantos I spent half of PhD interacting with old legacy F77 (no advantage of 2003) and that's one thing that drove me crazy and still does - arbitrary int size. Still have to think about that linking to BLAS/Lapack – Anycorn Aug 29 '13 at 19:56
  • @Anycorn - would you believe me if I told you that when I try to declare it as INTEGER*2, INTERGER*4, INTEGER*8 that the compiler says "Error: Syntax error in data declaration"? I also tried INTEGER(8), same error. So if I can't declare it using any word other than "integer" what else can I do to account for the size? – Eric Inclan Aug 30 '13 at 04:37
  • @SeanPatrickSantos - I'm not ignoring your advice, by the way. I'm trying to implement it as well (see my comment to your response). – Eric Inclan Aug 30 '13 at 04:37
  • @EricInclan usually the integer type matching happens on the C side, by selecting appropriate int64_t or int32_t as the interface integer type. – Anycorn Aug 30 '13 at 17:29
  • I GOT IT TO WORK!!!!!!!!!!!!! FINALLY!!!!!!!!!!!! Thank you all for your responses. For people wondering about the integer*2 issues I was having, it was this: http://stackoverflow.com/questions/6503494/whats-wrong-with-the-following-fortran-77-code – Eric Inclan Aug 30 '13 at 17:30
  • @EricInclan so was it integer size mistmatch? – Anycorn Aug 30 '13 at 17:31
  • @Anycorn I know how that goes. I still work with code that has configure-time tests to figure out how the Fortran compiler is mangling the names. – Sean Patrick Santos Aug 30 '13 at 21:03
  • @Anycorn - actually, I did not have that issue. Instead, I was trying to do this: "integer tp = 1" after some lines of code. Once I moved the declaration up with all the others, and the initialization down after all the declarations were completed, it worked. – Eric Inclan Aug 31 '13 at 03:25
  • 1
    @SeanPatrickSantos CMake has been helpful for me in that regards, with FortranCInterface – Anycorn Aug 31 '13 at 18:45
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.

  • Thanks for the feedback. Can you please comment on where this code should go? Does it go inside of "subroutine objfun(nv, var, f, impass)" or above it? I tried above but the compiler didn't take it (gfortran). – Eric Inclan Aug 30 '13 at 04:33
  • For example, if I place it above the subroutine, the compiler says that the subroutine declaration is an unclassifiable statement. If I place it inside the subroutine, every line becomes either something like "Unexpected INTERFACE statement" or "Unclassifiable statement". Thanks again for your help! – Eric Inclan Aug 30 '13 at 04:47
  • The correct place is in the declaration section, i.e. after the "implicit" and before any executable statements. Also, I accidentally made this a subroutine and not a function; I'm going to edit my answer to fix that. – Sean Patrick Santos Aug 30 '13 at 20:53
  • This is great! Thank you once again for our help! – Eric Inclan Aug 31 '13 at 03:24