produced binary is quite big

2012-02-26 Thread Jabba Laci
Hi,

I'm new to D. I tried the basic Hello World program (dmd hello.d)
but it produced a 316 KB big binary. The same thing with C (gcc
hello.c) is about 9 KB. Is there a way to reduce the size of the
produced binary?

Thanks,

Laszlo


Syntax highlighting for CodeRunner

2012-02-26 Thread Joshua Niehus
In the off chance that some of you are running a Mac and using 
CodeRunner to play around with D, I cooked up the files you need 
for CodeRunner to highlight D's syntax:

https://github.com/jniehus/Dlang-for-CodeRunner


Re: produced binary is quite big

2012-02-26 Thread James Miller
On 26 February 2012 21:28, Jabba Laci jabba.l...@gmail.com wrote:
 Hi,

 I'm new to D. I tried the basic Hello World program (dmd hello.d)
 but it produced a 316 KB big binary. The same thing with C (gcc
 hello.c) is about 9 KB. Is there a way to reduce the size of the
 produced binary?

 Thanks,

 Laszlo

Phobos (the standard library) is currently not a shared library, due
to the fact that shared library support in D is poor at the moment,
and D itself is relatively unstable, otherwise we would have 100's of
versions of the library floating around...

The C version is small because it uses the C library and runtime,
which are dynamically linked, so aren't included in the size of the
file. Try statically linking the C version, I can't remember exactly
how to do that at the moment, and you'll find the sizes to be similar.

Also compiling with -release and -O2 would reduce the size, but I'm
not sure by how much.


Re: Make alias parameter optional?

2012-02-26 Thread Ali Çehreli

On 02/25/2012 05:04 PM, Robert Rouse wrote:
 On Saturday, 25 February 2012 at 23:10:51 UTC, Ary Manzana wrote:
 On 2/25/12 7:31 PM, Robert Rouse wrote:

...

 This means that D can simulate Ruby blocks more than I thought. That's
 pretty awesome. I'm loving D more every day.

 How's that like a Ruby block?

 The D code simulates the following Ruby if you were to make bar print
 something with writeln.

 def foo(a, b, block)
 puts a is #{a})
 b.call
 yield
 end

 f = lambda { puts good bye }

 foo(1, f) { puts something }


 That's what I'm talking about.


I don't know Ruby but from what I've read so far about Ruby blocks, 
their D equivalents may also be D ranges.


Ali



Re: Supporting and signature-checking all foreach variations

2012-02-26 Thread Ali Çehreli

On 02/25/2012 08:25 AM, Ashish Myles wrote:
 I want to define a general-purpose centroid computer for point containers
 and ran into a couple of challenges. Firstly, here is the basic code

  Point3 computeCentroid(PointContainer)(const ref PointContainer C)
  if (...)// want a signature constraint for usability of 
foreach

  {
  Point3 c = Point3(0.0, 0.0, 0.0);
  size_t total = 0;
  foreach(Point3 p; C) {   // enforce that the container 
supports this

  c += p; ++total;
  }
  if (total  0)
  c /= cast(double)(total);
  return c;
  }

...

 2. Secondly, TDPL on page 381 says that foreach iterates over C[], if
C defines the opSlice() function without any arguments.

Although what you describe also seems useful, that heading seems to be 
about ranges and specifically about the three InputRange functions. The 
feature has indeed been implemented recently:


  http://d.puremagic.com/issues/show_bug.cgi?id=5605

However the code above doesn't seem to work and requires me to
explicitly invoke the slice operator myself like
  foreach(p; C[]) { ... }
when my data structure clearly defines the following functions.
  Point3[] opSlice() { return _cpts[]; }
  const (Point3)[] opSlice() const { return _cpts[]; }
Is this a misunderstanding on my part or an unimplemented feature?

But I've just verified that the following works with dmd 2.058:

import std.stdio;

struct Point3
{}

struct MyCollection
{
Point3[] _cpts;

Point3[] opSlice() { return _cpts; }  // -- _cpts[] works too

const (Point3)[] opSlice() const { return _cpts; }
}

void main()
{
auto coll = MyCollection();

foreach (i; coll) {
// ...
}
}

Ali



Re: Make alias parameter optional?

2012-02-26 Thread Jacob Carlborg

On 2012-02-26 11:03, Ali Çehreli wrote:

