September 25

For a long time now (~10 years), I've wanted a cheap way of registering symbols for frameworks to operate on. Existing registration schemes are expensive, and slow. They usually require some effort on the programmer to make happen, not ideal.

A solution to this can be seen in ModuleInfo and xgetmembers, have the compiler perform the registration of a symbol transformed into a descriptor list that then can be used to do stuff with at runtime.

However existing methods are not geared up to be used by a library writer for registration of custom data, this proposal offers a way to do this registration cleanly and effortlessly by the user. So they can focus on their goal rather than the how.

In this design, UDA's trigger registration of a symbol, but this UDA must first have an attribute specific to the list placed upon it.

In core.attributes the following mixin template implements the list mechanism.

mixin template ListOfSymbolState(SymbolState, alias SymbolTransformation) {
    private MySymbolState/* ... */ symbolStates;

    enum Attribute;

    ptrdiff_t length();
    int opApply(scope int delegate(immutable ref SymbolState) @safe nothrow @nogc) @safe nothrow @nogc;
}

To use it as a library writer:

module library.registration;

@mySymbolStates.Attribute
struct Route {
    string path;
}

mixin ListOfSymbolState!(MySymbolState, MySymbolTransformation) mySymbolStates;

package(library):

struct MySymbolState {
    string fqn, name;
}

template MySymbolTransformation(alias symbol) {
    enum MySymbolTransformation = MySymbolstate(__traits(fullyQualifiedName, symbol), __traits(identifier, symbol));
}

By the user:

import library.registration;

@Route("/")
void myRoute() {

}

When the enum Attribute in ListOfSymbolState is placed upon a symbol, that symbol when used as a UDA will trigger registration.

Registration is done on a symbol, that is then transformed using SymbolTransformation a template, that results in a value of SymbolState which must be a struct.

A SymbolState is stored in a list symbolStates that external code does not have direct access to.

The list is compiler and linker defined sequence of elements. Common strategies for this are linked lists and section appending. Due to the possibility of moving by the loader, no interior pointers are allowed in SymbolState or to an element.

Exportation of the list is compiler and platform defined, assume that it is not exported, and each binary must be registered separately. See dub's injectSourceFiles directive for how to do this.

This will enable deserialization, orm's, web routes, game logic, GUI controls, to self register.

September 25

I did something like that once, i agree it should be greatly simplified

    alias THIS_MODULE = game.states.gameplay;
    static foreach (member; __traits(allMembers, THIS_MODULE))
    {
        static foreach (attr; __traits(getAttributes, __traits(getMember, THIS_MODULE, member)))
        {
            static if (is(typeof(attr) == PacketType))
            {
                if(type == attr)
                {
                    found = true;
                    handled = true;

                    // check if it has params
                    static if (is(typeof( __traits(getMember, THIS_MODULE, member)) params == __parameters))
                    {
                        // LINFO("Packet Handler found: {} {}", attr, member.ptr);
                        // LINFO("Param: {}", params[0].stringof.ptr);

                        // get type of the packet (from the function parameter)
                        alias ptype = typeof(*params[0]);
                        static if (__traits(hasMember, ptype, "write") == false) static assert(0);

                        auto data = ptype();

                        static if (__traits(hasMember, ptype, "allocator"))
                        {
                            data.allocator = state.ctx.engine.arena.allocator();
                        }

                        data.read(&reader);
                        __traits(getMember, THIS_MODULE, member)(&data);
                    }
                }
            }
        }
    }
@(PacketType.RECONNECT)
void handle_reconnect(ReconnectPacket* rec)
{
    disconnect();
    state.net_info.host = rec.host;
    state.net_info.port = rec.port;
    state.net_info.connect_timer = 1.0;
}
``

I can never remember how to do it, never