What is your favorite D feature?

H. S. Teoh via Digitalmars-d digitalmars-d at puremagic.com
Fri Jun 23 12:25:18 PDT 2017


On Fri, Jun 23, 2017 at 06:51:13PM +0000, Anonymouse via Digitalmars-d wrote:
[...]
> Fairly specific, but foreach (member; someStruct.tupleof).
> 
> Part of my work on my IRC bot has been to serialise structs into
> configuration files (since std.json was *non-trivial* to deal with),
> and foreaching a someStruct.tupleof or a __traits(allMembers, symbol)
> allows for really, really interesting stuff.

Yup!  Recently I wrote a module that, given a struct S annotated with
certain UDA's, automatically:

1) Fills an instance of S with values extracted from a given range of
key/value pairs (with automatic type conversion from string input to the
correct field type via the awesome std.conv.to);

2) Displays a usage message listing the name of each field in S, its
type, along with a message explaining that the field is for (the message
is in a UDA attached to the struct field);

3) Given an instance of S, produces a listing of field names to the
current values in the given instance.

Put together, these 3 things can basically replace getopt(): you just
declare a struct with UDAs attaching descriptions to struct fields, then
just pass the program arguments along to this module and you're all set.
It will parse the program arguments and fill in the struct for you,
automatically generate a helpful usage message, and even dump the values
of the obtained settings if you implement a verbose mode, for example.

But it gets even better. Yesterday I added a new UDA that lets you split
up your struct into multiple nested structs, but have all the field
names visible at the top-level, i.e., say you have modules A, B, C, each
declaring their own configuration structs:

	module A;
	struct Config {
		@Desc("The number of iterations")
		int x;
	}

	module B;
	struct Config {
		@Desc("The starting value")
		float y;
	}

	module B;
	struct Config {
		@Desc("The output label")
		string z;
	}

Then you can combine all of these configuration parameters into a single
struct in your main program:

	struct Config {
		@Denest A.Config aCfg;
		@Denest B.Config bCfg;
		@Denest C.Config cCfg;
	}

The @Denest UDA tells the parser that aCfg.x should appear to the user
as simply "x", bCfg.y as just "y", and cCfg.z as "z".  So the user
doesn't have to know how your program is internally divided into
modules; all of the settings appear as top-level settings.

But internally, you pass Config.aCfg to module A, Config.bCfg to module
B, etc., thereby avoiding leaking details about module B to module A,
which would happen if you put everything into a single struct (you might
end up accidentally having code in module A depend on a setting that
belongs to module B, which breaks encapsulation and makes the code
harder to maintain).

D rawkz, I tell ya.


T

-- 
VI = Visual Irritation


More information about the Digitalmars-d mailing list