www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - nested enum like template generator

reply "JS" <js.mdnq gmail.com> writes:
http://dpaste.dzfl.pl/7c8b0ba9

Why the heck can't we use integers in ctfe's? There seems to be 
no simple way to create a counter and this is one of the most 
basic programming constructs to use.. yet with ctfe's it's 
impossible.

I'd like each variable in the nested structs to be incremented 
properly.
Jul 15 2013
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/15/2013 08:43 PM, JS wrote:

 http://dpaste.dzfl.pl/7c8b0ba9

 Why the heck can't we use integers in ctfe's? There seems to be no
 simple way to create a counter and this is one of the most basic
 programming constructs to use.. yet with ctfe's it's impossible.

 I'd like each variable in the nested structs to be incremented properly.
I did not read the code but just from your description, the separate compilation model that D uses would preclude that. For example, compilation of a.d would not know anything about the counter that b.d has counted during its compilation. Ali
Jul 15 2013
parent reply "JS" <js.mdnq gmail.com> writes:
On Tuesday, 16 July 2013 at 04:37:33 UTC, Ali Çehreli wrote:
 On 07/15/2013 08:43 PM, JS wrote:

 http://dpaste.dzfl.pl/7c8b0ba9

 Why the heck can't we use integers in ctfe's? There seems to
be no
 simple way to create a counter and this is one of the most
basic
 programming constructs to use.. yet with ctfe's it's
impossible.
 I'd like each variable in the nested structs to be
incremented properly. I did not read the code but just from your description, the separate compilation model that D uses would preclude that. For example, compilation of a.d would not know anything about the counter that b.d has counted during its compilation. Ali
Huh? what is a.d and b.d? The template transforms a string into a D code string... which is then string mixed in. The counter and code generation have nothing to do with the result. A counter is needed in the generation of the code to generate enum like characteristics. It does use recursion, and I could pass a variable that counts the number of elements but this too would probably not work due to D bitching about using ints. I imagine one could hack D to make it's flawed CTFE system work, to some degree... like using strings: template inc(string s) { string _(string a) { if (a == "0") return "1"; if (a == "1") return "2"; if (a == "2") return "3"; if (a == "3") return "4"; return "0"; } enum inc = _(s); pragma(msg, ":"~_(s)); } which is a modulo 4 incrementer. So, CTFE's have the ability to count... but extremely short sighted that they don't. I imagine can write a whole template library using strings to emulate ints in ctfe's... what a shame though...
Jul 15 2013
next sibling parent "Dicebot" <public dicebot.lv> writes:
On Tuesday, 16 July 2013 at 05:51:53 UTC, JS wrote:
 ...
Once again you post a complex and messy snippet and than jump to wrong conclusions. This works: template inc(int i) { enum inc = i + 1; } pragma(msg, inc!3); Integers are treated normally in CTFE/templates, contrary to your last statement. You need to reduce to the real problem.
Jul 16 2013
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/15/2013 10:51 PM, JS wrote:

 On Tuesday, 16 July 2013 at 04:37:33 UTC, Ali Çehreli wrote:
 On 07/15/2013 08:43 PM, JS wrote:

 http://dpaste.dzfl.pl/7c8b0ba9

 Why the heck can't we use integers in ctfe's? There seems to
be no
 simple way to create a counter and this is one of the most
basic
 programming constructs to use.. yet with ctfe's it's
impossible.
 I'd like each variable in the nested structs to be
incremented properly. I did not read the code but just from your description, the separate compilation model that D uses would preclude that. For example, compilation of a.d would not know anything about the counter that b.d has counted during its compilation. Ali
Huh? what is a.d and b.d?
Yeah, I should have read your code before writing that. My comment is for the general case where a.d and b.d are two separte modules that are compiler separately. I thought that you wanted a counter to continue counting between modules.
 The template transforms a string into a D code string... which is then
 string mixed in. The counter and code generation have nothing to do with
 the result. A counter is needed in the generation of the code to
 generate enum like characteristics.

 It does use recursion, and I could pass a variable that counts the
 number of elements but this too would probably not work due to D
 bitching about using ints.
I must still be in the dark but I seriously doubt that you would have problems incrementing an int in D.
 I imagine one could hack D to make it's flawed CTFE system work, to some
 degree... like using strings:


 template inc(string s)
 {
      string _(string a)
      {
          if (a == "0") return "1";
          if (a == "1") return "2";
          if (a == "2") return "3";
          if (a == "3") return "4";
          return "0";
      }
      enum inc = _(s); pragma(msg, ":"~_(s));
 }

 which is a modulo 4 incrementer. So, CTFE's have the ability to count...
 but extremely short sighted that they don't. I imagine can write a whole
 template library using strings to emulate ints in ctfe's... what a shame
 though...
