D input: how-to (RFC)

grauzone none at example.net
Sun May 10 22:35:59 PDT 2009


Tyro[a.c.edwards] wrote:
> I am looking for a D version of scanf() but I'm sure there is no such 
> thing so I tried contrived one. I am sure I missed a slew of obvious 

There's readf() in std.stream. I think you have to use std.cstream : din 
to use it with stdin.

> int read(/+File inFile = stdin,+/ A...)(out A a) /+Uncommenting results
>     in: Error: arithmetic/string type expected for value-parameter, not
>     File+/

That would make inFile a template parameter, which obviously doesn't 
make sense with objects, and I guess File is an object. This should work:

int read(A...)(File inFile, out A a)

> {  start:
>    auto data = stripr(readln());
>    auto input = split(data);
> 
>    string assign()
>    {
>        return
>        `  try {
>                       if(a.length != input.length)
>                               return -1;
>                       a[i] = to!(typeof(t))(input[i]);
>               } catch (ConvError e) {
>                     writeln("Invalid input!");
>                     goto start;
>               }`;
>    }

If the user types in too much or too less input, read() returns with an 
error (-1), and the caller has to deal with it.
If the user typed in something unparseable, the function prompts an 
error and lets the user retry.
This is inconsistent.

Also, instead of using CTFE to return a const string, why not simply

const assign = ` .... ` ;

You're using D 2.0, you probably have to replace "const" by "enum". I 
don't know.

You use goto to retry. I'd replace it by a loop. If someone wants to 
argue with me if goto is or is not evil, go ahead.

>    foreach(i, t; a)
>    {
>      static if(is(typeof(t) == void))
>        {}
>      else static if(is(typeof(t) == bool))
>        {}

bools can't be read? If the implementation is incomplete, there should 
be at least an "assert(false);", maybe even a "static assert(false);".

>      else static if(is(typeof(t) == byte))
>        mixin(assign);
>      else static if(is(typeof(t) == ubyte))
>        mixin(assign);
>      else static if(is(typeof(t) == short))
>        mixin(assign);
>      else static if(is(typeof(t) == ushort))
>        mixin(assign);
>      else static if(is(typeof(t) == int))
>        mixin(assign);
>      else static if(is(typeof(t) == uint))
>        mixin(assign);
>      else static if(is(typeof(t) == long))
>        mixin(assign);
>      else static if(is(typeof(t) == ulong))
>        mixin(assign);
>      /+static if(is(typeof(t) == cent))
>        mixin(assign);
>      static if(is(typeof(t) == ucent))
>        mixin(assign);+/
>      else static if(is(typeof(t) == float))
>        mixin(assign);
>      else static if(is(typeof(t) == double))
>        mixin(assign);
>      else static if(is(typeof(t) == real))
>        mixin(assign);

Oh god, what the fuck! String mixins are the new #define! OK, at least 
this reduces the code duplication, but the code duplication shouldn't be 
there in the first place.

You could just throw all the types into a tuple, and then foreach() on 
it, checking for the type in each iteration.

Or just make a single giant if() statement to check for all types to!() 
supports (like if(is(typeof(t) == int) && is(typeof(t) == uint)...). In 
any case, you could reduce the number of is() parts by using isNumeric() 
from std.traits.

Or find a way to check for to!() supported data types automatically (but 
I don't know to!() well enough if this is possible).

>      else static if(is(typeof(t) == ifloat))
>        a[i] = ifloat.max;
>      else static if(is(typeof(t) == idouble))
>        a[i] = idouble.max;
>      else static if(is(typeof(t) == ireal))
>        a[i] = ireal.max;
>      else static if(is(typeof(t) == cfloat))
>        a[i] = cfloat.max;
>      else static if(is(typeof(t) == cdouble))
>        a[i] = cdouble.max;
>      else static if(is(typeof(t) == creal))
>        a[i] = creal.max;

What?

>      else static if(is(typeof(t) == char))
>        a[i] = input[i][0];

input could be an empty string, and random things could happen.

>      else static if(is(typeof(t) == wchar))
>        mixin(assign);
>      else static if(is(typeof(t) == dchar))
>        a[i] = input[i][0];
>      else static if(is(typeof(t) == string))
>        if(a.length > 1)
>          a[i] = input[i];
>        else
>          a[i] = data;

I see, if the caller of the function only requests a char[], the 
splitting by white space is disregarded, and the whole string is 
returned. Isn't that a but inconsistent? At least, it could always 
return the remainder of the string, if this char[] parameter is the last 
of the function.

Also, the array index i isn't checked for input, and random things could 
happen. (You'd get an exception in debug mode, but not in release mode.)

Also, http://en.wikipedia.org/wiki/Dangling_else

>      else static if(is(typeof(t) == dstring))
>        {}
>      else static if(is(typeof(t) == char[]))
>        a[i] = stripr(data).dup;

Shouldn't split() already remove all white space?

>    }
>    return 0;
> }


More information about the Digitalmars-d-learn mailing list