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