Henry
Henry

Reputation: 21

Use CFFI for using Python function inside Fortran. Warnings in cffi build. I can't get results from print in console or file, but not error in running

Coding

I followed documentation described in "CFFI Documentation Release 1.15.0" section "9.1 Usage" but with semplification, using a 'identity' function.

Step 1 - plugin.h

# ifndef CFFI_DLLEXPORT
# if defined(_MSC_VER)
# define CFFI_DLLEXPORT extern __declspec(dllimport)
# else
# define CFFI_DLLEXPORT extern
# endif
#endif
CFFI_DLLEXPORT int identity(int);

Step 2 - plugin_build.py

import cffi
ffiBuilder = cffi.FFI()
with open('plugin.h') as f:
    data = ''.join([line for line in f if not line.startswith('#')])
    data = data.replace('CFFI_DLLEXPORT', '')
    ffiBuilder.embedding_api(data)

ffiBuilder.set_source("my_plugin", r'''
#include "plugin.h"
''')

# Here thanks to @Armin in the comment I replace 'value'
# inside print and fw.write with 'str(value)' to avoid "python crash"
ffiBuilder.embedding_init_code("""
    from my_plugin import ffi
    @ffi.def_extern()
    def identity(value):
        print(str(value))
        with open('results.txt', 'w') as fw:
            fw.write(str(value))
        return value
""")

ffiBuilder.compile(target="plugin-1.5.*", verbose=True)

Step 4 - Execute plugin_build.py

python plugin_build.py I received that in console

generating .\my_plugin.c
the current directory is 'C:\\Users\\utente\\...\\FortranFiles\\CFFIexample5'
running build_ext
building 'my_plugin' extension
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\bin\HostX86\x64\cl.exe /c /nologo /Ox /W3 /GL /DNDEBUG /MD -IC:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.2032.0_x64__qbz5n2kfra8p0\include -IC:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.2032.0_x64__qbz5n2kfra8p0\include -IC:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\ATLMFC\include -IC:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include -IC:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\include\um -IC:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\ucrt -IC:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\shared -IC:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\um -IC:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\winrt -IC:\Program Files (x86)\Windows Kits\10\include\10.0.17763.0\cppwinrt /Tcmy_plugin.c /Fo.\Release\my_plugin.obj
my_plugin.c
my_plugin.c(1060): warning C4047: 'function': 'volatile PVOID *' differs in levels of indirection from 'volatile int *'
my_plugin.c(1060): warning C4022: '_InterlockedCompareExchangePointer': pointer mismatch for actual parameter 1
my_plugin.c(1060): warning C4022: '_InterlockedCompareExchangePointer': pointer mismatch for actual parameter 2
my_plugin.c(1060): warning C4022: '_InterlockedCompareExchangePointer': pointer mismatch for actual parameter 3
my_plugin.c(1060): warning C4047: '==': 'PVOID' differs in levels of indirection from 'int'
my_plugin.c(1095): warning C4047: 'function': 'volatile PVOID *' differs in levels of indirection from 'volatile int *'
my_plugin.c(1095): warning C4022: '_InterlockedCompareExchangePointer': pointer mismatch for actual parameter 1
my_plugin.c(1095): warning C4022: '_InterlockedCompareExchangePointer': pointer mismatch for actual parameter 2
my_plugin.c(1095): warning C4022: '_InterlockedCompareExchangePointer': pointer mismatch for actual parameter 3
my_plugin.c(1095): warning C4047: '==': 'PVOID' differs in levels of indirection from 'int'
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\bin\HostX86\x64\link.exe /nologo /INCREMENTAL:NO /LTCG /DLL /MANIFEST:EMBED,ID=2 /MANIFESTUAC:NO /LIBPATH:C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.2032.0_x64__qbz5n2kfra8p0\libs /LIBPATH:C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.9_3.9.2032.0_x64__qbz5n2kfra8p0\PCbuild\amd64 /LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\ATLMFC\lib\x64 /LIBPATH:C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\lib\x64 /LIBPATH:C:\Program Files (x86)\Windows Kits\NETFXSDK\4.6.1\lib\um\x64 /LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\ucrt\x64 /LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.17763.0\um\x64 python39.lib /EXPORT:PyInit_my_plugin .\Release\my_plugin.obj /OUT:.\plugin-1.5.dll /IMPLIB:.\Release\plugin-1.5.lib /MANIFEST
   Library creation .\Release\plugin-1.5.lib e dell'oggetto .\Release\plugin-1.5.exp
Code generation in progress ...
Code generation finished

This command creates my_plugin.c, plugin-1.5.dll in root project folder and my_plugin.obj, plugin-1.5.exp and plugin-1.5.lib in Release folder inside root project folder.

Step 5 - Create Fortran esempio.f90

program esempio
  use, intrinsic :: iso_c_binding, only : c_int

  integer(c_int) :: intero
  interface
     subroutine identity(a, output) bind (c, name="identity")
        use iso_c_binding
        integer(c_int), intent(in) :: a
        integer(c_int), intent(out) :: output
     end subroutine identity
  end interface

  call identity(4, intero)
  open(unit=100, file='filename.txt', status="unknown", action="write")
  write(100, '(I0)') intero
end program esempio

Step 6 - Link everything

gfortran -o testtest.exe esempio.f90 -L. plugin-1.5.dll

Command executed without any message printed in console, executable file testtest.exe was generated.

Step 7 - Run executable file

Before run testtest.exe, I set PYTHONHOME env. Command executed without any message printed in console and any files was created. Why? I expect files and print in console but nothing happen.

Tech Context

Help Please tell me something to go on, because I saw "warnings" in step 4, I don't know if they are negligible and how to procede to correct them. Thank you.

Upvotes: 2

Views: 308

Answers (1)

I managed to get your code working on Linux. I cannot guarantee it will work properly on Windows, but the changes make sense and should get you at least very close.

  1. use a function, not a subroutine when you have a non-void function in C

  2. pass the arguments with the value attribute

  integer(c_int) :: intero
  interface
     function identity(a) result(output) bind (c, name="identity")
        use iso_c_binding
        integer(c_int), value :: a
        integer(c_int) :: output
     end function identity
  end interface

  intero =  identity(4_c_int)

Disclaimer: I never used CFFI nor did I study any documentation for it, I just applied changes that seemed obvious enough.

Upvotes: 2

Related Questions