Вернуть двойной массив через FORTRAN (DLL) для дальнейшей обработки в python

Я уже некоторое время борюсь с этой проблемой, и поисковые запросы / применимая документация также не дали никаких жизнеспособных результатов; поэтому размещаю его здесь.

Чего я хочу добиться:

  • У меня есть программа, написанная на FORTRAN77, которая принимает некоторые аргументы и возвращает массив двойной точности фиксированной длины.
  • Я хочу использовать другой язык программирования для вызова этой функции (сейчас я использую python для целей тестирования, но это может быть изменено, поэтому f2py здесь не вариант)

Подпрограмму FORTRAN можно обобщить следующим образом:

      FUNCTION TST (IIN)
      IMPLICIT LOGICAL (A-Z)
cGCC$ ATTRIBUTES DLLEXPORT, STDCALL :: TST
      INTEGER           II,IIN
      DOUBLE PRECISION, DIMENSION(3) :: TST
C
C     Test function:
      DO 1001 II = 1,3
         TST(II) = II * 1.11D0 + IIN
 1001 CONTINUE
      RETURN
      END

Это скомпилировано с помощью gcc-fortran следующим образом:

gfortran -static -c -fdollar-ok -fno-align-commons TEST.for
gfortran -shared -mrtd -static -o TEST.dll TEST.def TEST.o

Где TEST.def сопоставляет TST с tst_

Пока проблем нет, однако в python возникает вопрос: "Как мне вызвать эту функцию и обработать возвращаемое значение?"

Использование инструмента «depwalker»; функция TST, по-видимому, ожидает 2 аргумента (tst_@8). Помимо целого числа, я предполагаю, что это должен быть либо указатель на выходной массив, либо его длину.

Мой код Python выглядит следующим образом:

import ctypes as cs
import numpy as np

#import dll library hooks:
tdll = cs.WinDLL(pathToDLL)
#test:
tdll.TST.restype = None
tdll.TST.argtypes = [cs.POINTER(cs.c_long), cs.POINTER(cs.c_double*3)]

#start testing:
Ar = [0.0, 0.0, 0.0]
_A = np.array(Ar).ctypes.data_as(cs.POINTER(cs.c_double*len(Ar)))
_L = cs.c_long(3)
tdll.TST(cs.byref(_L), _A)

Проблема в том, что этот код (вместе со всеми вариантами, которые я пробую) выдаст ошибку: OSError: exception: access violation writing 0x0000000F. Если я попытаюсь передать первый аргумент ByValue, это приведет к OSError: exception: access violation reading 0x0000000F

Может ли кто-нибудь указать мне правильное направление здесь?


person Unji    schedule 26.12.2014    source источник
comment
Не используйте теги для конкретной версии без общего тега.   -  person Vladimir F    schedule 26.12.2014
comment
Зачем вам стандартный вызов? На каком языке у вас окончательный план?   -  person Vladimir F    schedule 26.12.2014
comment
Некоторая функциональность скомпилированной DLL также используется в Excel/VBA — конечным языком будет либо C#, либо python в сочетании с VBA для модульного тестирования/вычислений общего назначения.   -  person Unji    schedule 26.12.2014
comment
Ожидайте, что функция будет реализована как функция void с дополнительным аргументом с указателем на массив результатов.   -  person Vladimir F    schedule 26.12.2014
comment
Это именно то, что я предполагал, но передача массива (или его указателя) в качестве дополнительного аргумента все равно приводит к нарушению прав доступа.   -  person Unji    schedule 26.12.2014


Ответы (1)


После некоторой обработки; также благодаря ценным предложениям Vladimir F решение проблемы, как описано выше, было найдено.

Чтобы заставить его работать, потребовались некоторые небольшие изменения на стороне Python:

import ctypes as cs
import numpy as np

#import dll library handle:
tdll = cs.WinDLL(pathToDLL)
#specify result and argument types
tdll.TST.restype = None
tdll.TST.argtypes = [cs.POINTER(cs.POINTER(cs.c_double*3)), cs.POINTER(cs.c_long)]

#call the dll function 'TST':
Ar = (cs.c_double*3)()
_A = cs.pointer(Ar)
tdll.TST(cs.byref(_A), cs.byref(cs.c_long(3)))
result = Ar[:]

Надеюсь, этот пост может быть полезен кому-то еще.

person Unji    schedule 26.12.2014