I’m trying to understand the behaviour of bind(C) which I don’t really get when applied to functions or variables.
When I compile a static library, functions and variables with bind(C) will be global (symbol types T and D) even if public is not used.
However, when I create a shared library from the static one:
functions with bind(C) will be global (symbol type T) even if I did not use public.
variables (might be protected) will be local (symbol type d) except if I use public.
Did you have a similar experience? I’m really interested in understanding why is it working this way?
N.B: Very often, I create shared libraries from the static libraries generated by fpm. I would like to avoid public for C API functions and variables to avoid polluting the Fortran side but still make C API functions and variables global.
Functions, subroutines and module variables are public by default. You need to use the private attribute or statement explicitly to hide them.
To ensure that bind(C) routines are not visible on the Fortran side, you might use a mixed-case C name. As Fortran is case-insensitive, a name like “FoO” is very unlikely to be visible. Fortran compilers sometimes use all-lowercase or all-uppercase names for their symbols, but I have never seen one with a mixture.
Perhaps it helps to think of this in C language terms. Variables and procedures with bind(C) have external linkage, meaning the symbol is visible throughout the whole program.
It has to work this way for interoperability with C:
// foo.c
int bar = 42;
! foo.f90
module foo
use, intrinsic :: iso_c_binding, only: c_int
implicit none
private
integer(c_int), bind(c,name="bar") :: bar
end module
The private and public attributes in Fortran apply when importing from a module. But nothing prevents you from declaring a second module that binds to the same variable:
! foo.f90
module foo2
use, intrinsic :: iso_c_binding, only: c_int
implicit none
private
integer(c_int), bind(c,name="bar") :: bar ! same bar as in foo
end module
Just a related question: I’ve just tried this code (for learning C-interoperability),
!! lib1.f90
module test1_m
use iso_c_binding
private
public show1
integer(c_int), bind(c,name="bar") :: bar = 100_c_int
contains
subroutine show1(); print *, bar; end
end module
!! lib2.f90
module test2_m
use iso_c_binding
private
public show2
integer(c_int), bind(c,name="bar") :: bar = 200_c_int
contains
subroutine show2(); print *, bar; end
end module
!! main.f90
program main
use test1_m
use test2_m
implicit none
call show1()
call show2()
end
But compilartion with gfortran-10 gives (on my mac):
$ gfortran lib1.f90 lib2.f90 main.f90
duplicate symbol _bar in:
/var/folders/9v/gx_yp_3913b9_kc8rzw0vtqc0000jb/T//cccB55Jw.o
/var/folders/9v/gx_yp_3913b9_kc8rzw0vtqc0000jb/T//ccFVEx25.o
ld: 1 duplicate symbol for architecture x86_64
collect2: error: ld returned 1 exit status
with
$ gfortran -c lib1.f90
$ nm lib1.o
...
0000000000000074 D _bar
(similar for lib2.o)
So I wonder the two modules define the same symbol twice? (in contrast to extern vs definition in C?)
EDIT: If I remove the initialization (100_c_int and 200_c_int), the code seems to compile without error. Also, if I remove only 200_c_int, the code compiles and gives 100 100. So if I do not give the initial value, it works as extern thing in C…?
I prefix all my module variables and/or functions that will be C interoperable with capi_*. This clearly indicates that they are not “pure fortran” variables or functions.
module example
use iso_c_binding
use iso_fortran_env
private
real(real64), parameter, public :: p
real(c_double), protected, bind(C, name="C_name_for_p") :: capi_p = p
! capi_p will not be global in the shared library if I don't specify `public`
contains
function f(args)
...
end function
function capi_f(args)bind(C, name="C_name_for_f")
! This function will be global in the shared library even I did not use `public`
...
end function
From this simple example, I understand that public will imply global (symbol type T) but this is not the case for variables. I don’t really understand the difference between variables and functions when bind(C) is used.