AFAICU, it doesn’t build prototypes, but only helps you define the correct mangled symbol names. You are correct that this CMake module is only needed if you want to do things the old way, before standard interoperability with C was introduced.
That’s correct. Depending on the Fortran compiler selected at configuration time, it will generate a header (here for gfortran on Mac):
#ifndef FC_HEADER_INCLUDED
#define FC_HEADER_INCLUDED
/* Mangling for Fortran global symbols without underscores. */
#define FC_GLOBAL(name,NAME) name##_
/* Mangling for Fortran global symbols with underscores. */
#define FC_GLOBAL_(name,NAME) name##_
/* Mangling for Fortran module symbols without underscores. */
#define FC_MODULE(mod_name,name, mod_NAME,NAME) __##mod_name##_MOD_##name
/* Mangling for Fortran module symbols with underscores. */
#define FC_MODULE_(mod_name,name, mod_NAME,NAME) __##mod_name##_MOD_##name
#endif
Judging by the contents of the CMake module, it infers the name-mangling by compiling some small test programs. Annoyingly, the macros expect you to provide the names in both upper- and lower-case (the C preprocessor cannot change case, and Intel on Windows uses upper-case names for Fortran symbols). For example if there is an external Fortran procedure named mysub
, you’d use it like this:
#define mysub FC_GLOBAL(mysub, MYSUB)
It remains up to the user to specify the prototypes with the correct argument types:
// my_fortran_lib.h
#include "fc.h"
#define mysub FC_GLOBAL(mysub, MYSUB)
void mysub(int * /* n */, float * /* a */);
The CMake module can do the symbol wrapping step for you via the SYMBOLS argument:
FortranCInterface_HEADER(FCMangle.h
MACRO_NAMESPACE "FC_"
SYMBOLS mysub)
(You can control the prefix of symbols using SYMBOL_NAMESPACE
.)
When to use it?
- The Fortran compiler doesn’t support C binding.
- The Fortran side doesn’t use
bind(c)
and you cannot change the source (perhaps the Fortran code is distributed in compiled form).
- Using the standard approach would be too expensive or time-consuming.
As I see it, the CMake module only helps with names, but it has no knowledge of argument passing conventions (say assumed-length character strings, callbacks, structs/derived types, etc.), where you also need to know the type layout.
So in my eyes it’s really just “duct tape” for old F77-like libraries of external routines with arguments of the intrinsic types integer and real.
I’ve tried to imagine two examples of how this might be used:
Calling Fortran from C
! flib.f90
subroutine mysub(n, a)
integer, intent(in) :: n
real, intent(in) :: a(n)
print *, a
end subroutine
// c_flib.h
// (!) ONLY FOR DEMONSTRATION PURPOSES (!)
// WHEN POSSIBLE, USE THE STANDARD C INTEROPERABILITY
// WITH THE BIND(C) ATTRIBUTE ON THE FORTRAN SIDE INSTEAD
#include "c_flib_symbols.h"
void mysub(int *n, float *a); // <-- mangled name
# Fortran library (defines subroutine 'mysub')
add_library(flib flib.f90)
include(FortranCInterface)
FortranCInterface_HEADER(c_flib_symbols.h
MACRO_NAMESPACE "FC_"
SYMBOLS mysub)
# C Interface of the Fortran library
add_library(c_flib INTERFACE c_flib.h)
target_link_libraries(c_flib INTERFACE flib)
target_include_directories(c_flib INTERFACE ${CMAKE_BINARY_DIR})
Calling C from Fortran
// clib.c
#include "c_flib_symbols.h" // reusing same header as in previous example
#include <stdio.h>
void mysub(int *n, float *a) // <-- mangled name
{
for (int i = 0; i < *n; i++) {
printf("%f\n", a[i]);
}
}
In the CMake file you’d then build the library like this:
add_library(clib clib.c)
target_include_directories(clib PRIVATE ${CMAKE_BINARY_DIR})
To use it in a Fortran program, you’d use an interface block (desirable) or simply use the subroutine with external
(less desirable).