digitalmars.D - static this(), interdepencies, and bootstrapping
- Derek Ney (84/84) Aug 03 2008 I am working on an interpreted language written in D. It has an object
- Jason House (17/124) Aug 03 2008 The problem with the sample has nothing to do with the boot call table
- Lutger (5/12) Aug 03 2008 You can work around this problem by creating a third module that publicl...
- Lars Ivar Igesund (14/17) Aug 03 2008 The ordering is not new in 2.0, and has been well defined in the spec si...
- Sivo Schilling (87/192) Aug 03 2008 Hi Derek,
- Manfred_Nowak (16/17) Aug 03 2008 The problem in your example is, that you have cyclic `import's _and_ a
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
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, DerekThe 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
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, DerekYou 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
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
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, DerekHi 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
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