www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - specializing template with string variable in CTFE

reply ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
Hello.

the following code is not working:

  template prstr(string s) {
    enum prstr =3D "write("~s.stringof~");\n";
  }

  string buildWriter() (string fmt) {
    return prstr!(fmt[0..$-1]);
  }

  string writer(string fmt) () {
    enum s =3D buildWriter(fmt);
    return s;
  }

  void main () {
    import std.stdio;
    writeln(writer!"str"());
  }

  z40.d(6): Error: variable fmt cannot be read at compile time
  z40.d(10): Error: template instance z40.buildWriter!() error instantiating
  z40.d(16):        instantiated from here: writer!"str"

but why? fmt is known in CTFE and compiler can use it as string literal
for instantiating `prstr`.

please, don't mind the idiocity of the code: this is just a sample to
show what confuses me. i know about possibility of moving `fmt` to
template argument, but i need it as function argument, 'cause
`buildWriter()` actually does string processing and i want to
instantiate `prstr` with the part of the string. and this processing
cannot be done in 'foreach'.

and writing everything in functional style sux: it leads to explosive
growing of the number of arguments passed to templates.
Oct 26 2014
next sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Sunday, 26 October 2014 at 10:48:46 UTC, ketmar via 
Digitalmars-d-learn wrote:
 Hello.

 the following code is not working:

   template prstr(string s) {
     enum prstr = "write("~s.stringof~");\n";
   }

   string buildWriter() (string fmt) {
     return prstr!(fmt[0..$-1]);
   }
Your passing the runtime parameter `fmt` as template argument. Try this: string buildWriter(string fmt)() { return prstr!(fmt[0..$-1]); }
   string writer(string fmt) () {
     enum s = buildWriter(fmt);
and this: enum s = buildWriter!(fmt);
     return s;
   }

   void main () {
     import std.stdio;
     writeln(writer!"str"());
   }

   z40.d(6): Error: variable fmt cannot be read at compile time
   z40.d(10): Error: template instance z40.buildWriter!() error 
 instantiating
   z40.d(16):        instantiated from here: writer!"str"

 but why? fmt is known in CTFE and compiler can use it as string 
 literal
 for instantiating `prstr`.

 please, don't mind the idiocity of the code: this is just a 
 sample to
 show what confuses me. i know about possibility of moving `fmt` 
 to
 template argument, but i need it as function argument, 'cause
 `buildWriter()` actually does string processing and i want to
 instantiate `prstr` with the part of the string. and this 
 processing
 cannot be done in 'foreach'.

 and writing everything in functional style sux: it leads to 
 explosive
 growing of the number of arguments passed to templates.
Oct 26 2014
parent "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Sunday, 26 October 2014 at 12:27:55 UTC, Marc Schütz wrote:
 On Sunday, 26 October 2014 at 10:48:46 UTC, ketmar via 
 Digitalmars-d-learn wrote:
 Hello.

 the following code is not working:

  template prstr(string s) {
    enum prstr = "write("~s.stringof~");\n";
  }

  string buildWriter() (string fmt) {
    return prstr!(fmt[0..$-1]);
  }
Your passing the runtime parameter `fmt` as template argument. Try this: string buildWriter(string fmt)() { return prstr!(fmt[0..$-1]); }
  string writer(string fmt) () {
    enum s = buildWriter(fmt);
and this: enum s = buildWriter!(fmt);
    return s;
  }

  void main () {
    import std.stdio;
    writeln(writer!"str"());
  }

  z40.d(6): Error: variable fmt cannot be read at compile time
  z40.d(10): Error: template instance z40.buildWriter!() error 
 instantiating
  z40.d(16):        instantiated from here: writer!"str"

 but why? fmt is known in CTFE and compiler can use it as 
 string literal
 for instantiating `prstr`.

 please, don't mind the idiocity of the code: this is just a 
 sample to
 show what confuses me. i know about possibility of moving 
 `fmt` to
 template argument, but i need it as function argument, 'cause
 `buildWriter()` actually does string processing and i want to
 instantiate `prstr` with the part of the string. and this 
 processing
 cannot be done in 'foreach'.

 and writing everything in functional style sux: it leads to 
 explosive
 growing of the number of arguments passed to templates.
