www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - can I generate an enum from a typelist?

reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
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.
Apr 08 2014
parent reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
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; }
Apr 08 2014
parent reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
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...
Apr 08 2014
next sibling parent reply "Rene Zwanenburg" <renezwanenburg gmail.com> writes:
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 ;)
Apr 08 2014
parent reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
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!
Apr 08 2014
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Vlad Levenfeld:

 alias Layer = Algebraic !(Solid, Gradient, Text, Sprite, Plane);
I suggest no space before the bang when you instantiate templates. Bye, bearophile
Apr 08 2014
parent reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
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?
Apr 09 2014
parent "monarch_dodra" <monarchdodra gmail.com> writes:
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.
Apr 09 2014
prev sibling parent "Frustrated" <Who where.com> writes:
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.
Apr 08 2014