On 02/25/2012 05:04 PM, Robert Rouse wrote:
  On Saturday, 25 February 2012 at 23:10:51 UTC, Ary Manzana wrote:
  On 2/25/12 7:31 PM, Robert Rouse wrote:

...

  This means that D can simulate Ruby blocks more than I thought. That's
  pretty awesome. I'm loving D more every day.
 
  How's that like a Ruby block?
 
  The D code simulates the following Ruby if you were to make bar print
  something with writeln.
 
  def foo(a, b, block)
  puts a is #{a})
  b.call
  yield
  end
 
  f = lambda { puts good bye }
 
  foo(1, f) { puts something }
 
 
  That's what I'm talking about.
 

I don't know Ruby but from what I've read so far about Ruby blocks,
their D equivalents may also be D ranges.

Ali


A Ruby block is basically like a delegate in D and has nothing to do 
with ranges.


Ruby:

def foo (block)
block.call
end

foo do
p asd
end

D:

void foo (void delegate () block)
{
block();
}

void main ()
{
foo({
writeln(asd);
});

foo(() = writeln(asd)); // new lambda syntax
}

Both examples print asd. If you want to have a more Ruby looking 
syntax in D you do some operator overload abuse:


struct Block
{
void delegate (void delegate ()) impl;

void opIn (void delegate () block)
{
impl(block);
}
}

Block foo ()
{
return Block((x) = x());
}

void main ()
{
foo in {
writeln(asd);
};
}

--
/Jacob Carlborg


Re: struct init() method

2012-02-26 Thread Timon Gehr

On 02/26/2012 12:18 AM, Jonathan M Davis wrote:

On Saturday, February 25, 2012 17:07:14 Timon Gehr wrote:

This is useful:

struct S{
  @disable enum init = 0;
}


I thought that the way that you were supposed to do that was

@disable this();

- Jonathan M Davis


struct S{@disable this();}
void main(){S s = S.init;}


Re: Syntax highlighting for CodeRunner

2012-02-26 Thread Jacob Carlborg

On 2012-02-26 10:26, Joshua Niehus wrote:

In the off chance that some of you are running a Mac and using
CodeRunner to play around with D, I cooked up the files you need for
CodeRunner to highlight D's syntax:
https://github.com/jniehus/Dlang-for-CodeRunner


I'm using TextMate on Mac with D, it works great.

--
/Jacob Carlborg


Re: struct init() method

2012-02-26 Thread Jonathan M Davis
On Sunday, February 26, 2012 13:15:51 Alex Rønne Petersen wrote:
 On 26-02-2012 12:53, Jonathan M Davis wrote:
  On Sunday, February 26, 2012 12:48:06 Timon Gehr wrote:
  On 02/26/2012 12:18 AM, Jonathan M Davis wrote:
  On Saturday, February 25, 2012 17:07:14 Timon Gehr wrote:
  This is useful:
  
  struct S{
  
 @disable enum init = 0;
  
  }
  
  I thought that the way that you were supposed to do that was
  
  @disable this();
  
  - Jonathan M Davis
  
  struct S{@disable this();}
  void main(){S s = S.init;}
  
  Well, that's a problem then. It looks like it's bug - either that or I
  completely misunderstood what was going on with @disable and structs.
  
  - Jonathan M Davis
 
 IMHO .init should yield an error for structs with @disable this().
 Anything else seems illogical.

Well, like I said, it's my understanding that @disable this() is the way that 
you're supposed to disable .init - as in that's the whole point of having 
added @disable this() to the language (though it can also be used to make 
classes unconstructable). So, either my understanding is wrong and/or this is 
a bug.

- Jonathan M Davis


Re: produced binary is quite big

2012-02-26 Thread Trass3r
You may try -L--gc-sections (in case of gdc in combination with  
-ffunction-sections -fdata-sections) and -L-s


Re: SONAME and D

2012-02-26 Thread bioinfornatics
Le vendredi 24 février 2012 à 13:00 -0800, H. S. Teoh a écrit :
 On Fri, Feb 24, 2012 at 09:35:57PM +0100, Jordi Sayol wrote:
  Al 24/02/12 20:45, En/na H. S. Teoh ha escrit:
 [...]
   I personally think that phobos *should* be a shared object at some
   point, but not everyone agrees with me.
   
  
  I think that phobos should be a shared object too, but only when it
  has some sense (by now, every dmd release breaks previous libraries,
  at least gtkd ones)
 [...]
 
 I suppose D/Phobos is currently still too volatile to start committing
 to shared library version numbers yet. One *could* in theory start doing
 it and just end up with very large version numbers, which the system in
 theory can handle just fine, although people seem aversive to the
 possibility of having /usr/lib/libphobos.so.2.x for all x from 0 to some
 very large number.
 
 
 T
 

