As the OP noted in his comment on @Armin Rigo's answer, the correct way to do this is to use wrapper functions. Also, as noted in the comments (by me) to the original question, this is also possible for C ++ and Fortran. However, the way to get this to work in all three languages ββis not necessarily obvious. Therefore, this answer is a working example for each language.
Suppose you have a C / C ++ / Fortran procedure that takes a scalar complex argument. Then you need to write a wrapper procedure that takes two floating point / double numbers (real and imaginary parts), combines them into a complex number and then calls the original procedure. Obviously, this can be extended to arrays of complex numbers, but for now, let's be simpler with one complex number.
C
For example, suppose you have a C function to print a formatted complex number. So we have my_complex.c :
#include <stdio.h> #include <complex.h> void print_complex(double complex z) { printf("%.1f + %.1fi\n", creal(z), cimag(z)); }
Then we need to add a wrapper function (at the end of the same file), for example, like this:
void print_complex_wrapper(double z_real, double z_imag) { double complex z = z_real + z_imag * I; print_complex(z); }
Compile this into the library in the usual way:
gcc -shared -fPIC -o my_complex_c.so my_complex.c
Then call the wrapper function from Python:
from ctypes import CDLL, c_double c = CDLL('./my_complex_c.so') c.print_complex_wrapper.argtypes = [c_double, c_double] z = complex(1.0 + 1j * 2.0) c.print_complex_wrapper(c_double(z.real), c_double(z.imag))
C ++
The same thing in C ++ is a bit more complicated because you need to define an extern "C" interface to avoid name distortion, and we need to deal with a class in Python (both according to this SO Q & A ).
So now we have my_complex.cpp (to which I have already added a wrapper function):
#include <stdio.h> #include <complex> class ComplexPrinter { public: void printComplex(std::complex<double> z) { printf("%.1f + %.1fi\n", real(z), imag(z)); } void printComplexWrapper(double z_real, double z_imag) { std::complex<double> z(z_real, z_imag); printComplex(z); } };
We also need to add the extern "C" interface (at the end of the same file) as follows:
extern "C" { ComplexPrinter* ComplexPrinter_new() { return new ComplexPrinter(); } void ComplexPrinter_printComplexWrapper(ComplexPrinter* printer, double z_real, double z_imag) { printer->printComplexWrapper(z_real, z_imag); } }
Assemble the library in the usual way:
g++ -shared -fPIC -o my_complex_cpp.so my_complex.cpp
And call the wrapper from Python:
from ctypes import CDLL, c_double cpp = CDLL('./my_complex_cpp.so') cpp.ComplexPrinter_printComplexWrapper.argtypes = [c_double, c_double] class ComplexPrinter: def __init__(self): self.obj = cpp.ComplexPrinter_new() def printComplex(self, z): cpp.ComplexPrinter_printComplexWrapper(c_double(z.real), c_double(z.imag)) printer = ComplexPrinter() z = complex(1.0 + 1j * 2.0) printer.printComplex(z)
Fortran
And finally, in Fortran, we have the original routine in my_complex.f90 :
subroutine print_complex(z) use iso_c_binding, only: c_double_complex implicit none complex(c_double_complex), intent(in) :: z character(len=16) :: my_format = "(f4.1,a3,f4.1,a)" print my_format, real(z), " + ", aimag(z), "i" end subroutine print_complex
To which we add a wrapper function (at the end of the same file):
subroutine print_complex_wrapper(z_real, z_imag) bind(c, name="print_complex_wrapper") use iso_c_binding, only: c_double, c_double_complex implicit none real(c_double), intent(in) :: z_real, z_imag complex(c_double_complex) :: z z = cmplx(z_real, z_imag) call print_complex(z) end subroutine print_complex_wrapper
Then compile to the library in the usual way:
gfortran -shared -fPIC -o my_complex_f90.so my_complex.f90
And a call from Python (note the use of POINTER):
from ctypes import CDLL, c_double, POINTER f90 = CDLL('./my_complex_f90.so') f90.print_complex_wrapper.argtypes = [POINTER(c_double), POINTER(c_double)] z = complex(1.0 + 1j * 2.0) f90.print_complex_wrapper(c_double(z.real), c_double(z.imag))