混合编程-包括Fortran的C ++头

T.B*_*own 5 c++ fortran fortran-iso-c-binding

我试图在用Fortran编写的程序中使用C ++编写的库中的函数。C ++库在一个头文件中进行了汇总,因此,如果您只想在另一个C ++程序中使用它,#include functions.h我想了解如何在Fortran中执行类似的操作。

根据我的研究,我创建了这个最小可行的示例:

clib / functions.h:

#ifndef ADD_H
#define ADD_H
extern "C"
{
int __stdcall add(int x, int y);
} 
#endif
Run Code Online (Sandbox Code Playgroud)

clib / functions.cpp:

extern "C"
{
int __stdcall add(int x, int y)
{
    return x + y;
}
}
Run Code Online (Sandbox Code Playgroud)

cinclude.c

#include "clib/functions.h"
Run Code Online (Sandbox Code Playgroud)

cinterface.f95:

module cinterface
  use,intrinsic::ISO_C_BINDING
  integer(C_INT)::a,b
  interface
    integer(C_INT) function add(a,b) bind(C,name="add")
      use,intrinsic::ISO_C_BINDING
      implicit none
!GCC$ ATTRIBUTES STDCALL :: add
!DEC$ ATTRIBUTES STDCALL :: add
      integer(C_INT), value ::a,b
    end function add
  end interface
end module cinterface
Run Code Online (Sandbox Code Playgroud)

main.f90

 program main
   use cinterface      
   implicit none
   integer :: c
   c = add(1,2)
   write(*,*) c
 end program
Run Code Online (Sandbox Code Playgroud)

生成文件:

FC = gfortran
CC = g++
LD = gfortran
FFLAGS = -c -O2
CFLAGS = -c -O2
OBJ=main.o 
DEP = \
    cinterface.o cinclude.o

.SUFFIXES: .f90 .f95 .c .o
# default rule to make .o files from .f files
.f90.o  : ;       $(FC) $(FFLAGS) $*.f90 -o $*.o
.f95.o  : ;       $(FC) $(FFLAGS) $*.f95 -o $*.o
.c.o  : ;       $(CC) $(CFLAGS) $*.c -o $*.o
%.o: %.mod
#
main.ex: ${DEP} ${OBJ}
    $(LD)  ${DEP}  ${OBJ} -o prog.exe
#
Run Code Online (Sandbox Code Playgroud)

当我尝试使用Cygwin进行此项目时,出现以下错误:

main.o:main.f90:(.text+0x13): undefined reference to `add'
main.o:main.f90:(.text+0x13): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `add'
collect2: error: ld returned 1 exit status
make: *** [makefile:19: main.ex] Error 1
Run Code Online (Sandbox Code Playgroud)

如何使addFortran中的功能起作用?

Ken*_*vis 4

你已经差不多到那里了。为了使这项工作有效,您需要解决两件事:链接和参数传递约定。

连锁

正如 francescalus 所指出的,Fortran 编译器不理解如何解析 C/C++ 头文件。因此,您的functions.h 和cinclude.c 文件在此示例中不会有任何用处。

不过,不要扔掉你的functions.h。在其中您将 add 函数声明为:

extern "C"
{
    int __stdcall add(int x, int y);
} 
Run Code Online (Sandbox Code Playgroud)

extern "C"是重要的部分。这告诉 g++ 以下代码块中的符号不​​受所有 C++名称修饰的影响。您将需要与functions.cpp 中的定义相同的内容add

extern "C"
{
    int add(int x, int y)
    {
        return x + y;
    }
}
Run Code Online (Sandbox Code Playgroud)

完成此操作后,您需要链接的只是functions.o、cinterface.o/mod 和main.o。

参数传递约定

该方法add声明参数x并按y值传递给函数。这是 C/C++ 函数参数的默认行为。另一方面,Fortran 默认通过引用将参数传递给函数/子例程。在 C++ 中,这看起来像int add(int* x, int* y). 有两种方法可以解决这个问题。

第一个选项是使用参数的整数指针重新定义 add 函数,并在函数内取消引用它们。

extern "C"
{
    int add(int* x, int* y)
    {
        return *x + *y;
    }
}
Run Code Online (Sandbox Code Playgroud)

第二个选项(恕我直言,这是首选选项)是声明 Fortran 接口以按值传递参数。它们没有在add函数中被修改......为什么通过引用传递它们?如果您选择此选项,那么您的 cinterface.f95 将需要包含以下声明add

integer(C_INT) function add(a,b) bind(C,name="add")
    use,intrinsic::ISO_C_BINDING
    implicit none
    integer(C_INT),value::a,b
end function add
Run Code Online (Sandbox Code Playgroud)

value请注意变量a和上的附加修饰b。无论您使用哪个选项,如果我的机器上没有它,我都会打印出 8393540 作为add函数调用的结果。在解决参数传递约定后,我按预期打印出 3 。