ldc build already phobos / druntime as shared lib with
/usr/lib/libphobos.so.2.x
/usr/lib/libphobos.so.2
/usr/lib/libphobos.so.2

and same for druntime



Re: SONAME and D

2012-02-26 Thread bioinfornatics
Le vendredi 24 février 2012 à 15:56 +0100, Mike Wey a écrit :
 On 02/23/2012 08:29 PM, bioinfornatics wrote:
  Le jeudi 23 février 2012 à 19:46 +0100, Mike Wey a écrit :
  On 02/23/2012 05:27 PM, bioinfornatics wrote:
  dear,
  for set soname with:
  - gdc: -Xlinker -soname myLib.so.1
  - ldc2: -soname myLib.so.1
  - dmd: ?
 
  someone know how set soname with dmd ?
 
 
  dmd -L-soname=mylib.so.1
 
 
  Thanks
  i will do so a $(SONAME_FLAG) in my makefile and maybe i will do a pull
  request for gtkd because ldc do not use -L-soname instead of dmd
 
 
 The GtkD Makefile uses:
 
 $(LINKERFLAG)-soname=$@.$(call stripBugfix,$(SO_VERSION))
 
 Witch works with all the compilers, $(LINKERFLAG) is either -L or 
 -Xlinker (with a space on th end).
 

this it is good for dmd and gdc but ldc use -soname without -L
gdc: -Xlinker -soname 
ldc: -soname
dmd: -L -soname


