【问题标题】:Loading Fortran function from dynamic library: Debug vs Release从动态库加载 Fortran 函数:调试与发布
【发布时间】:2018-06-04 06:41:50
【问题描述】:

我写了一些代码来加载一个用 Fortran 编译的动态库。 Fortran 库中的导出函数包括导出的 setter 和 getter。我正在尝试使用 Windows API 加载这些 setter 和 getter。虽然这适用于我在调试中构建的代码。它不适用于我的内置版本代码。在发布时,当我尝试将1.0f 传递给set() 时,当我进入fortran 代码时,我在调试器中看到0.0f

编辑:我忘记提及的另一个观察结果。如果我只加载setValue() 函数。我的问题完全消失了。只有当我从库中加载其他函数时,我才会开始遇到我所看到的问题。

编译器

  • 英特尔 Fortran 2017 更新 5
  • Visual Studio 2017 v15.5.2

调试

我已经确定,当我的 C++ 代码在发行版中编译时,我加载的 set() 函数会将 0.0f 传递给 fortran 函数。这是通过加载 fortran 库的调试版本并通过 Visual Studio 的调试器运行它来发现的。对我的代码的调试版本执行相同的操作会产生正确的值并传递给 fortran 函数。

我尝试了以下方法试图弄清楚发生了什么:

  • 尝试在我的加载程序的调试和发布版本中加载已编译 Fortran 的调试和发布二进制文件。仅适用于我的加载程序的调试版本。
  • 重建了 Fortran 代码以验证编译器标志,例如用于调试的多线程调试 DLL 和用于发布的多线程 DLL。
  • 执行了 fortran 二进制文件的转储程序以验证函数地址​​是否正确加载;他们是。

FORTRAN

    REAL FUNCTION getValue()
!DEC$ ATTRIBUTES DLLEXPORT, c:: getValue
    IMPLICIT NONE
    INCLUDE 'VALUE.CMN'

    getValue = VAL
    RETURN
      END FUNCTION getValue

    SUBROUTINE setValue(x)
!DEC$ ATTRIBUTES DLLEXPORT, c:: setValue
    IMPLICIT NONE 
    INCLUDE 'VALUE.CMN'

    REAL, INTENT(IN) :: x

    VAL = x 

      END SUBROUTINE setValue

C++

const auto handle = reinterpret_cast<HMODULE>(LoadLibrary("fortran_value.dll"));

typedef void(*Set)(float&);
typedef float(*Get)(void);

const auto set = reinterpret_cast<Set>(GetProcAddress(handle, "setvalue"));
const auto get = reinterpret_cast<Get>(GetProcAddress(handle, "getvalue"));

auto value = 1.0f;

// 0.0f gets set to the internal fotran variable when this code is compile in release.
set(value);

// Only succeeds when this code is compiled in Debug.
// get() returns 0.0f when this code is compiled in Release.
if(value == get()) 
{
    std::cout << "Success!\n";
}
else
{
    std::cout << "Fail!\n";
}

FreeLibrary(handle);

我很茫然。对这里可能发生的事情有什么想法或建议吗?

完整的可编译示例

以下是我遇到的问题的完整可编译示例的链接。在有更多的时间花在它身上之后。看来我的问题源于我尝试使用上述代码制作包装器。

fortran_value.dll

fortran_value_test.exe

【问题讨论】:

  • 什么是“不起作用”?究竟会发生什么?有什么错误信息吗?
  • 我用一些解释我从调试器看到的值的 cmets 更新了我的 C++ 代码。在发布时,我通过set() 传递了 1.0f,但在单步执行 fortran 代码时,我看到了 0.0f。在调试中,它按预期工作,我将 1.0f 传入,我在调试器中看到 1.0f。
  • 非常、非常怀疑在调试器中检查变量时所看到的内容。我强烈建议单步执行汇编程序指令,并查看它在做什么。
  • 这是 32 位还是 64 位?我认为您的调用约定不匹配,或者您对调试器显示的内容感到困惑。
  • 这只是 64 位。我的机器上只安装了 64 位 fortran 编译器。

标签: c++ visual-studio dll fortran intel-fortran


【解决方案1】:

在阅读了对我的问题的回复后,我花了更多时间调试 fortran 代码并进行了更多研究。正如对我的问题的答复中所讨论的,调用约定不匹配。通过在我的 fortran 代码中使用 bind(c),我的问题得到了解决。

自 Fortran 2003 (ISO/IEC 1539-1:2004(E)) 以来,有一种标准化方法可以生成可与 C (ISO/IEC 9899:1999) 互操作的过程和派生类型声明以及全局变量。添加了 bind(C) 属性以通知编译器符号应与 C 互操作;此外,还添加了一些约束。但是请注意,并非所有 C 功能都具有 Fortran 等效功能,反之亦然。例如,C 的无符号整数和具有可变参数数量的 C 函数在 Fortran 中都没有等价物

    REAL FUNCTION getValue() bind(c)
!DEC$ ATTRIBUTES DLLEXPORT :: getValue
    IMPLICIT NONE
    INCLUDE 'VALUE.CMN'

    getValue = VAL
    RETURN
      END FUNCTION getValue

    SUBROUTINE setValue(x) bind(c)
!DEC$ ATTRIBUTES DLLEXPORT :: setValue
    IMPLICIT NONE 
    INCLUDE 'VALUE.CMN'

    REAL, INTENT(IN) :: x

    VAL = x 

      END SUBROUTINE setValue

【讨论】:

    猜你喜欢
    • 2021-10-18
    • 2016-12-07
    • 2015-08-07
    • 1970-01-01
    • 2011-07-28
    • 2012-01-31
    • 1970-01-01
    • 2020-07-22
    • 2012-03-02
    相关资源
    最近更新 更多