www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - static this(), interdepencies, and bootstrapping

reply Derek Ney <derek hipgraphics.com> writes:
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
Aug 03 2008
next sibling parent Jason House <jason.james.house gmail.com> writes:
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

The problem with the sample has nothing to do with the boot call table (except that it's why you have static initialization). The following sample should cause the error as well: module A; import B; class A{ B ref_to_b; static this(){} } ========== module B; import A; class B{ A ref_to_a; static this(){} }
Aug 03 2008
prev sibling next sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
Derek Ney wrote:

...

 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

You can work around this problem by creating a third module that publicly imports the two modules with constructors, and import that one inside the modules with constructors.
Aug 03 2008
prev sibling next sibling parent Lars Ivar Igesund <larsivar igesund.net> writes:
Derek Ney wrote:
 
 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. 

The ordering is not new in 2.0, and has been well defined in the spec since the start. static this are called in lexical order, and static ~this in opposite lexical order. Without this it would be impossible to have runtime initialized data depending on other data, which probably is the more common usecase. And FWIW, circular dependencies are never a good idea if avoidable. -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Aug 03 2008
prev sibling next sibling parent Sivo Schilling <sivo.schilling web.de> writes:
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 $>
Aug 03 2008
prev sibling parent "Manfred_Nowak" <svv1999 hotmail.com> writes:
Derek Ney wrote:

 [...] get an order independent static this() like call?

The problem in your example is, that you have cyclic `import's _and_ a `static this' in _every_ module of the cycle. Your example gives you the solution to this problem: move the `static this' of at least one module of the cycle into the `module bootstrap'! This will not break the cyclic `import's, but the fact, that _every_ module in that cycle has a `static this'. This latter fact might enable a topological sorting of the remaining modules---of course: this is true only if your heterarchy of imports of modules with `static this' contains only one cycle. This is the case in your example though. -manfred -- Maybe some knowledge of some types of disagreeing and their relation can turn out to be useful: http://blog.createdebate.com/2008/04/07/writing-strong-arguments/
Aug 03 2008