Problem
The cytpes library in python 2.6 and greater allows python to call fortran, c, and c++ functions from a specially compiled library. However the python cytpes library is limited in the datatypes that can be called from those libraries. This tutorial will show how to call derived data types from a fortran library and structs in a c++ in python.
Ctypes Fundamental Data Types
Here is a list of data types that can be passed through the ctypes library. For more information on ctypes please visit the python cytpes tutorial.
ctypes type | C type | Python type |
---|---|---|
c_char | char | 1-character string |
c_wchar | wchar_t | 1-character unicode string |
c_byte | char | int/long |
c_ubyte | unsigned char | int/long |
c_short | short | int/long |
c_ushort | unsigned short | int/long |
c_int | int | int/long |
c_uint | unsigned int | int/long |
c_long | long | int/long |
c_ulong | unsigned long | int/long |
c_longlong | __int64 or long long |
int/long |
c_ulonglong | unsigned __int64 or unsigned long long |
int/long |
c_float | float | float |
c_double | double | float |
c_char_p | char * (NUL terminated) |
string or None |
c_wchar_p | wchar_t * (NUL terminated) |
unicode or None |
c_void_p | void * | int/long or None |
Ctypes in python does not support booleans, derived data types, objects, structs, and other commonly used data types.
Solutions
Derived Data Type
ddt.f90
module ddt
implicit none
type rect !Name of data type
real :: height
real :: length
end type rect
contains
subroutine calc_area(rc)
type(rect) :: rc
real area, h, l
h = rc%height
l = rc%length
area = h*l
print *, "The area is", area
end subroutine
end module
[bash]gfortran -fpic -c ddt.f90[/bash]
Because there is no ctype for derived data types in fortran we get around this by making an extra module in fortran that will have subroutines that we will call in python.
modtest.f90
module modtest
use ddt
contains
subroutine pcall_area
type(rect) :: rc
rc%height = 3.5
rc%length = 2.5
call calc_area(rc)
end subroutine
end module modtest
[bash]gfortran -fpic -c modtest.f90[/bash]
Now we compile a library lib.so from which our python code will call.
[bash]gfortran -fpic -shared modtest.o ddt.o -o lib.so[/bash]
Now we want to see a list of functions and subroutines we can call in our python script from the compiled library lib.so
[bash]nm lib.so | less[/bash]
[text]
0000000000000f0e T ___ddt_MOD_calc_area
0000000000000f3a T ___modtest_MOD_pcall_area
0000000000000f08 t __dyld_func_lookup
0000000000000000 t __mh_dylib_header
U dyld_stub_binder
0000000000000ef4 t dyld_stub_binding_helper
[/text]
We want to call ___modtest_MOD_pcall_area in our python script. In order to do this we need to remove one of the underscores in the front as shown:
[python firstline=”1″]
#!/usr/bin/python
#Filename: foobar.py
#Author: Derek Carr
#Website: http://www.zaphinath.com
from ctypes import *
libtest = cdll.LoadLibrary("./lib.so")
method = libtest.__modtest_MOD_pcall_area
try:
method()
print "Passed"
except:
print "Failed"
traceback.print_exc()
sys.exit()
[/python]
[bash]
chmod 777 foobar.py
./foobar.py
The area is 8.7500000
Passed
[/bash]
Now we have successfully called a subroutine from fortran that uses a derived data type or defined data type and used that subroutine in python.