Search code examples
structddeclarationforward-declaration

Struct declaration order


If I define structs at the module level, I can reference not-yet defined structs.

struct S {
  ComesLater c;
}
struct ComesLater {}

But If I do the same inside an unittest or a function block, it doesn't work:

unittest {
  struct S {
    ComesLater c;
  }
  struct ComesLater {}
}

Error: undefined identifier 'ComesLater'

Why is that? How can I get order-independent declarations inside functions? Is there some kind of forward-declaration in d? I need this because I generate structs using mixin and ordering the declarations in the order of their inner-dependencies would be quite some effort, sometimes impossible, if there are circularly referencing structs. (using pointers.)


Solution

  • Declarations inside functions, unittests, or anywhere else that statements can actually be executed are indeed order-dependent because their values may depend on the code before them running. Think of a local variable:

    int a;
    writeln(a);
    a = b;
    int b = get_user_line();
    

    If order wasn't important there, when would the two functions get called? Would the user be asked for a line before the writeln as the declarations are rewritten?

    The current behavior of making b an undefined variable error keeps it simple and straightforward.

    It works independent of order in other contexts because there is no executable code that it can depend on, so there's no behavior that can change if the compiler needs to internally think about it differently.

    So:

    How can I get order-independent declarations inside functions?

    Change the context such that there is no executable code... put it all inside another struct!

    void main() { // or unittest { }
            struct Holder {
                    static struct S {
                            C c;
                    }
                    static struct C {}
            }
    }
    

    Since execution happens around the holder and doesn't happen inside it, the order of declaration inside doesn't matter again. Since you can define almost anything inside a struct, you can use this for variables, functions, other structs, and so on. Basically all you have to do is wrap your existing code inside the struct Holder {} brackets.

    By making everything static inside, you can just use it like a container and reference the stuff with Holder.S, etc., on the outside.