有没有办法控制GNU Fortran 4.8发出的符号?
旧版本(例如3.4)有-fcase-lower
,-fcase-preserve
和-fcase-upper
强制小写,分别在源和大写中使用的情况,但这些似乎是丢弃。是否有一些新方法来控制它?
修改
我正在尝试将大型混合C / Fortran代码库从英特尔编译器移植到GNU编译器。
我知道我们可以使用BIND(C, name='...')
来提供特定于案例的符号名称。但是,这还有其他影响。考虑这个C函数:
void print(char *str, size_t len) {
for(int ii = 0; ii < len; ii++) {
putchar(str[ii]);
}
putchar('\n');
}
我们可以从像这样的Fortran程序中调用它:
program test
implicit none
interface
subroutine printstr(str)
character :: str(*)
end subroutine
end interface
call printstr("Hello, world.");
end
如果C函数名称不是全部小写(例如PrintStr
),那么我们可能会尝试修复Fortran程序:
program test
implicit none
interface
subroutine printstr(str) bind(C, name='PrintStr')
use iso_c_binding
character :: str(*)
end subroutine
end interface
call printstr("Hello, world.");
end
这不起作用,因为bind(C, ...)
改变了字符串参数的处理方式,并且不再提供长度参数(我不确定这是否会导致堆栈损坏或只是缓冲区溢出 - 给出的例子总是来自缓冲区溢出的段错误。
我认为现在可能是一个关于如何解决这个问题的新问题的时候了。
答案 0 :(得分:2)
以下是使用Fotran iso_c_binding模块解决此问题的一种方法。
首先,C代码,就像你的例子一样:
void PrintStr(char *str, size_t len)
{
for (int ii=0; ii < len; ++ii)
putchar(str[ii]);
putchar('\n');
}
和适合它的Fortran接口:
interface
subroutine printstr_c(str, len) bind(C, name='PrintStr')
use iso_c_binding, only: c_char, c_size_t
implicit none
character(kind=c_char) :: str(*)
integer(c_size_t),value :: len
end subroutine printstr_c
end interface
这将让您将C函数调用为:
character(len=20) :: string = 'Hello ISO C env!'
call printstr_c(string, int(len(string),kind=c_size_t))
这样可行,但需要显式指定length参数。我们可以做得更好,并在它周围创建一个Fortran函数包装器来隐藏来自调用者的长度。
subroutine printstr(str)
use iso_c_binding, only: c_size_t
implicit none
character(len=*) :: str
call printstr_c(str, int(len(str),kind=c_size_t))
end subroutine
现在我们可以打电话:
character(len=20) :: string = 'Hello ISO C env!'
call printstr(string)
请注意,由于C函数循环遍历length参数,因此我没有因为null终止字符串而烦恼。如果需要null终止符,可以在printstr
子例程中添加一个。
如果你想要调用许多这样的函数,所有形式都是void f(str, len)
,那么我们可以做更多的工作并进一步抽象printstr
函数来成为Fortran的通用翻译器字符变量配对char,len变量适合iso_c_binding调用。考虑这个例子:
module cfuncs
implicit none
interface
! this interface is for an actual C function
subroutine printstr_c(str, len) bind(C, name='PrintStr')
use iso_c_binding, only: c_char, c_size_t
implicit none
character(kind=c_char) :: str(*)
integer(c_size_t),value :: len
end subroutine printstr_c
end interface
end module
module ffuncs
use cfuncs
implicit none
interface
! this interface is to constrain procedures passed to printstr()
subroutine string_and_len(str, len)
use iso_c_binding, only: c_char, c_size_t
implicit none
character(kind=c_char) :: str(*)
integer(c_size_t),value :: len
end subroutine string_and_len
end interface
contains
! this routine takes the C function you want to call and a string to pass
! and does the translation to call funct(str,len)
subroutine printstr(func,str)
use iso_c_binding, only: c_size_t
implicit none
procedure(string_and_len) :: func
character(len=*) :: str
call func(str, int(len(str),kind=c_size_t))
end subroutine
end module
program main
use ffuncs
implicit none
character(len=20) :: string = 'Hello ISO C env!'
call printstr(printstr_c, string)
end program
对于这个特定的例子,这可能有点过分,但它让一个Fortran包装器调用许多相同类型签名的C函数。这反过来只需要您在移植的代码中装饰Fortran调用,以便由包装函数包装。例如,假设printstrc_c
是外部接口:
! old F77 non iso_c_binding call
call printstr_c("Hello World")
变成
! wrapping the old F77 call through our Fortran wrapper handling iso_c_binding
call printstr(printstr_c, "Hello World")
这不像你做旧的方式那样优雅,但新版本的gfortran(3.x gcc是g77,而4.x是gfortran,afaik完全重写)似乎没有能够强制保存案件。如果你想避免我演示的混乱,另一种选择是在链接之前对已编译的Fortran对象进行后处理并重写函数名称以获得适当的情况,但我认为处理更糟糕(和肮脏的黑客)。
答案 1 :(得分:0)
gfortran中没有相应的命令行选项。话虽如此,GFortran支持ISO_C_BINDING功能,因此您可以将符号重命名为C中有效的符号,例如:
subroutine foo (a) bind(C, name="FoOoF")
use iso_c_binding
integer(C_int) :: a
a = 42
end subroutine foo
答案 2 :(得分:0)
部分且有些不尽如人意的答案,但迄今为止我们发现的最好。
我们已经搜索了这个功能的高低。特别是,要保留WITHOUT Bind&C; C。极端&#34;推动&#34;由坚持使用Bind C的权力引入,对于许多重要情况,特别是遗留代码,尤其是在Windows上,是一个可怕的结果。
将遗留或英特尔等混合语言代码移植到gFortran没有CHEAP方式,因为据我们所知,多年前GNU Fortran的守护者明确决定他们不会保留&#34;保留案例& #34;或类似的东西(我忘记了错误报告,但你可以搜索它)。奇怪的是,他们放弃这些功能的借口是没有足够的兴趣...我必须说这听起来有点荒谬,因为在Windows中混合语言环境中工作的人几乎肯定需要一个高频率的保存案例工具。这在DLL的VBA / Fortran中是必需的,在访问WinAPI / r等时需要,并且Bind&#39; C / ISO导致的问题多于修复。
...我们有时会得到这样的印象:GNU Fortran的守护者主要生活在Linux中,并且在某些情况下表达了对Windows的公开敌意......这可能是问题的一部分。
同样,他们决定在编译器指令中没有ALIAS或类似的属性。
因此,允许保留大小写的gFortran(我们知道)中的唯一机制是Bind&#39; C,由于各种原因这是一个灾难,最特别的是用于传递len的固定len字符串&gt; 1。
我们使用两种不同的,非常不令人满意的解决方案样式,用于混合lang / interop与gFortran:
1)MUST PRESERVE案例,例如与WinAPI接口时:
Interface
!
Function w32_GetWindowTextW(hWnd, lpString, nMaxCount) bind(C,name="GetWindowTextW")
!
! from c:\Windows\System32\User32.dll
!
Use, Intrinsic :: ISO_C_BINDING, Only: c_IntPtr_t, c_Size_t, c_Int, c_Ptr, c_Char
!
!GCC$ ATTRIBUTES STDCALL :: w32_GetWindowTextW, hWnd, lpString, nMaxCount
!
Integer(c_Int) :: w32_GetWindowTextW
Integer(c_IntPtr_t), Value :: hWnd ! NOTE: this is SUPPOSED TO BE a Win HANDLE, this seems ISO equivalent
Character(Kind=c_Char) :: lpString(*)
Integer(c_Int), Value :: nMaxCount ! NOTE: this is SUPPOSED TO BE WinAPI int type, this seems ISO equivalent
End Function w32_GetWindowTextW
!
End Interface
这里保留案例是必不可少的,因为否则界面不会在WinAPI中找到入口点。此外,Bind&#C; C允许使用GCC FPP出于某种奇怪的原因而无法使用别名。
需要GCC编译器指令来克服Bind&#C;对调用约定的破坏/更改,并根据WinAPI(以及VB,Excel / VBA等)的要求将其还原为STDCALL。等)。
然而,看看狗的早餐&#34;需要处理lpString var,它现在必须是一个向量或Len = 1&#39; s。这是一个非常昂贵的重写(并且不仅在声明中,但是在使用这些变量的地方,至少必须添加额外的层以将Len = 1 char数组转换为原始的固定len char变量)。
2)情况会很好&#34;很好&#34;保存案例,但它更便宜&#34;使用全部小写,例如使用VBA / Fortran互操作来创建DLL作为Excel加载项。
ASIDE:以下内容依赖于我们自己的库中自定义的erf实现,但是作为内在函数可以使用相当好的实现。
你可以去Bind&C; C并保留案例(包含所有&#34;字符串含义&#34;),例如
Function Erf_Math_BindC_XL(x) bind(C,name="Erf_Math_BindC_XL")
!
Use ARTMathFuncsMod, Only: MathErf ! Proprietary alternative to Fortran2008 intrinsic Erf()
!
!GCC$ ATTRIBUTES DLLEXPORT, STDCALL :: Erf_Math_BindC_XL
!
Real*8 :: Erf_Math_BindC_XL
!
Real*8, Intent(In) :: x
!
Erf_Math_BindC_XL = MathErf(x)
!
End Function Erf_Math_BindC_XL
相比之下,您可以使用,而不是:
Function Erf_Math_XL(x)
!
Use ARTMathFuncsMod, Only: MathErf ! Proprietary alternative to Fortran2008 intrinsic Erf()
!
!GCC$ ATTRIBUTES DLLEXPORT, STDCALL :: Erf_Math_BindC_XL
!
Real*8 :: Erf_Math_XL
!
Real*8, Intent(In) :: x
!
Erf_Math_XL = MathErf(x)
!
End Function Erf_Math_XL
但是,如果直接使用gFortan编译它,则会对条目名称进行修饰,看起来像_erf_math_xl@4
。
所以,如果你已经有一个依赖&#34; Erf_Math_XL&#34;的VBA声明,那么它将失败。
现在,您可以重新编写所有VBA端...非常昂贵,以及&#34; @nn&#34;每次更改arg列表时都会更改,所以实际上#34;几何上更昂贵&#34;。
相比之下,如果在gFortran端使用-fno-underscore和-mrtd,则条目名称将变为erf_math_xl
。好的,所以它不是保留的情况,但它们都是小写的,并且&#34;未修饰的&#34;。与将许多固定len字符串重写为len = 1数组等相比,这可以以更便宜的方式处理。
然后在VBA端,Function / Sub声明允许ALIAS,因此&#34; case问题&#34;是一个相对简单的编辑/替换宏&#34;
......如果有人知道更好的方法,请发表评论。
然而,真正明显且大量有用的结果将是&#34;保留案例&#34;切换,或者最重要的是,FPP ATTRIBUTES中的ALIAS选项
...不确定如何组织一场&#34; en mass&#34;努力鼓励GNU Fortran程序员创建这个,但很乐意提供帮助。
PS。如果我们进入&#34;阴谋论阵营&#34;,我们会怀疑英特尔是否正在应用某些影响&#34;,因为目前gFortran机制几乎无法完全转换DEC / CVF / IVF等编译器指令等。例如,如果存在ALIAS属性,则很可能大多数/所有DEC / CVF / IVF代码都可以相对容易地成为GCC,这可能会导致从英特尔Fortran大量迁移,因此很多&#34 ;从英特尔转移现金&#34; ......只是一个想法: - )。
作为一个有趣的问题,几年前,司法部发现英特尔犯有各种干扰竞争的罪行。等等,并对他们罚款1000万美元/罚款......所以考虑这种可能性也许并不太遥远。
答案 3 :(得分:0)
我在尝试使用Linux(WSL)中的x86_64-w64-mingw32-gfortran为Windows的Matlab交叉编译mex文件时遇到了这个问题。
这需要我链接一个专有的.dll文件,该文件包含名为MXGETPR
的Fortran函数,但是gfortran会为mxgetpr
生成符号。 bind(C,...)
方法确实重命名了这些名称,但是正如其他人指出的那样,它将调用约定更改为C样式,这对于某些子例程(即处理字符串参数的子例程)来说是有问题的。
@Tom的评论使我指向objcopy来重命名这些符号。因为我所有的符号都以mx*
或mex*
开头,所以以下脚本为我转换了符号:
gfortran $(SRC) -o objfile.o --no-underscoreing $(FCFLAGS)
nm objfile.o | awk '/U mex/ || /U mx/ { print $2 " " toupper($2) }' | sort -u > rdef_syms.txt
objcopy --redefine-syms=rdef_syms.txt objfile.o
第二行查找objfile.o
中任何未定义且命名为mx*
或mex*
的符号。它生成一个将小写版本映射到大写版本的文件。
排序可确保没有重复的条目(objcopy不喜欢重复的条目)。
nm objfile.o
的输出最初是这样的:
0000000000000000 b .bss
0000000000000000 d .data
0000000000000000 T mexfunction
U mxcopyreal8toptr730
U mxcreatedoublematrix730
U mxgetpr
U mxgetscalar
0000000000000000 p .pdata
0000000000000000 r .rdata
0000000000000000 r .rdata$zzz
0000000000000000 t .text
0000000000000000 r .xdata
生成rdef_syms.txt
文件以使用这些符号并将它们大写。看起来像这样:
mxcopyreal8toptr730 MXCOPYREAL8TOPTR730
mxcreatedoublematrix730 MXCREATEDOUBLEMATRIX730
mxgetpr MXGETPR
mxgetscalar MXGETSCALAR
最后,在objcopy读取文件并就地重命名符号之后,目标文件具有以下符号:
0000000000000000 b .bss
0000000000000000 d .data
0000000000000000 T mexfunction
U MXCOPYREAL8TOPTR730
U MXCREATEDOUBLEMATRIX730
U MXGETPR
U MXGETSCALAR
0000000000000000 p .pdata
0000000000000000 r .rdata
0000000000000000 r .rdata$zzz
0000000000000000 t .text
0000000000000000 r .xdata
很明显,我的案例得到了我要重新映射的唯一名称的帮助,而我只是在尝试将它们大写,而不是使用某种混合大小写的解决方案,但令我感到惊讶的是,这并不像hack就像我预期的那样,而且只有2条额外的行!