with ldc you do not to do calling the linker with $(LINKERFLAG



A very strange bug. DMD 2.058 64-bit Linux

2012-02-26 Thread Caligo
bug.d
88
@trusted:

import std.datetime : benchmark;
import std.stdio: writefln, writeln;

alias double Real;

void ben(alias fun)(string msg, uint n = 1_000_000) {
  auto b = benchmark!fun(n);
  writefln( %s %s ms, msg, b[0].to!(msecs, int));
}

struct Matrix(int row, int col) {

private:
  alias row Row;
  alias col Col;
  alias Real[Row * Col] Data;

public:
  Data _data = void;
  alias _data this;
  this(const Real[Row*Col] data) pure nothrow { _data = data; }
}

M inverse(M)(const auto ref M m)  {
  writeln(m[]);
  M minv = m;
  return minv;
}

unittest {
  alias Matrix!(4, 4) Matrix4x4;
  auto m9 = Matrix4x4([4, 0, 0, 0, 0, 0, 2, 0, 0, 1, 2, 0, 1, 0, 0, 1]);
  ben!( {auto r = inverse(m9);} )(4x4 inverse:);
}
88

t1.d
88
import std.stdio;

void main(){ }
88

It took me a long time to pinpoint this because it's tricky to trigger the bug.

Once you have those two files, compile with this:

dmd -unittest t1.d bug.d

and then run t1:

./t1

The output you get should look like this:

...
[0, 4, 0, 0, 0, 0, 0, 2, 0, 0, 1, 2, 0, 1, 0, 0]


Obviously the output is wrong.  'm9' for some reason is getting
overwritten.  In my project this caused big problems because there are
other m# with different values, and their values would literally get
copied to m9.  Calling inverse() on m9 then would fail because the
other matrices are not invertible.  Placing a writeln() in inverse()
helped me realize that what was being passed to inverse() was being
modified somewhere.  I'm still now sure how m9 is being modified.

Another point, compiling with this:

dmd -unittest bug.d t1.d

and then running bug:

./bug

doesn't trigger the bug.


Could someone else please confirm this behavior?


Re: A very strange bug. DMD 2.058 64-bit Linux

2012-02-26 Thread Timon Gehr

On 02/26/2012 09:06 PM, Caligo wrote:

Once you have those two files, compile with this:

dmd -unittest t1.d bug.d

and then run t1:

./t1

The output you get should look like this:

...
[0, 4, 0, 0, 0, 0, 0, 2, 0, 0, 1, 2, 0, 1, 0, 0]



I get:

...
[0, 4, 0, 0, 0, 0, 0, 2, 0, 0, 1, 2, 0, 1, 0, 0]



Obviously the output is wrong.  'm9' for some reason is getting
overwritten.  In my project this caused big problems because there are
other m# with different values, and their values would literally get
copied to m9.  Calling inverse() on m9 then would fail because the
other matrices are not invertible.  Placing a writeln() in inverse()
helped me realize that what was being passed to inverse() was being
modified somewhere.  I'm still now sure how m9 is being modified.

Another point, compiling with this:

dmd -unittest bug.d t1.d

and then running bug:

./bug

doesn't trigger the bug.



I get:

...
[4, 0, 0, 0, 0, 0, 2, 0, 0, 1, 2, 0, 1, 0, 0, 1]



Could someone else please confirm this behavior?


I have used DMD 2.058 64-bit linux.


Re: A very strange bug. DMD 2.058 64-bit Linux

2012-02-26 Thread Caligo
Thanks.  I have reported the bug:
http://d.puremagic.com/issues/show_bug.cgi?id=7595


Re: SONAME and D

2012-02-26 Thread Mike Wey

On 02/26/2012 02:53 PM, bioinfornatics wrote:

Le vendredi 24 février 2012 à 15:56 +0100, Mike Wey a écrit :

On 02/23/2012 08:29 PM, bioinfornatics wrote:

Le jeudi 23 février 2012 à 19:46 +0100, Mike Wey a écrit :

On 02/23/2012 05:27 PM, bioinfornatics wrote:

dear,
for set soname with:
- gdc: -Xlinker -soname myLib.so.1
- ldc2: -soname myLib.so.1
- dmd: ?

someone know how set soname with dmd ?



dmd -L-soname=mylib.so.1



Thanks
i will do so a $(SONAME_FLAG) in my makefile and maybe i will do a pull
request for gtkd because ldc do not use -L-soname instead of dmd



The GtkD Makefile uses:

$(LINKERFLAG)-soname=$@.$(call stripBugfix,$(SO_VERSION))

Witch works with all the compilers, $(LINKERFLAG) is either -L or
-Xlinker (with a space on th end).



this it is good for dmd and gdc but ldc use -soname without -L
gdc: -Xlinker -soname
ldc: -soname
dmd: -L -soname


with ldc you do not to do calling the linker with $(LINKERFLAG



Well, i am able to build the GtkD shared libs for ldc2 with the current 
make file.


Not sure why ldc has it's own soname flag as only the linker would care 
about it so using -L to pass it on to the linker works just fine.


--
Mike Wey


Re: Make alias parameter optional?

2012-02-26 Thread Ali Çehreli

On 02/26/2012 03:45 AM, Jacob Carlborg wrote:
 On 2012-02-26 11:03, Ali Çehreli wrote:
 On 02/25/2012 05:04 PM, Robert Rouse wrote:
  On Saturday, 25 February 2012 at 23:10:51 UTC, Ary Manzana wrote:
  On 2/25/12 7:31 PM, Robert Rouse wrote:

 ...

  This means that D can simulate Ruby blocks more than I thought.
 That's
  pretty awesome. I'm loving D more every day.
 
  How's that like a Ruby block?
 
  The D code simulates the following Ruby if you were to make bar print
  something with writeln.
 
  def foo(a, b, block)
  puts a is #{a})
  b.call
  yield
  end
 
  f = lambda { puts good bye }
 
  foo(1, f) { puts something }
 
 
  That's what I'm talking about.
 

 I don't know Ruby but from what I've read so far about Ruby blocks,
 their D equivalents may also be D ranges.

 Ali

 A Ruby block is basically like a delegate in D and has nothing to do
 with ranges.

 Ruby:

 def foo (block)
 block.call
 end

 foo do
 p asd
 end

 D:

 void foo (void delegate () block)
 {
 block();
 }

 void main ()
 {
 foo({
 writeln(asd);
 });

 foo(() = writeln(asd)); // new lambda syntax
 }

 Both examples print asd. If you want to have a more Ruby looking
 syntax in D you do some operator overload abuse:

 struct Block
 {
 void delegate (void delegate ()) impl;

 void opIn (void delegate () block)
 {
 impl(block);
 }
 }

 Block foo ()
 {
 return Block((x) = x());
 }

 void main ()
 {
 foo in {
 writeln(asd);
 };
 }


I see, thanks. The reason I thought about ranges is that the yield 
statement in the Ruby code above reminded me of Python's generators, and 
that D's ranges can also take the role of generators.


Ali



Re: Write struct to file

2012-02-26 Thread Chopin

Hello! OP here :)
I'm gonna be more precise why I want this.
I'm writing my own blog, I will write my own HTTP server etc. and 
the saving of the data myself. Everything will be done in D! Why? 
Learn D. I don't want to hear the reinvent the wheel :(, I think 
you learn a lot by doing thing like this.


So, when I POST a blog-entry, I will read it, and write it to a 
file. I don't want write strings in a file, like:


1--||--Title--||--The entry--||--Date

I have little knowledge about system programming, and writing raw 
files. But my dream was like:


SOME_INT_WITH_LENGTH_OF_STRUCT_TO_THE_RIGHT 
HERE_IS_THE_RAW_STRUCT 
SOME_INT_WITH_LENGTH_OF_STRUCT_TO_THE_RIGHT 
HERE_IS_THE_RAW_STRUCT etc.etc.etc.


Then read those structs in array :)

