Compilers utilise a technique that enables the linker to produce a list of symbols or data, available for iteration over. This is used by D compilers for a number of use cases such as ModuleInfo.
However, the method that each linker supports does vary and changes over time, so it is unsuitable as a general pattern that people should rely upon. It is also not supported in all compilers either.
Putting the behaviour into the compiler guarantees that it has ability to work, if D were to work on a given target. Consistently, in a way that does not rely upon knowledge of how linkers work.
Four new behaviours here are needed:
-
A special identifier is added to alias template parameters called
__udasymbol
, this will be populated by the compiler by default with the symbol that the UDA is placed upon. -
A new type is introduced, called
__linkerlist
, this type is always__gshared
, it cannot be copied nor heap allocated. It may be located in object.d or in language.2.1. It has two methods on it,
opApply
andopOpAssign
.2.2. The
opApply
method is only callable at runtime, doing so at CT would affect reproducibility of builds.2.3. The
opOpAssign
method is limited to appendingopOpAssign!"~"
, it is is only callable at CT, it is used to append to the list.
The benefits of this are that it can minimise templates and start-up times in the registration of symbols. This is used quite often in web development.
It is likely that you will want to have per-binary linker lists, if so, you will need a proposal such as what is described in this ticket: https://github.com/dlang/dmd/issues/21455
An example of this:
struct __linkerlist(Type) {
int opApply(int delegate(immutable(Type)*) del); // not callable at CT
void opOpAssign(string op:"~")(Type); // only callable at CT
}
struct RouteInfo {
string path;
void function() func;
}
__linkerlist!RouteInfo routes;
void Route(alias __udasymbol)(string path) {
routes ~= RouteInfo(path, &__udasymbol);
}
@Route("/path") // but actually is: @Route!myRoute("/path")
void myRoute() {
}
void main() {
import std.stdio;
foreach(immutable(RouteInfo)* route; routes) {
writeln(route.path);
}
}