static this(), interdepencies, and bootstrapping

Sivo Schilling sivo.schilling at web.de
Sun Aug 3 14:33:27 PDT 2008


Derek Ney Wrote:

> I am working on an interpreted language written in D. It has an object
> structure similar to ruby. There is a set of about 10 classes that form the
> base of the object hierarchy. Bootstraping the hierarchy at startup is tricky
> and I have based it all on static this() calls. The static this() calls
> register the different classes so a run-time table can be created which knows
> about all the different classes and their bootstrapping calls.
> 
> Unfortunately when I finally got it compiled for the first time, and ran it, I
> got this error from the D runtime (from std/moduleinit.d in fact):
> 
> Error: circular initialization dependency with module base
> 
> Looking at the code for moduleinit.d (I am using D2.0, 2.017 compiler) I see
> that it is this guarantee of the order in which static this() is called that
> is causing my problem. In my case I do not care at all about the order in
> which the static this() calls are called. I designed it to not care about that
> order. But there does not appear to be any way to tell D that I do not care
> and so none of this code will work. I noted that moduleinit.d does have a
> "ictor" member in ModuleInfo that is labeled "order independent ctor". I would
> love to use that, but I do not see anyway to do that. Is there some way of
> specifying that a static this() call is order independent?
> 
> To illustrate my problem, here is a short set of 3 files that uses a
> "bootstrap" template to create a table called boot_call_table that maps a name
> to the boot function for the class. The table gets filled in by static this()
> in the mixin "Bootstrap". In main() it iterates over the table and calls each
> boot function. Here is the code:
> 
> -----File mod_a.d-----
> module mod_a;
> 
> import bootstrap;
> import mod_b;
> 
> import std.stdio;
> 
> class A
> {
>   B ref_to_b;
> 
>   static void boot_class_a(string name)
>   {
>     writeln("bootstrap name: ", name);
>     // initialize some A stuff...
>   }
> 
>   mixin Bootstrap!(boot_class_a, "class A");
> }
> 
> void main()
> {
>   // boot all the things up
>   foreach(string name, BootCall call; boot_call_table)
>   {
>     call(name);
>   }
> }
> ----- File mod_b.d -----
> module mod_b;
> 
> import bootstrap;
> import mod_a;
> 
> import std.stdio;
> 
> class B
> {
>   A ref_to_a;
> 
>   static void boot_class_b(string name)
>   {
>     writeln("bootstrap name: ", name);
>     // initialize some B stuff...
>   }
> 
>   mixin Bootstrap!(boot_class_b, "class B");
> }
> ----- File bootstrap.d -----
> module bootstrap;
> 
> alias void function(string) BootCall;
> 
> BootCall[string] boot_call_table;
> 
> // mixin that will cause boot_call to be made at startup
> template Bootstrap(alias boot_call, string name)
> {
>   static this()
>   {
>     boot_call_table[name] = &boot_call;
>   }
> }
> ----- end of files -----
> 
> This sample will generate the error "Error: circular initialization dependency
> with module mod_a". If you comment out the mixin Bootstrap! call in mod_b.d
> and recompile, it will run fine and print "bootstrap name: class A" as expected.
> 
> I know that this "ordering" of static this() calls was added to 2.0 presumably
> because people did not like the non-deterministic order in which they were
> called. But for my application, there is no way I can get rid of the
> "circular" nature (except for putting every single class into one gigantic
> file). Is there some trick that someone has for me to get an order independent
> static this() like call?
> -Thanks, Derek

Hi Derek,

as Lutger suggested you can solve the problem, if you put
the static initialization of your BootCall table in a
static modul constructor in module bootstrap.d; but then
you have to import all modules referencing BootCall in
your bootstrap.d module.
You can avoid this if you place an initialization functions
in a new module (see below, named as mod_i.d). The declaration
of this function as extern(C) prevents the linker to complain
of unresolved external function reference.

-----File mod_a.d-----
module mod_a;

import bootstrap;
import mod_b;

import std.stdio;

class A
{
  B ref_to_b;

  static void boot_class_a(string name)
  {
    writeln("bootstrap name: ", name);
    // initialize some A stuff...
  }

  mixin Bootstrap!(boot_class_a, "class A");
}

void main()
{
  // boot all the things up
  foreach(string name, BootCall call; boot_call_table)
  {
    call(name);
  }
}
----- File mod_b.d -----
module mod_b;

import bootstrap;
import mod_a;

import std.stdio;

class B
{
  A ref_to_a;

  static void boot_class_b(string name)
  {
    writeln("bootstrap name: ", name);
    // initialize some B stuff...
  }

  mixin Bootstrap!(boot_class_b, "class B");
}
----- File bootstrap.d -----
module bootstrap;

alias void function(string) BootCall;

BootCall[string] boot_call_table;

// mixin that will cause boot_call to be made at startup
template Bootstrap(alias boot_call, string name)
{
  /*** static this() replaced ***/
  static void build_table()
  {
    boot_call_table[name] = &boot_call;
  }
}

/*** initalizer added ***/
extern(C) void build_tables();

/*** modul constructor added ***/
static this()
{
    build_tables();
}
----- File mod_i.d -----  /*** file added ***/
// !! the table construction goes here !!

module mod_i;
import mod_a;
import mod_b;
import bootstrap;

extern(C) void build_tables()
{
    A.build_table();
    B.build_table();
}
----- end of files -----

Compiled with DMD 2.017, running the executable and output is as expected:
$>dmd mod_a.d mod_b.d mod_i.d bootstrap.d -ofbootstrap_v2.exe
$>bootstrap_v2
bootstrap name: class A
bootstrap name: class B
$>




More information about the Digitalmars-d mailing list