Here’s a non-standard
hack
to achieve initialization using global constructors that I found out about on OSDev.org,
// foo.c
#include <stdio.h>
// Global variables
int x = 42;
struct { float a[5]; } s;
__attribute__ ((constructor)) void foo(void)
{
printf("foo is called first!\n");
// Fill array
for (int i = 0; i < 5; i++) s.a[i] = i;
}
__attribute__ ((destructor)) void bar(void)
{
puts("Au revoir.");
}
! main.f90
module foo
use, intrinsic :: iso_c_binding
implicit none
private
public :: x, s
integer(c_int), bind(c,name="x") :: x
type, bind(c) :: something
real(c_float) :: a(5)
end type
type(something), bind(c,name="s") :: s
end module
program main
use foo, only: x, s
print *, "x = ", x
print *, "s.a[:] = ", s%a
end program
~/fortran/constructor$ gcc-13 -c foo.c
~/fortran/constructor$ gfortran-13 -o main main.f90 foo.o
~/fortran/constructor$ ./main
foo is called first!
x = 42
s.a[:] = 0.00000000 1.00000000 2.00000000 3.00000000 4.00000000
To understand why and how this works read the section 18.20.5 on How Initialization Functions Are Handled in the GCC internals manual. The meaning of the constructor/destructor
attributes can be found here.
Bear in mind, this is just a “side-effect” of the GCC Fortran implementation sitting on top of the C Runtime Initialization system (also known as crt0.o
). This is also why you have two “main” symbols in the symbol table:
$ echo "end" > program.f90
$ gfortran program.f90
$ nm a.out
0000000100003ed9 t _MAIN__
0000000100008010 d __dyld_private
U __gfortran_set_args
U __gfortran_set_options
0000000100000000 T __mh_execute_header
0000000100003ee0 T _main
0000000100003f50 s _options.0.0
U dyld_stub_binder
There is a Fortran _MAIN__
which is called from the C _main
. I suppose it’s more of an OS or platform thing, than related to the compiler. Intel Fortran on Mac also does things this way:
$ ifort -o main main.f90 foo.o
$ nm main | grep -i "main"
0000000100003f50 T _MAIN__
0000000100003f00 T _main
On a different operating system, there might be no such hook. On Windows for example with MSVC it’s already a bit more complex.
To make this more “plugin”-friendly, we could borrow a technique I saw used for extending Eigen’s MatrixBase class:
// user_plugins.c
#include "user_plugin_config.h"
#ifdef USER_PLUGIN
#include USER_PLUGIN
__attribute__ ((constructor)) void user_plugin_init(void) {
register_plugin();
}
#endif
// user_plugin_config.h
/* users may modify this file */
#define USER_PLUGIN "MyPlugin.h"
// MyPlugin.h
// Put global data here
// ...
void register_plugin(void) {
// Initialization goes here
// ...
}
Now that I think about it, R’s system for registering native routines (C and Fortran) is a similar type of thing.