简体   繁体   中英

Template an extern “C” function to call Fortran functions from C++ with different types

I have a library with fortran functions that is compiled in single and double precision, but I cannot alter anything in the source code of this library. Ideally, I would define the external function as

template<typename TF> extern "C" void fortran_function(TF*)

And call the function (with both calls in the same scope) as

double a[3] = { 2, 3, 4 };
fortran_function<double>(a);

float b[3] = { 2, 3, 4 };
fortran_function<float>(b);

However, this is not allowed. How do I solve this problem in an elegant way?

There is a very strong problems with this requirement. C++ does allow overloading for native C++ functions, but not for "C"language linkages. Linkage specifications [dcl.link] §6 says:

At most one function with a particular name can have C language linkage.

And your templating attempt is equivalent to declaring explicitely:

extern "C" void fortran_function(double *);
extern "C" void fortran_function(float *);

This would declare 2 different function with C language linkage and the same name => explicitely forbidden by C++ standard.

The rationale behind that is that common implementation use name mangling to build a function identifier containing the argument types for the linker to be able to identify them. The C language linkage precisely avoid that name mangling to allow interfacing with C language functions. That immediately defeats any overloading possibility.


Anyway, you will not be able to define 2 C or Fortran functions with the same name and using different parameters. The best I can imagine is to do manual mangling :

extern "C" void fortran_function_double(double *);
extern "C" void fortran_function_float(float *);

Maybe you could use macros to ease multiple declarations, but I am really not proficient enough in macro meta-programming...

You may have to use the C preprocessor to perform name mangling on the C++ side, but on the Fortran end there is no need to use non-Fortran tools to achieve the necessary mangling. Consider

! mangle.i90

subroutine mangle(x) bind(C,name='fortran_function_'// &
   trim(merge('float ','double',mykind==C_FLOAT)))
   real(mykind) x(3)
   x([2,3,1]) = x
end subroutine mangle

and

! mangle.f90

module floatmod
   use ISO_C_BINDING
   implicit none
   integer, parameter :: mykind = C_FLOAT
   contains
include 'mangle.i90'
end module floatmod

module doublemod
   use ISO_C_BINDING
   implicit none
   integer, parameter :: mykind = C_DOUBLE
   contains
include 'mangle.i90'
end module doublemod

When compiled via gfortran -c mangle.f90 you get a mangle.o file with subroutines fortran_function_float and fortran_function_double .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM