Getting a list of child classes

Another form of runtime information available in D is a list of all classes in the program. Using this information, we can achieve tasks that will be impossible at runtime, such as getting a list of all child classes of a particular class. A potential use of this capability will be to load game objects, defined as classes inheriting a common base, from an XML file at runtime.

How to do it…

Let's execute the following steps to get a list of child classes:

  1. Get the typeid of the parent class you're interested in.
  2. Loop over ModuleInfo with the foreach loop.
  3. Loop over the localClasses member of each loop value.
  4. Add recursion if you want to get all descendants, including the children's children.

The code is as follows:

ClassInfo[] getChildClasses(ClassInfo c) {
    ClassInfo[] info;
    foreach(mod; ModuleInfo) {
        foreach(cla; mod.localClasses) {
            if(cla.base is c)
                info ~= cla;
        }
    }

    return info;
}

class A {}
class B : A {}
class C : A {}

void main() {
    foreach(cla; getChildClasses(A.classinfo)) {
        import std.stdio;
        writeln(cla.name); // you could also create the class with cla.create();
    }
}

Running the program will give the following output:

test.B
test.C

Notice that test is the name of the module containing this program. Also, try adding more modules to inherit from class A and observe that they still work.

How it works…

Similar to TypeInfo, the D compiler also automatically creates static instances of a type called ModuleInfo, tailored to the needs of druntime, which provides limited runtime reflection across the entire program. The definition is in object.d, so no explicit import is required to use it.

Note

Since ModuleInfo is undocumented on the official website, its interface may be subject to change without notice.

The members of ModuleInfo include pointers to the module constructor, destructor, unit tests, a list of imported modules, a list of local classes, and the module name. It also has a static opApply function that lets us perform a foreach loop over it.

The primary use of ModuleInfo is for the internal druntime code. The druntime is responsible for calling your main function. Before it runs your function, it loops over all modules in the application to run their module constructors, using the list of imported modules to run them in the proper order. So, dependencies are initialized before the modules that use them. If unit tests are enabled, druntime also runs those tests through ModuleInfo before running main.

A secondary use of ModuleInfo is to find and inspect classes present in the program. This is how the Object.factory function is implemented, and it is through these facilities that we achieved our task to find all the child classes.

Getting a list of all the available child classes is impossible at compile time. While a list of base classes is possible, the compiler must be aware of all base classes to form a working inheritance hierarchy—a list of child classes is a runtime task because two modules or shared libraries may both provide child classes and may be compiled separately, with no knowledge of one another.

The foreach(mod; ModuleInfo) loops over each module in the program, yielding ModuleInfo* on each iteration. Then, for each module, we loop over the localClasses member. The localClasses array is an array of the TypeInfo_Class objects (sometimes known by its alias, ClassInfo), the same type we retrieved with typeid in the previous recipe. The base member of this object also points to a TypeInfo_Class instance, and it may be null in the case of Object, which is the root of all D classes.

By comparing these TypeInfo instances with the is identity operator, we will build a list of each type we're interested in. It can be used with further runtime reflection operations, including to call the create method to construct a default object of that class type.

It is impossible to declare a variable, cast to, or to use any other compile-time operation on a ClassInfo object to learn more about or manipulate the class because compile time is longer than the time we're in this function. The ClassInfo objects aren't special in any sense other than the fact that they are automatically created at runtime; they are just another block of data. We'll cover a technique to enable extended runtime reflection later in this chapter.

Note

Note that the names returned are the full name of the class, including module and package name. All the runtime class functions work with full names, the most common mistake when trying to use Object.factory is forgetting to use the module name as well as the class name.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.144.56