【问题标题】:Passing string to Fortran DLL using ctypes and Python使用 ctypes 和 Python 将字符串传递给 Fortran DLL
【发布时间】:2014-05-13 21:18:32
【问题描述】:

我正在尝试使用 ctypes 在 Python 2.7 中加载 DLL。 DLL 是使用 Fortran 编写的,其中包含多个子例程。我能够成功设置几个以long 和double 指针作为参数的导出函数。

import ctypes as C
import numpy as np

dll = C.windll.LoadLibrary('C:\\Temp\\program.dll')
_cp_from_t = getattr(dll, "CP_FROM_T")
_cp_from_t.restype = C.c_double
_cp_from_t.argtypes = [C.POINTER(C.c_longdouble),
                    np.ctypeslib.ndpointer(C.c_longdouble)]

# Mixture Rgas function
_mix_r = getattr(dll, "MIX_R")
_mix_r.restype = C.c_double
_mix_r.argtypes = [np.ctypeslib.ndpointer(dtype=C.c_longdouble)]

def cp_from_t(composition, temp):
    """ Calculates Cp in BTU/lb/R given a fuel composition and temperature.

    :param composition: numpy array containing fuel composition
    :param temp: temperature of fuel
    :return: Cp
    :rtype : float
    """
    return _cp_from_t(C.byref(C.c_double(temp)), composition)

def mix_r(composition):
    """Return the gas constant for a given composition.
    :rtype : float
    :param composition: numpy array containing fuel composition
    """
    return _mix_r(composition)

# At this point, I can just pass a numpy array as the composition and I can get the 
# calculated values without a problem
comps = np.array([0, 0, 12.0, 23.0, 33.0, 10, 5.0])
temp = 900.0

cp = cp_from_t(comps, temp)
rgas = mix_r(comps)

到目前为止,一切顺利。

当我尝试 另一个 名为 Function2 的子例程时出现问题,它需要一些字符串作为输入。字符串都是固定长度(255),它们还询问每个字符串参数的长度。

函数在Fortran中实现如下:

Subroutine FUNCTION2(localBasePath,localTempPath,InputFileName,Model,DataArray,ErrCode)
!DEC$ ATTRIBUTES STDCALL,REFERENCE, ALIAS:'FUNCTION2',DLLEXPORT :: FUNCTION2
Implicit None
Character *255 localBasePath,localTempPath,InputFileName
Integer   *4  Model(20), ErrCode(20)
Real      *8  DataArray(900)

Python中的函数原型设置如下

function2 = getattr(dll, 'FUNCTION2')
function2.argtypes = [C.POINTER(C.c_char_p), C.c_long,
                      C.POINTER(C.c_char_p), C.c_long,
                      C.POINTER(C.c_char_p), C.c_long,
                      np.ctypeslib.ndpointer(C.c_long , flags='F_CONTIGUOUS'),
                      np.ctypeslib.ndpointer(C.c_double, flags='F_CONTIGUOUS'),
                      np.ctypeslib.ndpointer(C.c_long, flags='F_CONTIGUOUS')]

我称之为:

base_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\".ljust(255)
temp_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\temp".ljust(255)
inp_file = "inp.txt".ljust(255)

function2(C.byref(C.c_char_p(base_path)),
                  C.c_long(len(base_path)),
                  C.byref(C.c_char_p(temp_dir)),
                  C.c_long(len(temp_dir))),
                  C.byref(C.c_char_p(inp_file)),
                  C.c_long(len(inp_file)),
                  model_array,
                  data_array,
                  error_array)

字符串本质上是路径。函数Function2无法识别路径,并证明错误信息末尾带有一些不可读的字符,例如:

forrtl: severe (43): file name specification error, unit 16, D:\Users\xxxxxxx\Documents\xxxxx\œâa。

我希望函数接收的是D:\Users\xxxxxxx\Documents\xxxxx\。显然,字符串没有正确传递。

我读到 Python 使用 NULL 终止的字符串。将字符串传递给 Fortran dll 时会出现问题吗?如果是这样,我该如何解决?

有什么建议吗?

【问题讨论】:

  • 好的。将 argtypes 更改为按值传递。至于前面的评论,你是对的。它应该是原型函数。进行了上述更改。必须简化一些代码,因为它最初是作为一个类实现的。
  • @eryksun 这有帮助。现在正在正确地传递字符串。 DLL 似乎仍然有Function2 的问题。将不得不与其作者合作。

标签: python string dll fortran ctypes


【解决方案1】:

根据@eryksun 的评论,我进行了以下更改以使其正常工作。

将argtypes 更改为:

function2 = getattr(dll, 'FUNCTION2')
function2.argtypes = [C.c_char_p, C.c_long,
                  C.c_char_p, C.c_long,
                  C.c_char_p, C.c_long,
                  np.ctypeslib.ndpointer(C.c_long , flags='F_CONTIGUOUS'),
                  np.ctypeslib.ndpointer(C.c_double, flags='F_CONTIGUOUS'),
                  np.ctypeslib.ndpointer(C.c_long, flags='F_CONTIGUOUS')]

我没有将字符串传递为byref,而是将其更改为以下内容。

base_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\".ljust(255)
temp_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\temp".ljust(255)
inp_file = "inp.txt".ljust(255)

function2(base_path, len(base_path), temp_dir, len(temp_dir), inp_file, len(inp_file), 
          model_array, data_array, error_array)

直接传递值就足够了。

【讨论】:

  • 根据英特尔Mixed Language Programming 的文档,长度附加到参数列表的末尾,而不是交错的。此外,参数是 32 位进程中的 c_int32 和 64 位进程中的 c_int64。 c_long 在 Windows 中始终是 c_int32,但实际上你不会遇到问题,除非你需要一个比 2**31 - 1 更长的字符串。
猜你喜欢
  • 2017-07-08
  • 1970-01-01
  • 2021-04-27
  • 1970-01-01
  • 1970-01-01
  • 2011-08-16
  • 1970-01-01
  • 2021-09-28
  • 1970-01-01
相关资源
最近更新 更多