含指针C结构体与含指针联合体的Fortran转换 Excel XLL SDK重构问题
Fortran重写Excel XLL SDK的联合体指针问题解决方案
核心绕过方法:Fortran的ISO_C_BINDING模块提供的C_PTR是不透明标量值类型,不是带POINTER属性的Fortran原生指针,完全可以合法放入UNION块中,无需额外 hack 即可实现1:1映射C侧的联合体内指针成员。
1 先修正统一基础类型映射
不要使用非标准的logical*2、integer*4等写法,改用ISO_C_BINDING提供的标准兼容类型,保证跨编译器、跨架构内存布局完全和C一致:
WORD(unsigned short)对应integer(c_ushort)INT32(signed int)对应integer(c_int)DWORD_PTR(unsigned __int64)对应integer(c_intptr_t)或integer(c_long_long)BYTE(unsigned char)对应integer(c_uchar)LPSTR(char*)、所有C结构体指针、HANDLE类型统一用type(c_ptr)映射
2 嵌套结构体优先转换
先把所有C定义的嵌套结构体(比如XLREF、XLMREF、XLREF12等)先转换成Fortran的BIND(C)派生类型,保证内存布局和C完全对齐,示例:
use, intrinsic :: iso_c_binding implicit none ! 对应C的XLREF结构体 type, bind(C) :: XLREF integer(c_ushort) :: rwFirst integer(c_ushort) :: rwLast integer(c_uchar) :: colFirst integer(c_uchar) :: colLast end type XLREF
3 XLOPER结构体1:1转换示例
所有C指针都替换为C_PTR放入联合体即可,Fortran的UNION/MAP完全支持该类型:
! 对应C的XLOPER结构体 type, bind(C) :: XLOPER union map real(c_double) :: num ! xltypeNum end map map type(c_ptr) :: str ! xltypeStr,对应LPSTR end map map integer(c_ushort) :: xbool ! xltypeBool end map map integer(c_ushort) :: err ! xltypeErr end map map integer(c_short) :: w ! xltypeInt end map map type, bind(C) :: sref_t integer(c_ushort) :: count type(XLREF) :: ref end type sref_t type(sref_t) :: sref ! xltypeSRef end map map type, bind(C) :: mref_t type(c_ptr) :: lpmref ! 对应XLMREF* integer(c_int) :: idSheet ! IDSHEET对应int类型 end type mref_t type(mref_t) :: mref ! xltypeRef end map map type, bind(C) :: array_t type(c_ptr) :: lparray ! 对应xloper* integer(c_ushort) :: rows integer(c_ushort) :: columns end type array_t type(array_t) :: array ! xltypeMulti end map ! flow、bigdata等剩余成员按照相同逻辑转换即可,所有指针替换为C_PTR end union integer(c_ushort) :: xltype end type XLOPER
4 指针操作封装
你拿到C_PTR后如果需要在Fortran侧操作指向的内容,直接调用ISO_C_BINDING提供的转换函数即可:
- C指针转Fortran指针:调用
c_f_pointer(c_ptr_val, fortran_ptr [, dims]),比如字符串类型可以转成character(kind=c_char)的指针,数组类型可以指定维度转成对应数组指针 - Fortran变量转C指针:调用
c_loc(fortran_var)得到C_PTR,即可塞进联合体的对应成员里传给Excel
5 柔性数组成员处理
类似XLMREF12里reftbl[1]实际是长度为count的数组这种情况,你可以直接把成员定义为长度1的数组,拿到后用c_f_pointer转成指定长度的Fortran数组即可,内存布局和C完全兼容。
6 导出函数兼容性处理
要让Excel能直接调用Fortran写的XLL函数,只需要给Fortran函数加上调用约定和导出声明即可,以Intel Fortran为例:
!DEC$ ATTRIBUTES STDCALL, DECORATE, ALIAS:'MyFortranFunc' :: MyFortranFunc !DEC$ ATTRIBUTES DLLEXPORT :: MyFortranFunc function MyFortranFunc(oper) result(res) bind(C) type(XLOPER), intent(in) :: oper type(XLOPER) :: res ! 你的函数逻辑 end function MyFortranFunc
这样编译出来的DLL和C编译的XLL完全一致,Excel可以直接识别加载。
内容的提问来源于stack exchange,提问作者Olórin
相关产品推荐
相关产品推荐

