Thread overview
can I generate an enum from a typelist?
Apr 08, 2014
Vlad Levenfeld
Apr 08, 2014
Vlad Levenfeld
Apr 08, 2014
Vlad Levenfeld
Apr 08, 2014
Rene Zwanenburg
Apr 09, 2014
Vlad Levenfeld
Apr 09, 2014
bearophile
Apr 09, 2014
Vlad Levenfeld
Apr 09, 2014
monarch_dodra
Apr 08, 2014
Frustrated
April 08, 2014
I'm a beginner to D and I have written this awful code:

    struct Layer
    {
	// defs
	alias Types  = TypeTuple !(Solid, Gradient, Text, Sprite, Plane);
	enum Type
	{
		solid 		= staticIndexOf !(Solid, Types),
		gradient	= staticIndexOf !(Gradient, Types),
		text		= staticIndexOf !(Text, Types),
		sprite		= staticIndexOf !(Sprite, Types),
		plane		= staticIndexOf !(Plane, Types),
		invalid		= -1
	}
	struct Solid
		{...}
	struct Gradient
		{...}
	struct Text
		{...}
	struct Sprite
		{...}
	struct Plane
		{...}
	// data
	const Type type;
	union
	{
		Solid 	 solid;
		Gradient gradient;
		Text 	 text;
		Sprite 	 sprite;
		Plane 	 plane;
	}
	// ctor
	@disable this ();
	this (T) (T layer)
		if (not (staticIndexOf !(T, Types) is (Type.invalid)))
		{
			auto type = cast (const Type) staticIndexOf !(T, Types);
			auto type_name = cast (const) to !string (EnumMembers !Type [type]);
			mixin ("this."~type_name~"= layer;");
			this.type = type;
		}
    }

So, the layer types are being rewritten 4 times. I anticipate hair pulling and teeth grinding when I come back later to add more layer types (after I've forgotten the implementation details).

Ideally, I'd like to just get the names of all the nested structs within the Layer struct and use a template to populate both the enum and the union, but I can't use templates to change the size of a type (so the union is out) and I have no idea how I might lowercase the first letter of each type name in the TypeTuple at compile time (there goes the enum).

Is there anything I can do to clean this mess up?  Maybe there's at least a way to generate an enum off the union using std.traits.allMembers or something?

I've got a feeling that my design kind of sucks anyway, so I'm open to suggestions to take it in completely different direction as well.
April 08, 2014
On Tuesday, 8 April 2014 at 07:39:29 UTC, Vlad Levenfeld wrote:
> I'm a beginner to D and I have written this awful code:
>
>     struct Layer
>     {
> 	// defs
> 	alias Types  = TypeTuple !(Solid, Gradient, Text, Sprite, Plane);
> 	enum Type
> 	{
> 		solid 		= staticIndexOf !(Solid, Types),
> 		gradient	= staticIndexOf !(Gradient, Types),
> 		text		= staticIndexOf !(Text, Types),
> 		sprite		= staticIndexOf !(Sprite, Types),
> 		plane		= staticIndexOf !(Plane, Types),
> 		invalid		= -1
> 	}
> 	struct Solid
> 		{...}
> 	struct Gradient
> 		{...}
> 	struct Text
> 		{...}
> 	struct Sprite
> 		{...}
> 	struct Plane
> 		{...}
> 	// data
> 	const Type type;
> 	union
> 	{
> 		Solid 	 solid;
> 		Gradient gradient;
> 		Text 	 text;
> 		Sprite 	 sprite;
> 		Plane 	 plane;
> 	}
> 	// ctor
> 	@disable this ();
> 	this (T) (T layer)
> 		if (not (staticIndexOf !(T, Types) is (Type.invalid)))
> 		{
> 			auto type = cast (const Type) staticIndexOf !(T, Types);
> 			auto type_name = cast (const) to !string (EnumMembers !Type [type]);
> 			mixin ("this."~type_name~"= layer;");
> 			this.type = type;
> 		}
>     }
>
> So, the layer types are being rewritten 4 times. I anticipate hair pulling and teeth grinding when I come back later to add more layer types (after I've forgotten the implementation details).
>
> Ideally, I'd like to just get the names of all the nested structs within the Layer struct and use a template to populate both the enum and the union, but I can't use templates to change the size of a type (so the union is out) and I have no idea how I might lowercase the first letter of each type name in the TypeTuple at compile time (there goes the enum).
>
> Is there anything I can do to clean this mess up?  Maybe there's at least a way to generate an enum off the union using std.traits.allMembers or something?
>
> I've got a feeling that my design kind of sucks anyway, so I'm open to suggestions to take it in completely different direction as well.

After a bit more thought I've managed to eliminate the TypeTuple and the enum by defining a const TypeInfo member. Also, std.string.toLower apparently works at compile time. Just when I thought D couldn't possible get more 'magic'...

Here is my current solution (redundant declarations of Solid... etc omitted) for  critique/completeness/posterity/something:

// data
const TypeInfo type;
union
{
	Solid 	 solid;
	Gradient gradient;
	Text 	 text;
	Sprite 	 sprite;
	Plane 	 plane;
}
// ctor
@disable this ();
this (T) (T layer)
{
	import std.string: toLower;
	auto type = cast (const) typeid (T);
	auto type_name = cast (const) toLower T.stringof);
	mixin ("this."~type_name~"= layer;");
	this.type = type;
}
April 08, 2014
Ok, starting to feel a bit sheepish at this point, that constructor was unnecessarily verbose:

this (T) (T layer)
{
	import std.string: toLower;
	mixin ("this."~toLower (T.stringof)~"= layer;");
	this.type = cast (const) typeid (T);
}

...is what I have settled on.
Sorry for all the noise! There doesn't seem to be a way to edit posts here...
April 08, 2014
On Tuesday, 8 April 2014 at 08:31:16 UTC, Vlad Levenfeld wrote:
> Ok, starting to feel a bit sheepish at this point, that constructor was unnecessarily verbose:
>
> this (T) (T layer)
> {
> 	import std.string: toLower;
> 	mixin ("this."~toLower (T.stringof)~"= layer;");
> 	this.type = cast (const) typeid (T);
> }
>
> ...is what I have settled on.

Depending on what you're exactly trying to do, you may be able to simplify your code using Algebraic [1] from std.variant.

> Sorry for all the noise! There doesn't seem to be a way to edit posts here...

Yeah, the forum is actually a web front end for the newsgroup ;)