Sorry, didn't read the rest of your post before replying :-P
Oct 26 2014
prev sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Sunday, 26 October 2014 at 10:48:46 UTC, ketmar via 
Digitalmars-d-learn wrote:
 Hello.

 the following code is not working:

   template prstr(string s) {
     enum prstr = "write("~s.stringof~");\n";
   }

   string buildWriter() (string fmt) {
     return prstr!(fmt[0..$-1]);
   }

   string writer(string fmt) () {
     enum s = buildWriter(fmt);
     return s;
   }

   void main () {
     import std.stdio;
     writeln(writer!"str"());
   }

   z40.d(6): Error: variable fmt cannot be read at compile time
   z40.d(10): Error: template instance z40.buildWriter!() error 
 instantiating
   z40.d(16):        instantiated from here: writer!"str"

 but why? fmt is known in CTFE and compiler can use it as string 
 literal
 for instantiating `prstr`.

 please, don't mind the idiocity of the code: this is just a 
 sample to
 show what confuses me. i know about possibility of moving `fmt` 
 to
 template argument, but i need it as function argument, 'cause
 `buildWriter()` actually does string processing and i want to
 instantiate `prstr` with the part of the string. and this 
 processing
 cannot be done in 'foreach'.

 and writing everything in functional style sux: it leads to 
 explosive
 growing of the number of arguments passed to templates.
Ok, I see two possibilities. The first is to make `prstr` into a normal function. You cannot use `stringof` then, but need to escape the string yourself. Luckily, std.format provides functionality for this already, albeit a bit hidden: private auto escapeStringLiteral(string s) { import std.format : formatElement, FormatSpec; import std.range : appender; auto app = appender!string; FormatSpec!char f; formatElement(app, s, f); return app.data; } The second possibility doesn't exist right now. The core problem is that the compiler needs to be able to generate runtime code for `buildWriter()`, because it's just a function after all. But there have been calls for compile-time only functions, or something along the lines of `static if(__ctfe)` (which doesn't work currently and was recently made an error). In this case, there might be a chance to allow what you want.
Oct 26 2014
next sibling parent reply ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sun, 26 Oct 2014 12:46:06 +0000
via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

 The second possibility doesn't exist right now. The core problem=20
 is that the compiler needs to be able to generate runtime code=20
 for `buildWriter()`, because it's just a function after all.
but it's templated function, and it's never called in runtime, so compiler never needs to generate code for it. seems that semantic analysis prohibits such code before CTFE, and semantic analyser is completely wrong here, 'case `fmt` *can* be read in compile-time. actually, semantic analiser is wrong for any CTFE-able function in this case, and it emits completely wrong error message (compile time variable can't be read in compile time? how this can be true?). i understand that it can be hard to fix semantic analyser though. ah, but this limitation is so... limiting! not sure if it worth the ER though. people seems to not write alot of complex CTFE code anyway.
Oct 26 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Sunday, 26 October 2014 at 19:32:28 UTC, ketmar via 
Digitalmars-d-learn wrote:
 On Sun, 26 Oct 2014 12:46:06 +0000
 via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> 
 wrote:

 The second possibility doesn't exist right now. The core 
 problem is that the compiler needs to be able to generate 
 runtime code for `buildWriter()`, because it's just a function 
 after all.
but it's templated function, and it's never called in runtime, so compiler never needs to generate code for it.
The documentation specifically says that: "Any functions that execute at compile time must also be executable at run time. [...] This means that the semantics of a function cannot depend on compile time values of the function." http://dlang.org/function.html I can imagine it would be difficult to implement it differently, because in effect the compiler would need to create a new instance of the function for every call, with each instance potentially being completely different (consider `static if` over CTFE runtime parameters).
Oct 26 2014
parent ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sun, 26 Oct 2014 20:16:18 +0000
via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

 The documentation specifically says that:
 "Any functions that execute at compile time must also be=20
 executable at run time. [...] This means that the semantics of a=20
 function cannot depend on compile time values of the function."
=20
 http://dlang.org/function.html
=20
 I can imagine it would be difficult to implement it differently,=20
 because in effect the compiler would need to create a new=20
 instance of the function for every call, with each instance=20
 potentially being completely different (consider `static if` over=20
 CTFE runtime parameters).
leaving away technical complexities for compiler writers, i like to say that restrictions for templated functions can be relaxed. maybe by marking some templates/functions as "CTFE-only". or just fix semantic analyser to allow some more things in CTFE. such template will never instantiates successfully for run-time code, and it still can be used in metaprogramming. writing purely functional templates really sux. i made a simple writef-like module which parses it's format string in compile time and i must admit that some templates has 10+ arguments and keep growing as i adding features. either this, or even more convoluted hacks. alas, i'm still don't understand compiler code deep enough to see how hard this will be to implement.
Oct 26 2014
prev sibling parent ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Sun, 26 Oct 2014 12:46:06 +0000
via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

 Ok, I see two possibilities. The first is to make `prstr` into a=20
 normal function. You cannot use `stringof` then, but need to=20
 escape the string yourself. Luckily, std.format provides=20
 functionality for this already, albeit a bit hidden:
=20
      private auto escapeStringLiteral(string s) {
          import std.format : formatElement, FormatSpec;
          import std.range : appender;
=20
          auto app =3D appender!string;
          FormatSpec!char f;
          formatElement(app, s, f);
=20
          return app.data;
      }
thank you, this is very handy. yet my desire to instantiate template with compile-time variable still remains. ;-)
Oct 26 2014