从动态库加载 Fortran 函数:调试与发布
Loading Fortran function from dynamic library: Debug vs Release
我写了一些代码来加载用 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 二进制文件执行 dumpbin 以验证函数地址是否已正确加载;他们是。
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 代码并进行了更多研究。正如在对我的问题的答复中所讨论的,调用约定不匹配。通过在我的 Fortran 代码中使用 bind(c),我的问题得到解决。
Since Fortran 2003 (ISO/IEC 1539-1:2004(E)) there is a standardized way to generate procedure and derived-type declarations and global variables which are interoperable with C (ISO/IEC 9899:1999). The bind(C) attribute has been added to inform the compiler that a symbol shall be interoperable with C; also, some constraints are added. Note, however, that not all C features have a Fortran equivalent or vice versa. For instance, neither C’s unsigned integers nor C’s functions with variable number of arguments have an equivalent in 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
我写了一些代码来加载用 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 二进制文件执行 dumpbin 以验证函数地址是否已正确加载;他们是。
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 代码并进行了更多研究。正如在对我的问题的答复中所讨论的,调用约定不匹配。通过在我的 Fortran 代码中使用 bind(c),我的问题得到解决。
Since Fortran 2003 (ISO/IEC 1539-1:2004(E)) there is a standardized way to generate procedure and derived-type declarations and global variables which are interoperable with C (ISO/IEC 9899:1999). The bind(C) attribute has been added to inform the compiler that a symbol shall be interoperable with C; also, some constraints are added. Note, however, that not all C features have a Fortran equivalent or vice versa. For instance, neither C’s unsigned integers nor C’s functions with variable number of arguments have an equivalent in 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