That would be extreme. Ali
Jul 16 2013
parent reply "JS" <js.mdnq gmail.com> writes:
On Tuesday, 16 July 2013 at 14:05:38 UTC, Ali Çehreli wrote:
 On 07/15/2013 10:51 PM, JS wrote:

 On Tuesday, 16 July 2013 at 04:37:33 UTC, Ali Çehreli wrote:
 On 07/15/2013 08:43 PM, JS wrote:

 http://dpaste.dzfl.pl/7c8b0ba9

 Why the heck can't we use integers in ctfe's? There seems
to
 be no
 simple way to create a counter and this is one of the most
basic
 programming constructs to use.. yet with ctfe's it's
impossible.
 I'd like each variable in the nested structs to be
incremented properly. I did not read the code but just from your description, the
separate
 compilation model that D uses would preclude that. For
example,
 compilation of a.d would not know anything about the counter
that b.d
 has counted during its compilation.

 Ali
Huh? what is a.d and b.d?
Yeah, I should have read your code before writing that. My comment is for the general case where a.d and b.d are two separte modules that are compiler separately. I thought that you wanted a counter to continue counting between modules.
That wold be nice but require external storage to an ctfe which is a limitation of ctfe's. Ok, at least this post has helped me solve my problem. I guess the issue was the error message given. The problem is I can't declare my "global" int variables directly inside the template. This does make it hard to use the same variable across multiple functions... template A { int c; // makes c near useless, can't use it like a normal it... } solution, one has to use nested functions: template A { string templateScope() { int c; // c now acts like a normal global int value to all nested functions. } } I think I understand why this works and is done this way but the error message given obfuscates the reason. Unfortunately it requires a messy technique to get around by using nested functions. (The parent function holds the global state of the child functions) It would be nice if we had some way to data globally(in module). e.g., __ctfestore["name"] = value; I understand this wouldn't probably work well outside the module since compilation order may be not be consistent. (but maybe __ctfeVolitileStore could be used for cross module storage)
Jul 16 2013
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/16/2013 01:40 PM, JS wrote:

 The problem is I can't declare my "global" int variables directly inside
 the template. This does make it hard to use the same variable across
 multiple functions...

 template A
 {
      int c; // makes c near useless, can't use it like a normal it...
 }
Could you please provide complete code. The following works: import std.stdio; import std.conv; template A() { int c; } string func(T)() { string result; alias counter = A!().c; for ( ; counter < 10; ++counter) { result ~= counter.to!string; } return result; } void main() { writeln(func!int()); } Prints: 0123456789 This works as well: import std.stdio; import std.conv; template A() { int c; string func() { string result; for ( ; c < 10; ++c) { result ~= c.to!string; } return result; } } void main() { writeln(A!().func()); } Prints the same: 0123456789
 the error message given obfuscates the reason.
What is the error message?
 Unfortunately it requires a messy technique to get around by using nested
 functions. (The parent function holds the global state of the child
 functions)
Well, this works as well: import std.stdio; import std.conv; template A() { int c; string globalFunc() { string result; void func() { for ( ; c < 10; ++c) { result ~= c.to!string; } } func(); return result; } } void main() { writeln(A!().globalFunc()); } Again, prints the same: 0123456789
 It would be nice if we had some way to data globally(in module).

 e.g., __ctfestore["name"] = value;
I would expect model-level objects start their lives after the program starts running but their initial value can be calculated during compile time: import std.stdio; import std.conv; int[string] ctfestore; static this() { ctfestore = A!().globalFunc(); } template A() { int c; int[string] globalFunc() { int[string] result; void func() { for ( ; c < 10; ++c) { result[c.to!string] = c; } } func(); return result; } } void main() { writeln(ctfestore); } Prints: ["0":0, "4":4, "8":8, "1":1, "5":5, "9":9, "2":2, "6":6, "3":3, "7":7] Ali
Jul 16 2013
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/16/2013 02:01 PM, Ali Çehreli wrote:

 On 07/16/2013 01:40 PM, JS wrote:
  > It would be nice if we had some way to data globally(in module).
  >
  > e.g., __ctfestore["name"] = value;

 I would expect model-level objects start their lives after the program
 starts running but their initial value can be calculated during compile
 time:

 import std.stdio;
 import std.conv;

 int[string] ctfestore;

 static this()
 {
      ctfestore = A!().globalFunc();
 }
Ok, I've been silly. That's not CTFE. I meant something like this: static this() { enum initialValue = A!().globalFunc(); ctfestore = initialValue; } And only then I got the problem:
 template A()
 {
      int c;

      int[string] globalFunc()
      {
          int[string] result;

          void func()
          {
              for ( ; c < 10; ++c) {
Error: static variable c cannot be read at compile time called from here: func() called from here: globalFunc()
                  result[c.to!string] = c;
              }
          }

          func();
          return result;
      }
 }

 void main()
 {
      writeln(ctfestore);
 }

 Prints:

 ["0":0, "4":4, "8":8, "1":1, "5":5, "9":9, "2":2, "6":6, "3":3, "7":7]
Ali
Jul 16 2013
parent "JS" <js.mdnq gmail.com> writes:
On Tuesday, 16 July 2013 at 21:12:36 UTC, Ali Çehreli wrote:
 On 07/16/2013 02:01 PM, Ali Çehreli wrote:

 On 07/16/2013 01:40 PM, JS wrote:
  > It would be nice if we had some way to data globally(in
module).
  >
  > e.g., __ctfestore["name"] = value;

 I would expect model-level objects start their lives after
the program
 starts running but their initial value can be calculated
during compile
 time:

 import std.stdio;
 import std.conv;

 int[string] ctfestore;

 static this()
 {
      ctfestore = A!().globalFunc();
 }
Ok, I've been silly. That's not CTFE. I meant something like this: static this() { enum initialValue = A!().globalFunc(); ctfestore = initialValue; } And only then I got the problem:
 template A()
 {
      int c;

      int[string] globalFunc()
      {
          int[string] result;

          void func()
          {
              for ( ; c < 10; ++c) {
Error: static variable c cannot be read at compile time called from here: func() called from here: globalFunc()
                  result[c.to!string] = c;
              }
          }

          func();
          return result;
      }
 }

 void main()
 {
      writeln(ctfestore);
 }

 Prints:

 ["0":0, "4":4, "8":8, "1":1, "5":5, "9":9, "2":2, "6":6,
"3":3, "7":7] Ali
yes, that error is the bitch that D loves to slap me with constantly when using ctfe's and I have to use wierd methods to get around it. Note that I am mainly talking about string mixins but the issue is that c is a template variable which is a compile time construct that has no real meaning inside a ctfe(if that makes sense). This is why nested functions have to be used... but then if you want global variables(cross-template variables) you need some other technique, if it's even possible. template A() { int c; // doesn't create a variable c for functions in the template to use but tells the template define a variable when used, I guess, as a normal template(not a mixin). That is, I was initially thinking `int c;` created a compile time variable inside the template but it doesn't... c does't even exist until the template is used, but by then, it's too late... specially if the template is used as a string mixin. I think we would need somethign like template A() { template int c; // or possibly internal int c; which defines c as a template variable(not a symbolic expression inserted into code where A is used. here is code that makes it clear: module main; import std.stdio, std.cstream, std.conv; template A() { int c; int foo() { return ++c; } enum A = foo(); // Comment out to change A to a standard template } void main(string[] argv) { alias A!() a; //writeln(a.c, a.foo()); writeln(a); } Note that A is used two different ways. A as a sort of function itself(with return foo() and, if that line is commented out, as a sort of container holding an int and a function. I think this is the confusion that I had not realizing they are two different beasts. When A is acting as a container, foo can use c no problem. When A is acting as a ctfe, foo can't use c. I'm not sure if this is a flaw, bug, or what... I don't see any reason why it can't work both ways, and nesting the template as a function works to solve the compile time error. I think both concepts can be unified by having the compiler implicitly wrap everything inside the template in a function, accept assignments to A, when used as a ctfe. (this may not work well though but a start in the right direction)
Jul 16 2013
prev sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/15/2013 08:43 PM, JS wrote:

 http://dpaste.dzfl.pl/7c8b0ba9

 Why the heck can't we use integers in ctfe's?
That is false: import std.range; import std.algorithm; import std.conv; import std.stdio; string makeCode(int begin, int end) { auto result = appender!string(`enum made = "`); iota(begin, end) .map!(a => a.to!string) .joiner(", ") .copy(result); result ~= `";`; return result.data; } unittest { assert(makeCode(3, 7) == `enum made = "3, 4, 5, 6";`); } void main() { mixin (makeCode(-3, 3)); writeln(made); }
 There seems to be no
 simple way to create a counter and this is one of the most basic
 programming constructs to use..
That would be pretty limiting, right?
 yet with ctfe's it's impossible.
I have a feeling that you had something else in mind.
 I'd like each variable in the nested structs to be incremented properly.
It should be simple to convert the code above to do that. Ali
Jul 16 2013