Initializing global/module variable object

Here’s a non-standard :warning: hack :warning: 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.

1 Like