混合编程:从 C++ 调用 FORTRAN

Posted

技术标签:

【中文标题】混合编程:从 C++ 调用 FORTRAN【英文标题】:Mixed programming: Calling FORTRAN from C++ 【发布时间】:2014-11-28 05:04:23 【问题描述】:

我正在尝试用命令行编译一个混合程序。

编译工具是intel visual fortran 2013和VS 2013。

我搜索了一下,发现使用dll 比使用lib 更容易。

所以我尝试将我的 fortran 源代码编译为 dll 文件

我的Fortran 来源:

!  forsubs.f90 
!
!  FUNCTIONS/SUBROUTINES exported from FORSUBS.dll:
! FORSUBS      - subroutine 
!
INTEGER*4 FUNCTION Fact (n)
   !DEC$ ATTRIBUTES DLLEXPORT::Fact
      INTEGER*4 n [VALUE]
      INTEGER*4 i, amt
      amt = 1
      DO i = 1, n
        amt = amt * i
      END DO
      Fact = amt
   write(*,*)"Mixed calls succeed!"
END
SUBROUTINE Pythagoras (a, b, c)
   !DEC$ ATTRIBUTES DLLEXPORT::Pythagoras
      REAL*4 a [VALUE]
      REAL*4 b [VALUE]
      REAL*4 c [REFERENCE]
      c = SQRT (a * a + b * b)
END

我的C++ 来源:c_main.cpp

/*     File CMAIN.C   */

#include <stdio.h>

extern int __stdcall fact(int* n);
extern void __stdcall pythagoras(float* a, float* b, float *c);

int main()

    float c;
    printf("Factorial of 7 is: %d\n", FACT(7));
    PYTHAGORAS (30, 40, &c);
    printf("Hypotenuse if sides 30, 40 is: %f\n", c);

首先我编译了我的 fortran 源代码:

ifort /dll forsubs.f90

得到了forsubs.dllforsubs.lib

其次我编译了我的 C++ 源代码:

cl c_main.cpp /link forsubs.lib

但是,我得到了一个错误:

D:\c_f\c_f_dll\c_main.cpp(11) : error C3861: “FACT”:  identifier not found
D:\c_f\c_f_dll\c_main.cpp(12) : error C3861: “PYTHAGORAS”:  identifier not found

另外,我尝试以另一种方式加载 dll,使用:

 HINSTANCE hLibrary=LoadLibrary("forsubs.dll"); 
 if(hLibrary==NULL)
 
  cout<<"can't find the dll file"<<endl;
  return -1;
 

但我每次都得到一个“找不到 dll 文件”,而我确信 forsubs.dll 在文件夹中。

我不擅长C++,不知道怎么编译,求助!

编辑

奇怪!我将程序编辑为@Mikael Persson 的答案,但仍然出现错误。

error LNK2019: unresolved external symbol _FACT@4 referenced in function _main
error LNK2019: unresolved external symbol _PYTHAGORAS@12 referenced in function _main

另外,我google了一下,有人说__stdcall不是必要的,我删除了但仍然有错误:

error LNK2019: unresolved external symbol _FACT referenced in function _main
error LNK2019: unresolved external symbol _PYTHAGORAS referenced in function _main

我认为这可能与this 一样的问题。但是自己解决不了。

PS: 我的dlllib 的信息:

D:\c_f\c_f_dll>dumpbin /exports forsubs.lib
Microsoft (R) COFF/PE Dumper Version 12.00.30501.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file forsubs.lib

File Type: LIBRARY

     Exports

       ordinal    name

                  FACT
                  PYTHAGORAS

  Summary

          C3 .debug$S
          14 .idata$2
          14 .idata$3
           8 .idata$4
           8 .idata$5
           C .idata$6

D:\c_f\c_f_dll>dumpbin /exports forsubs.dll
Microsoft (R) COFF/PE Dumper Version 12.00.30501.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file forsubs.dll

