Thread overview
Using getSymbolsByUDA in a static foreach loop
Jan 19, 2022
Jack Stouffer
Jan 19, 2022
Adam D Ruppe
Jan 19, 2022
Jack Stouffer
Jan 19, 2022
Adam D Ruppe
Jan 20, 2022
Jack Stouffer
Jan 20, 2022
Adam Ruppe
Jan 20, 2022
Jack Stouffer
January 19, 2022

I'm trying to use getSymbolsByUDA in order to loop over all of the members in a struct with a certain UDA, and then call a function on the member. The plan is to use this to avoid looping over an array of function pointers.

However, the compiler is giving a strange error and the documentation of getSymbolsByUDA is unhelpful, as there are no practical use-case examples.

Here's a very simplified version of my code

import std.traits;

enum Runnable;

struct SubSystem
{
    void run();
}

struct Manager
{
    @Runnable SubSystem subsystem;

    void run()
    {
        static foreach(system; getSymbolsByUDA!(Manager, Runnable))
        {
            system.run();
        }
    }
}

void main()
{
    Manager m;
    m.run();
}

Result:

onlineapp.d(16): Error: value of `this` is not known at compile time

This seems to me to be the logical way to write this code. What am I missing?

January 19, 2022
On Wednesday, 19 January 2022 at 20:46:17 UTC, Jack Stouffer wrote:
>         static foreach(system; getSymbolsByUDA!(Manager, Runnable))
>         {
>             system.run();
> onlineapp.d(16): Error: value of `this` is not known at compile time

The getSymbols returns aliases, meaning you hit what I wrote about a few days ago:

http://dpldocs.info/this-week-in-d/Blog.Posted_2022_01_10.html#tip-of-the-week

The `this` is a runtime value and all the other `static` things work on compile time info.

So you want to `__traits(child, system, this).run()` and it should work - the traits child will re-attach a this value.
January 19, 2022

On Wednesday, 19 January 2022 at 20:53:29 UTC, Adam D Ruppe wrote:

>

So you want to __traits(child, system, this).run() and it should work - the traits child will re-attach a this value.

The error is actually coming from trying to use the result of getSymbolsByUDA in the right part of the static foreach, not the call to the run function. Which was odd to me because I thought it just returned a AliasSeq.

Here's a link to the erroring code with your traits change:

https://run.dlang.io/is/gO84ox

January 19, 2022
On Wednesday, 19 January 2022 at 21:44:57 UTC, Jack Stouffer wrote:
> The error is actually coming from trying to use the result of getSymbolsByUDA in the right part of the `static foreach`

huh......

I never use most of std.traits, they just complicate things. Bleh idk, I wouldn't bother with it and loop through the __traits instead.

January 20, 2022

On Wednesday, 19 January 2022 at 21:49:12 UTC, Adam D Ruppe wrote:

>

I never use most of std.traits, they just complicate things. Bleh idk, I wouldn't bother with it and loop through the __traits instead.

Unless I'm missing something obvious this has to be a DMD bug, because this prints nothing:

import std.traits;
import std.stdio;

enum Runnable;

struct SubSystem
{
        void run()
        {
        	writeln("SubSystem ran");
        }
}

struct Manager
{
        @Runnable SubSystem subsystem;

        void run()
        {
            static foreach(member; __traits(allMembers, Manager))
            {
                static foreach (attribute; __traits(getAttributes, member))
                {
                    static if (attribute == Runnable)
                    {
                        __traits(child, Manager, member).run();
                    }
                }
            }
        }
}

void main()
{
        Manager m;
        m.run();
}

The __traits(getAttributes, member) call always returns an empty tuple. Calling __traits(getAttributes, Manager.subsystem) manually works as expected.

January 20, 2022

On Thursday, 20 January 2022 at 00:55:33 UTC, Jack Stouffer wrote:

>
        static foreach(member; __traits(allMembers, Manager))

member here is a string, not the member. I prefer to call it memberName.

Then you __traits(getMember, Manager, memberName) to actually get the alias you can pass to getAttributes.

January 20, 2022

On Thursday, 20 January 2022 at 01:14:51 UTC, Adam Ruppe wrote:

>

On Thursday, 20 January 2022 at 00:55:33 UTC, Jack Stouffer wrote:

>
        static foreach(member; __traits(allMembers, Manager))

member here is a string, not the member. I prefer to call it memberName.

Then you __traits(getMember, Manager, memberName) to actually get the alias you can pass to getAttributes.

Thanks, that fixed it. Final working version for anyone who finds this thread:

import std.traits;
import std.stdio;

enum Runnable;

struct SubSystem
{
    void run()
    {
    	writeln("SubSystem ran");
    }
}

struct Manager
{
    @Runnable SubSystem subsystem;

    void run()
    {
        static foreach(memberName; __traits(allMembers, Manager))
        {
            static foreach (attribute; __traits(getAttributes, __traits(getMember, Manager, memberName)))
            {
                static if (is(attribute == Runnable))
                {
                    __traits(getMember, Manager, memberName).run();
                }
            }
        }
    }
}


void main()
{
    Manager m;
    m.run();
}