I thought this was kinda easy in C, but I could be very wrong! So 
I thought it must be super easy in D! I don't have the 
knowledge...


ae.utils.json -- this will be not as cool as writing it raw 
binary? :p

Well. Thank you all for information, tips and tricks!!
Great community! Thanks!


class templates and static if

2012-02-26 Thread Tyler Jameson Little
So, here's my code, as it stands currently:

import std.stdio;

static enum Type {
request,
 response
};

class Parser(Type t) {
static if (t == Type.request) {
 string name = request;
} else {
string name = response;
 }

string message;

this(string message) {
 this.message = message;
}
}

void main() {
 immutable Type t = Type.request;
Parser!t h = new Parser!t(Hello world);

writefln(%s: %s, h.name, h.message);
}

The general goal is to make a templated class that will only include the
parts that each needs. I would like to keep this all as one class template,
because there is a lot of shared code, and I would like to keep it as
seamless as possible and not have to separate out code into separate
functions.

Anyway, my current approach is a little clunky, because it requires an
immutable to be created just to tell the compiler what type of parser I
need.

I was thinking about having two classes, Request and Response, and have
parser take a normal template (class Parser (T)), but I wanted to restrict
it to just those two classes (for now), so I came up with the enum solution.

Is there a better way to do this?

What I'd ultimately like to do is have some static if surrounding blocks of
code that depends on the input to the template.


Re: Write struct to file

2012-02-26 Thread Ali Çehreli

On 02/26/2012 03:22 PM, Chopin wrote:

 I don't want to hear the reinvent the wheel :(, I think you
 learn a lot by doing thing like this.

Agreed.

 So, when I POST a blog-entry, I will read it, and write it to a file. I
 don't want write strings in a file, like:

 1--||--Title--||--The entry--||--Date

I encourage you to stay with human-readable formats unless there is a 
reason not to. You can ditch XML, fine, but consider other formats like 
json. :) But I understand that you want to do this to learn. Fine...


 I have little knowledge about system programming, and writing raw files.
 But my dream was like:

 SOME_INT_WITH_LENGTH_OF_STRUCT_TO_THE_RIGHT HERE_IS_THE_RAW_STRUCT
 SOME_INT_WITH_LENGTH_OF_STRUCT_TO_THE_RIGHT HERE_IS_THE_RAW_STRUCT
 etc.etc.etc.

You can do that only with structs that contain the entire data. As soon 
as there is a reference member, you must dereference that member to grab 
that member's data. For example, when you have strings, slices, 
associative arrays, class variables, pointers, and other user types with 
reference semantics, the data is not within the struct itself.


No matter how long the string is, the following struct is always the 
same size:


struct S
{
char[] s;
}

On the other hand, the following is fine for what you want to do:

struct S
{
char[1000] s;
}

But is 1000 always enough? Is it wasteful (every instance of S will be 
very large.)?


 Then read those structs in array :)

Again, that part is easy as long as the struct doesn't have reference 
members.


 I thought this was kinda easy in C, but I could be very wrong!

