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
}

2 Answers 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);
}
Sign up to request clarification or add additional context in comments.

19 Comments

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!!
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?
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.
@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.
@SeanPatrickSantos CMake has been helpful for me in that regards, with FortranCInterface
|
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.

4 Comments

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).
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!
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.
This is great! Thank you once again for our help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.