I am trying to learn how to complie FORTRAN code into a DLL that I can call from Python using ctypes. Even a simple example is not working, can anyone help?
I have a single procedure in FORTRAN:
subroutine ex(i)
integer i
i=i+1
return
end
Then I try to run this from Python
I compile it with the MinGW complier as follows
gfortran -c test.f
gfortran -shared -mrtd -o test.dll test.o
Looking at the DLL created I see
Microsoft (R) COFF/PE Dumper Version 12.00.30723.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file test.dll
File Type: DLL
Section contains the following exports for test.dll
00000000 characteristics
0 time date stamp Thu Jan 01 13:00:00 1970
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA name
1 0 00001280 ex_
Summary
1000 .CRT
1000 .bss
1000 .data
1000 .edata
1000 .eh_fram
1000 .idata
1000 .rdata
1000 .reloc
1000 .text
1000 .tls
Then I try to access this from Python
from ctypes import *
DLL = windll.test
print DLL
print getattr(DLL,'ex_')
print DLL[1]
print DLL.ex_
x = pointer( c_int(3) )
DLL.ex_( x )
The output is
<WinDLL 'test', handle 6bec0000 at 2143850>
<_FuncPtr object at 0x020E88A0>
<_FuncPtr object at 0x020E8918>
<_FuncPtr object at 0x020E88A0>
Traceback (most recent call last):
File "C:\proj_py\test.py", line 20, in <module>
DLL.ex_( x )
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
So although the function is there, I'm not calling it correctly. I'm stumped.
I am using python 2.7.10 (32 bit) on a 64-bit Windows-7 machine, I have a recent version of the MinGW compiler:
$ gfortran -v
Using built-in specs.
COLLECT_GCC=c:\mingw\bin\gfortran.exe
COLLECT_LTO_WRAPPER=c:/mingw/bin/../libexec/gcc/mingw32/4.8.1/lto-wrapper.exe
Target: mingw32
Configured with: ../gcc-4.8.1/configure --prefix=/mingw --host=mingw32 --build=mingw32 --without-pic --enable-shared --e
nable-static --with-gnu-ld --enable-lto --enable-libssp --disable-multilib --enable-languages=c,c++,fortran,objc,obj-c++
,ada --disable-sjlj-exceptions --with-dwarf2 --disable-win32-registry --enable-libstdcxx-debug --enable-version-specific
-runtime-libs --with-gmp=/usr/src/pkg/gmp-5.1.2-1-mingw32-src/bld --with-mpc=/usr/src/pkg/mpc-1.0.1-1-mingw32-src/bld --
with-mpfr= --with-system-zlib --with-gnu-as --enable-decimal-float=yes --enable-libgomp --enable-threads --with-libiconv
-prefix=/mingw32 --with-libintl-prefix=/mingw --disable-bootstrap LDFLAGS=-s CFLAGS=-D_USE_32BIT_TIME_T
Thread model: win32
gcc version 4.8.1 (GCC)
Can anyone offer a solution?
Thanks
Fortran is generally pass-by-reference. When calling from other languages like C, that means passing a memory address into the Fortran subroutine. Apparently the Python implementation is similar. Before your edit, you had an error that said something like "Illegal access at 0x000003" which was suspiciously the same value "3" as you were trying to pass as the value. You entered the Fortran subroutine just fine, but when it tried to do the add, it looked for the integer at memory location 3 instead of using the value 3 in the addition itself.
After your edit, you are passing a pointer (based on my comment, I think). That gives a different error. It suggests that you passed an extra argument because it got 4 bytes more data than it expected. I think that's likely an incompatibility between some 32 bit libraries and some 64 bit libraries, with 4 bytes being a likely difference in length between pointers in the two architectures.