Last active
September 28, 2021 03:05
-
-
Save ryandvmartin/a3b9bb0c485d2ae58143d71f55a8d9e4 to your computer and use it in GitHub Desktop.
Fortran Character Arrays and Python Ctypes .... the pain is real.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
! testing in the ipython notebook with the fortran code in one cell: | |
! %%file test.f90 | |
! and with the build call in the following cell: | |
! `!gfortran -shared test.f90 -o test.dll` | |
! note: some useful stuff found here: https://stackoverflow.com/a/13880611/5545005 | |
module c_interop | |
use iso_c_binding | |
implicit none | |
integer, parameter :: STRLEN = 64 | |
contains | |
subroutine py2fortran(nstring, cptr) bind(C, name="py2fortran_") | |
integer(c_int), intent(in) :: nstring | |
type(c_ptr), intent(in), value :: cptr | |
character, pointer :: fptr(:, :) | |
character(len=STRLEN), allocatable :: fstrings(:) | |
integer :: i, lenstr | |
call c_f_pointer(cptr, fptr, [STRLEN, nstring]) | |
allocate(fstrings(nstring)) | |
do i = 1, nstring | |
lenstr = cstrlen(fptr(:, i)) | |
fstrings(i) = transfer(fptr(1:lenstr, i), fstrings(i)) | |
enddo | |
do i = 1, nstring | |
print *, fstrings(i) | |
enddo | |
end subroutine py2fortran | |
subroutine py2fortran2(nstring, c_string) bind(C, name="py2fortran2_") | |
integer(c_int), intent(in) :: nstring | |
character, dimension(STRLEN, nstring), intent(in), target :: c_string | |
character(len=STRLEN) :: fstrings(nstring) | |
integer :: i, lenstr | |
do i = 1, nstring | |
lenstr = cstrlen(c_string(:, i)) | |
fstrings(i) = transfer(c_string(1:lenstr, i), fstrings(i)) | |
fstrings(i)(lenstr + 1:) = " " | |
enddo | |
do i = 1, nstring | |
print *, "transferred from ctypes: " // trim(fstrings(i)) | |
enddo | |
end subroutine py2fortran2 | |
subroutine fortran2py(nstring, cstring_p) bind(C, name="fortran2py_") | |
integer(c_int), intent(in) :: nstring | |
character(c_char), dimension(*), intent(inout) :: cstring_p | |
integer :: i, j, ks, kf, n | |
character(len=STRLEN) :: mystr(2) | |
mystr(1) = "This is the first string." | |
mystr(2) = "Wow. Fortran + Python + Strings = Pain !" | |
ks = 1 | |
do i = 1, nstring | |
n = len_trim(mystr(i)) | |
kf = ks + (n - 1) | |
cstring_p(ks:kf) = transfer(mystr(i)(1:n), cstring_p(ks:kf)) | |
cstring_p(kf + 1) = c_null_char | |
ks = ks + n + 1 | |
enddo | |
end subroutine fortran2py | |
function cstrlen(carray) result(res) | |
character(kind=c_char), intent(in) :: carray(:) | |
integer :: res | |
integer :: ii | |
do ii = 1, size(carray) | |
if (carray(ii) == c_null_char) then | |
res = ii - 1 | |
return | |
end if | |
end do | |
res = ii | |
end function cstrlen | |
end module c_interop |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from ctypes import * | |
lib = CDLL("test.dll") | |
func = getattr(lib, "py2fortran_") | |
# pass an array of strings to fortran | |
arr = (c_char * 64 * 2)() | |
arr[0].value = b"This is the first python string" | |
arr[1].value = b"This is the next one" | |
nstring = pointer(c_int(2)) | |
func(nstring, arr) | |
""" | |
Output: | |
This is the first python string | |
This is the next one | |
""" | |
# fill python memory with c_null_char delimited strings from fortran | |
func = getattr(lib, "fortran2py_") | |
nstring = pointer(c_long(2)) | |
carr = (c_char * 255)() | |
func(nstring, carr) | |
str1, str2 = ''.join([v.decode("utf-8") for v in carr]).rstrip("\x00").split("\x00") | |
str1, str2 | |
""" | |
Output: | |
('This is the first string.', 'Wow. Fortran + Python + Strings = Pain !') | |
""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks, this is gold. I wish there were some built-in methods for this. The struggle is real.