Python Ctypes for Advanced Fortran and C++

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
gfortran -fpic -c ddt.f90

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
gfortran -fpic -c modtest.f90

Now we compile a library lib.so from which our python code will call.

gfortran -fpic -shared modtest.o ddt.o -o lib.so

Now we want to see a list of functions and subroutines we can call in our python script from the compiled library lib.so

nm lib.so | less
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

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:

#!/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()
chmod 777 foobar.py
./foobar.py
 The area is   8.7500000    
Passed

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.

Struct in C++

Comments are closed.