C has exactly the same issues. As soon as you have a pointer member you 
must /follow/ that member to reach the actual data.


 So I
 thought it must be super easy in D! I don't have the knowledge...

D is awesome compared to C as it enables serializing/deserializing data 
with its generic programming and compile-time reflection features like this:


  http://dlang.org/traits.html#allMembers

I don't know their details but I would imagine that serialization 
libraries must be written taking advantage of allMembers.


 ae.utils.json -- this will be not as cool as writing it raw binary? :p

Must be subjective. I find json output cooler that binary. :p

Ali



Re: std.socket with GDC

2012-02-26 Thread Mars

On Sunday, 26 February 2012 at 02:01:17 UTC, Andrew Wiley wrote:
I recall having some issues because Winsock needs to be on the 
linker
commandline *after* phobos. Try running with `gdc -v` to see 
what the

linker commandline looks like.


That's it... any way to get it in the correct order, without 
having to call the linker separately? :/


Re: Write struct to file

2012-02-26 Thread Andrej Mitrovic
On 2/27/12, Ali Çehreli acehr...@yahoo.com wrote:
 D is awesome compared to C as it enables serializing/deserializing data
 with its generic programming and compile-time reflection features like this:

http://dlang.org/traits.html#allMembers

ae's json uses .tupleof, and does it in 40(!)* lines of code.

* Well there's a helper function that escapes some strings, but in
essence it's 40 lines of code that do most of the work.

I think I'm going to print this out, frame it, and put it on a wall
because it's just so damn concise:
https://github.com/CyberShadow/ae/blob/master/utils/json.d#L69

(This is Vladimir's code for those not in the know)

   ae.utils.json -- this will be not as cool as writing it raw binary? :p

 Must be subjective. I find json output cooler that binary. :p

Yeah and again if there are no references he can simply use the
cast(void*)[0..len] trick. The challenge would be to implement this
for reference types.


Re: class templates and static if

2012-02-26 Thread Andrej Mitrovic
The immutable is not necessary, use:
auto h = new Parser!(Type.request)(Hello world);

Otherwise, if you really want to declare the type instead of infering
it you can write:
Parser!(Type.request) h = new Parser!(Type.request)(Hello world);

You can also do:
Parser!(Type.request) h;
h = new typeof(h)(Hello world);

or:
alias Parser!(Type.request) ParseRequest;
ParseRequest h = new ParseRequest(Hello world);

Also, 'static' on the enum declaration doesn't really do anything. :)


Re: class templates and static if

2012-02-26 Thread James Miller
 Also, 'static' on the enum declaration doesn't really do anything. :)

but what if the immutable data changes?! :P

But yeah, D's type inference is reasonably good (not as good as
Haskell's, but nothing is as good as Haskell's), so doing what seems
like the best way is normally the right way around it.

If you have a lot of shared code, it might make more sense to just use
traditional subclassing. Assuming that the shared code is all mostly
Parser stuff then subclassing a Parser type into Request and Response
types makes more sense. You can easily add common functionality to the
base class, then add specific functionality to the individual classes.
It also allows for more reflection, which is easier and probably more
powerful than conditional compilation.

--
James Miller


Re: Supporting and signature-checking all foreach variations

2012-02-26 Thread Ashish Myles
On Sun, Feb 26, 2012 at 5:25 AM, Ali Çehreli acehr...@yahoo.com wrote:
 On 02/25/2012 08:25 AM, Ashish Myles wrote:
    However the code above doesn't seem to work and requires me to
    explicitly invoke the slice operator myself like
      foreach(p; C[]) { ... }
    when my data structure clearly defines the following functions.
      Point3[] opSlice() { return _cpts[]; }
      const (Point3)[] opSlice() const { return _cpts[]; }
    Is this a misunderstanding on my part or an unimplemented feature?

 But I've just verified that the following works with dmd 2.058:

 import std.stdio;

 struct Point3
 {}

 struct MyCollection
 {
    Point3[] _cpts;

    Point3[] opSlice() { return _cpts; }  // -- _cpts[] works too

    const (Point3)[] opSlice() const { return _cpts; }
 }

 void main()
 {
    auto coll = MyCollection();

    foreach (i; coll) {
        // ...
    }
 }


Oh good to know. I had downgraded my dmd after some new CTFE bugs
resulted in compilation errors on some of my code, and hadn't thought
to check with the latest version.