Exceptional coding style

Artur Skawina art.08.09 at gmail.com
Tue Jan 15 04:43:46 PST 2013


On 01/15/13 12:21, bearophile wrote:
> Artur Skawina:
> 
>> Library territory. There's no need to put this in a D compiler.
> 
> One of the threads about possible ways to allow to implement that in library code:
> 
> http://forum.dlang.org/thread/nxhsgwliuwdgidaoudud@forum.dlang.org
> 
> 
>> Except if you'd like to have 'printf("%.2g", d)' instead of 'printf!"%.2g"(d)' syntax.
> 
> In some cases I don't mind a syntax like 'printf!"%.2g"(d)'. But I'd like D to offer ways to remove/avoid template bloat in most of those cases.

You can't both avoid template bloat *and* statically check the arguments.
Statically checking dynamic data is not possible, obviously. But it's not
always a problem, as as long as everything is properly inlined the overhead
can be /less/ than zero. Anyway, a way to do these check right now could be
something like:

   auto printf(string fmt, A...)(A a) /*@inline*/ {
      static bool isFlag(char c) {
         switch (c) {
            case '0':  case '#': case '-': case ' ': case '+':
            case '\'': case 'I': // Non-std.
               return true;
            default: return false;
         }
      }
      static bool checkArgs(B...)(string fmt) {
         if (!__ctfe)
            assert(0, "This function relies on bounds checks happening at CT");
         while (fmt.length && fmt[0]!='%')
            fmt = fmt[1..$];
         if (fmt.length==0)
            return true;
         static if (B.length==0)
            return fmt.length==0;
         else {
            size_t skip = 1;
            // Flags
            while (isFlag(fmt[skip]))
               ++skip;
            // Width
            while (fmt[skip]>='0' && fmt[skip]<='9')
               ++skip;
            // Precision
               // XXX skip \.[0-9]* , \.\*, \.[0-9]+\$ 
            // Length modifier
               // XXX skip them.
            // Conversion spec
            if (
                (fmt[skip]=='d' && is(B[0]==int))
             || (fmt[skip]=='g' && is(B[0]==double))
             || (fmt[skip]=='s' && is(typeof(cast()*B[0])==char))
             /* XXX etc */
                )
               return checkArgs!(B[1..$])(fmt[skip+1..$]);
            assert(0, "printf format error: '" ~ fmt ~ "', remaining args: " ~ B.stringof);
         }
      }

      static assert(checkArgs!A(fmt), "Invalid or unsupported printf args");

      import si = std.stdio, std.string;
      return si.printf(toStringz(fmt), a);
   }

   void main() {
      printf!"Pi == %d.%d != %3d\n"(3, 14, 314);
      printf!"Pi == %d.%d == %g\n"(3, 14, 3.14);
      printf!"Pi == %d.%d == %g == %s\n"(3, 14, 3.14, "3.14".ptr);

      // correctly flagged as invalid:
      //printf!"%"(42);
   }

But ideally the syntax should be

   printf(A...)(static string s, A a)

which would be "lowered" to the equivalent of

   printf(string s, A...)(A a)

and similarly:

   printf(A...)(alias a, A a)

should behave as

   print(alias a, A...)(A a)


Having this work just for templated functions would probably be ok.

artur


More information about the Digitalmars-d mailing list