Thread overview
Using Enums to Generate Scoped List of Names
Aug 03, 2022
Andrey Zherikov
Aug 04, 2022
Dennis
Aug 04, 2022
Andrey Zherikov
Aug 16, 2022
Dennis
Aug 16, 2022
Walter Bright
Aug 16, 2022
Ali Çehreli
Aug 16, 2022
Walter Bright
Aug 20, 2022
Salih Dincer
August 03, 2022

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 }

August 04, 2022

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.

August 04, 2022

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);
August 16, 2022

On Thursday, 4 August 2022 at 23:31:37 UTC, Andrey Zherikov wrote:

>

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

Sorry for the late reply. I don't think it generates the right extern(C++) symbols for DMD, but I like the solution in general. Minimizing string mixins is good.

August 16, 2022
On 8/3/2022 8:49 AM, Andrey Zherikov wrote:
> I was looking at Walter's ["Strawberries and Cream" presentation](https://github.com/dlang/dconf.org/blob/master/2022/online/slides/strawberries.pdf) 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:

Haha, this is a nice improvement! Nobody can think of everything, which is why these forums are worth while!
August 16, 2022
On 8/16/22 08:07, Walter Bright wrote:

> Haha, this is a nice improvement! Nobody can think of everything, which is why these forums are worth while!

And your array-at-compile-time example can subjectively be better like this:

import std;

void main() {
  pragma(msg, iota(20).map!(n => n * n).array);
}

Ali
August 16, 2022
On 8/16/2022 8:34 AM, Ali Çehreli wrote:
> And your array-at-compile-time example can subjectively be better like this:
> 
> import std;
> 
> void main() {
>    pragma(msg, iota(20).map!(n => n * n).array);
> }

True, but my example was a slide-friendly version of a much more complex piece of real life code.
August 20, 2022

On Tuesday, 16 August 2022 at 15:34:30 UTC, Ali Çehreli wrote:

>

On 8/16/22 08:07, Walter Bright wrote:
And your array-at-compile-time example can subjectively be better like this:

import std;

void main() {
  pragma(msg, iota(20).map!(n => n * n).array);
}

Generates different types if not specifically specified. Because Walter initialized a static array. Although your example runs with #pragma , it creates a dynamic array:

import std.stdio;
enum N = 10;
void main()
{
	int[N] squares_A = () {
		int[N] squares;
		foreach (i; 0 .. N)
			squares[i] = i * i;
		return squares;
	}();
    squares_A.writeln(": ", typeid(squares_A));	

	import std.algorithm, std.range;
	//pragma(msg, iota(20).map!(n => n * n).array);/*
	auto squares_B = iota(N).map!(n => n * n).array;
	     squares_B ~= 100;
         squares_B.writeln(": ", typeid(squares_B));//*/
   /* [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]: int[10]
    * [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]: int[]
    */
}

Also, squares_A doesn't get any help from any modules.

SDB@79