I have a bunch of old F77 source code (usually compiled on a x86_64 with gfortran -std=legacy).
It contains quite a few functions in form:
double complex function f(x, y, i)
double precision x, y
integer i
f = cmplx(x, y) * i
return
end
I need to call these functions from some C++ code (usually compiled on a x86_64 with g++).
It works with the default Fortran
KIND=8:extern "C" { std::complex<double> f_(double *x, double *y, int *i); }It works when I enforce the default Fortran
KIND=4using the-freal-8-real-4option:extern "C" { std::complex<float> f_(float *x, float *y, int *i); }It works when I enforce the default Fortran
KIND=16using the-freal-8-real-16option (and in C++#include <quadmath.h>):extern "C" { __complex128 f_(__float128 *x, __float128 *y, int *i); }To my surprise, in this case, it also seems to work with (the returned value is in
*z):extern "C" { void f_(__complex128 *z, __float128 *x, __float128 *y, int *i); }Which of these two above prototypes is the (more?) proper one?
My problem is that I cannot get it working with my desired default Fortran
KIND=10using the-freal-8-real-10option. Inside of Fortran, thekind,precision,rangeandsizeofreturn values which directly correspond to the C++long double. So, I tried:extern "C" { std::complex<long double> f_(long double *x, long double *y, int *i); } extern "C" { void f_(std::complex<long double> *z, long double *x, long double *y, int *i); } extern "C" { void f_(long double *x, long double *y, int *i, std::complex<long double> *z); }But I cannot get it working at all.
Maybe I need to add some special flags to
gfortranand / org++calls in order to let C++ retrieve FortranKIND=10complex values? Note: I don't think I can use-ff2c.
Update (2020.08.04): I have been able to fool the C++ compiler so that it seems to generate proper code for any Fortran KIND=4,8,10. The trick is to use the ISO C99 _Complex in C++ (note: this trick is required only for KIND=10, but it actually works for KIND=4,8, too):
#include <complex.h>
#define C99KIND long double /* it can be "float", "double" or "long double" */
extern "C" { C99KIND _Complex f_(C99KIND *x, C99KIND *y, int *i); }
Note that, in C++, you cannot use e.g. long double complex but fortunately long double _Complex is still fine.
The usability of the ISO C99 _Complex in C++ is rather limited. For example, with -std=c++11 (or newer) even the most basic creal* and cimag* functions disappear.
So, the best idea is to immediately copy the returned value into some standard C++ templated complex variable, e.g using something like (note: f_ returns C99KIND _Complex):
std::complex<C99KIND> z = f_(&x, &y, &i);