1

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?

Chiel
  • 6,006
  • 2
  • 32
  • 57
  • 2
    Declare two distinct functions, with whatever names they are exported under from Fortran library. Then, if you are so inclined, write a wrapper as a function template, with specializations that call one or the other Fortran function as necessary. – Igor Tandetnik Jun 01 '18 at 13:51
  • My problem is that both functions have the same name as it is the same library compiled twice. And `extern "C"` does not permit overloading. – Chiel Jun 01 '18 at 13:53
  • @ShadowRanger. It is not. I don't have a Fortran interface structure available. – Chiel Jun 01 '18 at 13:54
  • 2
    Then you have a problem. No matter what you do on C++ side, the linker will be confused by two libraries exporting the same symbol. Somehow, you'll have to arrange for them to be exported under different names. I don't know anything about Fortran, so can't assist you further. – Igor Tandetnik Jun 01 '18 at 13:55
  • This will be the same for C as is for Fortran. You must specialize your templates by hand. – Vladimir F Героям слава Jun 01 '18 at 13:55
  • @VladimirF Show me how, please. – Chiel Jun 01 '18 at 13:56
  • @Chiel: Okay, so your scenario is *worse* than the possible duplicate. I'm pretty sure you can't actually use the function from both libraries in the same program *period*; the functions have the same linkage name, with no namespacing or the like to disambiguate (because C linkage doesn't handle stuff like that), so the libraries are fundamentally incompatible with one another. You couldn't do what you're attempting natively in Fortran, let alone in C or C++. – ShadowRanger Jun 01 '18 at 13:56
  • Those functions do NOT have the same linkage name. That is impossible to achieve from Fortran! It is really the same for Fortran and C. Fortran generics will not help here at all, they are not visible by the linker, they are just an internal Fortran tool. – Vladimir F Героям слава Jun 01 '18 at 13:58
  • Also the second duplicate is not the same as the functions have different names there. – Chiel Jun 01 '18 at 14:00
  • 1
    @VladimirF: If I understand the OP, they compiled almost identical Fortran code to a library twice, once with all the APIs taking C `float`, one with all the APIs taking C `double`. C linkage doesn't name mangle to indicate argument types, so each library would have the same name; trying to link them both would inevitably fail (because as you note, it wouldn't be legal to have the same name appear twice; it wouldn't work at all if they'd tried to make a single library with the same function repeated twice). – ShadowRanger Jun 01 '18 at 14:00
  • OMG, then it really can't work. In that case I will re-open. One has to name them differently. – Vladimir F Героям слава Jun 01 '18 at 14:00
  • @VladimirF. The library defines the real type in Fortran based on a precompiler statement. The library is compiled twice with different precompiler flags. – Chiel Jun 01 '18 at 14:01
  • Then just name them differently using the pre-compiler. – Vladimir F Героям слава Jun 01 '18 at 14:02
  • @VladimirF. How do you picture that? I have one statement in a module that sets the real type, and this module is imported in many other files. – Chiel Jun 01 '18 at 14:03
  • 2
    The only way you could have done that would be to have two different fortran .f files compiled into two different .o files. If that's the case, you put yourself into the corner, and there is nothing you can do about it, in any language. If it is not the case and you have single .o file, post the result of `nm <.o>` for your two functions. – SergeyA Jun 01 '18 at 14:07
  • That will not be enough. You can start here https://stackoverflow.com/questions/39679689/concatenate-strings-in-a-macro-using-gfortran to add something like _sp and _dp to your function names. – Vladimir F Героям слава Jun 01 '18 at 14:09

2 Answers2

2

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...

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • It is thus impossible? – Chiel Jun 01 '18 at 14:27
  • 1
    Yep, be aware of the quirks of the C pre-procecessor in gfortran though, so again the link https://stackoverflow.com/questions/39679689/concatenate-strings-in-a-macro-using-gfortran and also https://stackoverflow.com/questions/41243555/concatenating-an-expanded-macro-and-a-word-using-the-fortran-preprocessor That can be used to append the suffix to the function name. – Vladimir F Героям слава Jun 01 '18 at 14:49
2

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.

user5713492
  • 954
  • 5
  • 11
  • This is a very interesting solution. Is it portable to other compilers? – ptb Jun 01 '18 at 17:40
  • @ptb It's standard Fortran, but uses aspects of the language which are not necessarily well supported by some compilers. If it doesn't work on your favorite compiler send them a bug report so they can fix it. – user5713492 Jun 01 '18 at 18:32