File Type: DLL

  Section contains the following exports for forsubs.dll

    00000000 characteristics
    5477F1C5 time date stamp Fri Nov 28 11:53:41 2014
        0.00 version
           1 ordinal base
           2 number of functions
           2 number of names

    ordinal hint RVA      name

          1    0 00001000 FACT
          2    1 000010B0 PYTHAGORAS

  Summary

        1000 .data
        1000 .pdata
        1000 .rdata
        1000 .reloc
        1000 .text

编辑2

Also, I have tried to add a header:
#ifdef FORSUBS_EXPORTS
#define FORSUBS_API __declspec(dllexport) 
#else
#define FORSUBS_API __declspec(dllimport) 
#endif

还是不行。

【问题讨论】:

我不了解你,但 fortran dll 文件只有函数,但没有任何 dllmain switch 语句或 createthread 函数。请记住为 loadlibrary 提供正确的目录/路径。 @Amadan,嗨,我在代码中添加了"C",但得到了同样的错误。我认为__stdcall 是必要的,取决于问题“混合编程:从 C 调用 FORTRAN”。我的问题中的第二种方式有什么想法吗? 是的,我错了,我很抱歉。 This 可能是一本好书,我不应该猜测我显然不太了解的事情。 @Bot 我把dllexe放在同一目录下,但还是找不到dll 我建议使用 Fortran 的 ISO_C_Binding 而不是扩展或以前的技术。 *** 有一个关于这个主题的标签:***.com/questions/tagged/fortran-iso-c-binding 【参考方案1】:

C/C++ 区分大小写,所以如果你调用FACT,那么它必须被声明为FACT,而不是fact。或者反过来。

并且您需要将您的函数(在 C++ 代码中)声明为 extern "C",以禁用 C++ 特定的名称修改,否则会假定。

__stdcall 确实是 Fortran 函数的正确调用约定(默认情况下)。

Fortran 不区分大小写(默认情况下,但可以通过编译器选项进行更改,这很愚蠢)。我相信英特尔 Fortran 编译器会为 Fortran 函数生成全大写符号(用于链接目的)(例如,名为“Fact”的函数将导出为“FACT”)。因此,您需要在 C++ 声明中匹配这种情况。我不确定它与 Fortran 符号(全大写或全小写)的搭配方式,因此,如果一个不起作用,请尝试另一个。

编辑 2:

至于原型,我相信这些是 C++ 中的正确形式(我不确定,因为我对 Fortran 很生疏):

#include <stdint.h>  // in C, or <cstdint> in C++ (but must use std::int32_t)

extern "C" __declspec(dllimport) int32_t __stdcall FACT(int32_t n);
extern "C" __declspec(dllimport) void __stdcall PYTHAGORAS(float a, float b, float *c);

请注意,对于一个 4 字节整数,您需要使用 int32_t,而对于 Fortran 中的 [VALUE] 类型,它也应该在 C/C++ 中按值传递...至少,这是我的理解(但就像我说的,我不是 Fortran 专家)。

【讨论】:

嗨,我编辑了我的代码,但又出现了一个错误,我已经更新了我的问题。 @PaleNeutron 对不起......我已经有一段时间没有使用 DLL、MSVC 导入库或 Windows 了......所以我对这些东西有点生疏了,不要有什么可以测试的。我相信,如果您要链接一个导入库(.lib 文件),那么您需要将__declspec(dllimport) 添加到extern "C" 和返回类型之间的函数声明中。 stdcall 不是 OP 正在使用的 Fortran 编译器的默认调用约定。

以上是关于混合编程:从 C++ 调用 FORTRAN的主要内容,如果未能解决你的问题,请参考以下文章

FORTRAN & MATLAB 混合编程

win64 qt与fortran (codeblocks) 混合编程

objective-c和Cpp怎么混合编程?

混合编程:如何用pybind11调用C++

混合编程jni 第五篇之C++ 访问 Java代码

简单上手nodejs调用c++(c++和js的混合编程)