混合编程-包括Fortran

时间:2018-07-10 13:35:57

标签: 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

clib / functions.cpp:

extern "C"
{
int __stdcall add(int x, int y)
{
    return x + y;
}
}

cinclude.c

#include "clib/functions.h"

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

main.f90

 program main
   use cinterface      
   implicit none
   integer :: c
   c = add(1,2)
   write(*,*) c
 end program

makefile:

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
#

当我尝试使用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

如何使add在Fortran中起作用?

3 个答案:

答案 0 :(得分:3)

大部分时间都在那儿。要完成此工作,需要处理两件事:链接和参数传递约定。

链接

正如francescalus指出的那样,Fortran编译器不了解如何解析C / C ++头文件。因此,在此示例中,您的functions.h和cinclude.c文件将无用。

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

extern "C"
{
    int __stdcall add(int x, int y);
} 

extern "C"是重要的部分。这告诉g ++,以下代码块中的符号并不受所有C ++ name mangling的约束。您将需要在functions.cpp中的add定义周围保持相同。

extern "C"
{
    int add(int x, int y)
    {
        return x + y;
    }
}

完成后,仅需链接即可使用functions.o,cinterface.o / mod和main.o。

参数传递约定

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

第一种选择是使用整数指针重新定义参数的add函数,并在函数内部取消引用。

extern "C"
{
    int add(int* x, int* y)
    {
        return *x + *y;
    }
}

第二个选项(首选IMHO)是声明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

请注意在变量valuea上附加的b装饰。无论使用哪个选项,如果没有在我的机器上使用它,由于add函数调用的结果,我都会打印8393540。解决了传递参数的约定后,我按预期得到了3个输出。

答案 1 :(得分:1)

构建系统可以大大简化此过程(尽管以引入复杂的构建系统为代价)。假设您的问题是目录布局(尽管没有cinclude.c,因为我看不出它的作用是什么)

$ tree
.
├── cinterface.f90
├── clib
│   ├── CMakeLists.txt
│   ├── functions.cpp
│   └── functions.h
├── CMakeLists.txt
└── main.f90

cmake文件的内容是

$ cat CMakeLists.txt 
cmake_minimum_required(VERSION 3.9)

project(cpp-add LANGUAGES C CXX Fortran)

add_subdirectory(clib)

add_executable(glue cinterface.f90 main.f90)
target_link_libraries(glue PUBLIC cpp-clib)

$ cat clib/CMakeLists.txt 
add_library(cpp-clib functions.cpp)

然后可以按照通常的方式配置和构建项目:

$ cmake -H. -Bbuild && cmake --build build

执行:

$ build/glue
           3

答案 2 :(得分:0)

我遇到了同样的问题,能否向我们展示g ++编译行?

我的问题是由于我的make文件未在.exe的编译中正确包含适当的.o文件而引起的

即我有类似

Test: Test.cpp dependancy.o 
      g++ Test.cpp -o test.exe

,我得到的错误与您返回的错误相同。

我通过确保.o实际上在编译行上得到解决。

Test: Test.cpp dependancy.o 
      g++ dependancy.o Test.cpp -o test.exe 

我之所以建议这样做,是因为错误“未定义符号”通常意味着编译器实际上并不知道您调用add函数的代码在哪里。