Thread overview
Using Enums to Generate Scoped List of Names
6 days ago
Dennis
6 days ago
Andrey Zherikov
August 03

I was looking at Walter's "Strawberries and Cream" presentation this morning and "Using Enums to Generate Scoped List of Names" section caught me with bad feeling: why do you need to generate code using string mixins if this can be done in more readable way (IMHO). So I played with it a bit and here is my solution:

    static struct S(E, T)
    {
        private T flags;
        private enum mask(string name) = 1 << __traits(getMember, E, name);

        pure nothrow @nogc @safe final {
            bool opDispatch(string name)(bool v)
            {
                v ? (flags |= mask!name) : (flags &= ~mask!name);
                return v;
            }
            bool opDispatch(string name)() const scope
            {
                return !!(flags & mask!name);
            }
        }
    }

    enum F {square,circle,triangle }

    S!(F, ubyte) s;

    assert(s.square = true);
    assert(!(s.circle = false));
    assert(s.triangle = true);

    assert(s.square);
    assert(!s.circle);
    assert(s.triangle);

Compare with this from the presentation:

    string generateBitFlags(E, T)() {
        string result = "pure nothrow @nogc @safe final {";
        enum enumName = __traits(identifier, E);

        foreach (size_t i, mem; __traits(allMembers, E)) {
            static assert(i < T.sizeof * 8, "too many fields”);
            enum mask = "(1 << "~i.stringof~")";
            result ~= "

            bool "~mem~"() const scope { return !!(flags & "~mask~"); }

            bool "~mem~"(bool v) {
                v ? (flags |= "~mask~") : (flags &= ~"~mask~");
                return v;
            }";
        }
        return result ~ "}\n private "~T.stringof~" flags;\n";
    }

    static struct S
    {
        mixin(generateFlags!(F, ubyte));
    }

    enum F { square, circle, triangle }

    S s;

    s.square = true;
    s.circle = false;
    s.triangle = true;

    assert(s.square == true);
    assert(s.circle == false);
    assert(s.triangle == true);

My solution can even support sparse bitmasks by simply tuning the enum:
enum F {square, circle=5, triangle=7 }

6 days ago

On Wednesday, 3 August 2022 at 15:49:25 UTC, Andrey Zherikov wrote:

>

why do you need to generate code using string mixins if this can be done in more readable way (IMHO).

In DMD, the generated getters/setters are extern(C++) so LDC and GDC can access them from C++. This doesn't work with opDispatch.

6 days ago

On Thursday, 4 August 2022 at 14:45:26 UTC, Dennis wrote:

>

In DMD, the generated getters/setters are extern(C++) so LDC and GDC can access them from C++. This doesn't work with opDispatch.

Will this work? It minimizes string mixins usage an has no opDispatch:

    static struct S(E, T)
    {
        private T flags;

        private template Impl(T mask)
        {
            pure nothrow @nogc @safe final
            {
                bool Impl(bool v)
	            {
    	            v ? (flags |= mask) : (flags &= ~mask);
        	        return v;
            	}

                bool Impl() const scope
	            {
    	            return !!(flags & mask);
                }
            }
        }

        static foreach (mem; __traits(allMembers, E))
            mixin("alias "~mem~" = Impl!(1 << E."~mem~");");
    }

    enum F { square,circle=5,triangle=7 }

    S!(F, ubyte) s;

    assert(s.square = true);
    assert(!(s.circle = false));
    assert(s.triangle = true);

    assert(s.square);
    assert(!s.circle);
    assert(s.triangle);