[1] http://dlang.org/phobos/std_variant.html#.Algebraic
April 08, 2014
On Tuesday, 8 April 2014 at 08:31:16 UTC, Vlad Levenfeld wrote:
> Ok, starting to feel a bit sheepish at this point, that constructor was unnecessarily verbose:
>
> this (T) (T layer)
> {
> 	import std.string: toLower;
> 	mixin ("this."~toLower (T.stringof)~"= layer;");
> 	this.type = cast (const) typeid (T);
> }
>
> ...is what I have settled on.
> Sorry for all the noise! There doesn't seem to be a way to edit posts here...

You can simply reflect on your nested structs and generate the enum from them. No need to duplicate anything.

The only thing you have to do is figure out how to determine if a nested struct is part of the enum. If all nested structs of Layer are then it is easy. Else some naming scheme or inheritance needs to be used to distinguish them from others.

April 09, 2014
That's exactly what I was looking for. I pulled the Solid,...,etc definitions out of Layer and replaced the entire Layer definition with this:

alias Layer = Algebraic !(Solid, Gradient, Text, Sprite, Plane);

and after grepping in the get!(T) method, everything works perfectly. This is a really clean solution, thank you!
April 09, 2014
Vlad Levenfeld:

> alias Layer = Algebraic !(Solid, Gradient, Text, Sprite, Plane);

I suggest no space before the bang when you instantiate templates.

Bye,
bearophile
April 09, 2014
On Wednesday, 9 April 2014 at 00:27:48 UTC, bearophile wrote:
> Vlad Levenfeld:
>
>> alias Layer = Algebraic !(Solid, Gradient, Text, Sprite, Plane);
>
> I suggest no space before the bang when you instantiate templates.
>
> Bye,
> bearophile

Because it will be more easily confused with a logical not operator?
April 09, 2014
On Wednesday, 9 April 2014 at 07:37:18 UTC, Vlad Levenfeld wrote:
> On Wednesday, 9 April 2014 at 00:27:48 UTC, bearophile wrote:
>> Vlad Levenfeld:
>>
>>> alias Layer = Algebraic !(Solid, Gradient, Text, Sprite, Plane);
>>
>> I suggest no space before the bang when you instantiate templates.
>>
>> Bye,
>> bearophile
>
> Because it will be more easily confused with a logical not operator?

Because it's generic D syntax. what's on the right of "!" are considered the parameters of what's on the left. The "whole" is considered a "word" of sorts. If you apply a space, it inserts a break in the word, which looks weird.

And yes, it does add a bit of confusion (but that's mostly because I've *never* actually seen a space before the parameter specifier "!").

Also, if you've already defined the type tuple "Types", then you can just use that:

alias Layer = Algebraic!Types;

Short and sweet.