Re: Compile-time variables

2018-04-06 Thread Kayomn via Digitalmars-d-learn

On Friday, 6 April 2018 at 14:15:08 UTC, Kayomn wrote:

On Friday, 6 April 2018 at 13:55:55 UTC, nkm1 wrote:

[...]


Figured I had a handle on how it worked doing but guess not. 
One final question, however.


[...]


Nevermind, I'm blind. I missed the post-increment in newID().


Re: Compile-time variables

2018-04-06 Thread Kayomn via Digitalmars-d-learn

On Friday, 6 April 2018 at 13:55:55 UTC, nkm1 wrote:

On Friday, 6 April 2018 at 13:10:23 UTC, Kayomn wrote:
ID tags are unique and spsecific to the class type. There 
shouldn't be more than one type ID assigned to one class type.


The idea behind what it is I am doing is I am implementing a 
solution to getting a type index, similar to 
std.variant.Variant.type(). The way that I implemented this in 
C++ is like so:



inline unsigned int getNodeTypeID() {
static unsigned int lastID = 0;

return lastID++;
}

template inline unsigned int getNodeTypeID() {
static unsigned int typeID = getNodeTypeID();

return typeID;
}


In this C++ example I am exploiting the fact that templates 
are generated at compile-time to execute getNodeTypeID for 
each new type instance generated. After initial type 
generation, whenever they are called at runtime they were 
return the ID assigned to that function template instance that 
was generated at compile-time.


It's pretty simple, and to be honest I'm surprised this has 
been causing me such a headache implementing it in D.


That's because the C++ code doesn't do what you think it does. 
Apparently you think that getNodeID() is executed at compile 
time. This is not the case, which you can verify by adding 
"constexpr" to it:


$ g++ -std=c++14 -Wall -Wextra -c -o example example.cpp
main.cpp: In function ‘constexpr unsigned int getNodeTypeID()’:
main.cpp:2:25: error: ‘lastID’ declared ‘static’ in ‘constexpr’ 
function

 static unsigned int lastID = 0;

In fact, you're "exploiting" the fact that static variables in 
C++ can be initialized at runtime (which is probably not what 
you want).

The equivalent D code is:

uint newID()
{
static uint lastID;

return lastID++;
}

uint getNodeID(T)()
{
static bool inited;
static uint id;

if (!inited)
{
id = newID();
inited = true;
}

return id;
}

(yes, C++ does insert some hidden bool that tells it whether 
the variable was initialized or not).
Naturally, you can't use that for constructing switches or 
other compile time constructs.


Figured I had a handle on how it worked doing but guess not. One 
final question, however.


I've implemented this test bed with your example to what I think 
is your exact implementation, but it seems to be giving 
unexpected output.



import std.stdio;

uint newID() {
static uint lastID;

return lastID;
}

uint getNodeID(T)() {
static bool inited;
static uint id;

if (!inited) {
id = newID();
inited = true;
}
return id;
}

class Node {}

class Sprite {}

class Camera {}

void main() {
// Test 01.
writeln("Test 01.");

writeln(getNodeID!(Node)(),'\t',getNodeID!(Sprite)(),'\t',getNodeID!(Camera)());
// Test 02.
writeln("Test 02.");

writeln(getNodeID!(Node)(),'\t',getNodeID!(Sprite)(),'\t',getNodeID!(Camera)());
}


Output:

Performing "debug" build using gdc for x86_64.
dlangtest ~master: building configuration "application"...
Running ./dlangtest
Test 01.
0   0   0
Test 02.
0   0   0


Have I misunderstood something?


Re: Compile-time variables

2018-04-06 Thread nkm1 via Digitalmars-d-learn

On Friday, 6 April 2018 at 13:10:23 UTC, Kayomn wrote:
ID tags are unique and spsecific to the class type. There 
shouldn't be more than one type ID assigned to one class type.


The idea behind what it is I am doing is I am implementing a 
solution to getting a type index, similar to 
std.variant.Variant.type(). The way that I implemented this in 
C++ is like so:



inline unsigned int getNodeTypeID() {
static unsigned int lastID = 0;

return lastID++;
}

template inline unsigned int getNodeTypeID() {
static unsigned int typeID = getNodeTypeID();

return typeID;
}


In this C++ example I am exploiting the fact that templates are 
generated at compile-time to execute getNodeTypeID for each new 
type instance generated. After initial type generation, 
whenever they are called at runtime they were return the ID 
assigned to that function template instance that was generated 
at compile-time.


It's pretty simple, and to be honest I'm surprised this has 
been causing me such a headache implementing it in D.


That's because the C++ code doesn't do what you think it does. 
Apparently you think that getNodeID() is executed at compile 
time. This is not the case, which you can verify by adding 
"constexpr" to it:


$ g++ -std=c++14 -Wall -Wextra -c -o example example.cpp
main.cpp: In function ‘constexpr unsigned int getNodeTypeID()’:
main.cpp:2:25: error: ‘lastID’ declared ‘static’ in ‘constexpr’ 
function

 static unsigned int lastID = 0;

In fact, you're "exploiting" the fact that static variables in 
C++ can be initialized at runtime (which is probably not what you 
want).

The equivalent D code is:

uint newID()
{
static uint lastID;

return lastID++;
}

uint getNodeID(T)()
{
static bool inited;
static uint id;

if (!inited)
{
id = newID();
inited = true;
}

return id;
}

(yes, C++ does insert some hidden bool that tells it whether the 
variable was initialized or not).
Naturally, you can't use that for constructing switches or other 
compile time constructs.


Re: Compile-time variables

2018-04-06 Thread Kayomn via Digitalmars-d-learn


Besides this, I tried something with types used as user defined 
attributes.

https://dlang.org/spec/attribute.html#uda

Automatic compile time tagging is not my speciality, however, I 
think is also achievable with mixins somehow?

But I don't know how to workaround the bug
https://issues.dlang.org/show_bug.cgi?id=18718
at this moment...

https://run.dlang.io/is/DmBhO5


Does the default case handle an unspecified class or does it 
handle a class which is specified, but is not mentioned in any 
of previous cases?


So in this example code the switch table is being used for 
loading data serialized into text. If the class cannot determine 
the node ID or it uses the default node type ID (e.g. the Node 
type if super "Node") it will create a simple node, as you can 
always be sure no matter what the type there will be sufficient 
information stored in the data to construct a default Node.


Another information shortage is: are the tags exclusive or not? 
So, is it possible that a class has more then one tag (still 
being unique (tuple))?


ID tags are unique and spsecific to the class type. There 
shouldn't be more than one type ID assigned to one class type.


The idea behind what it is I am doing is I am implementing a 
solution to getting a type index, similar to 
std.variant.Variant.type(). The way that I implemented this in 
C++ is like so:



inline unsigned int getNodeTypeID() {
static unsigned int lastID = 0;

return lastID++;
}

template inline unsigned int getNodeTypeID() {
static unsigned int typeID = getNodeTypeID();

return typeID;
}


In this C++ example I am exploiting the fact that templates are 
generated at compile-time to execute getNodeTypeID for each new 
type instance generated. After initial type generation, whenever 
they are called at runtime they were return the ID assigned to 
that function template instance that was generated at 
compile-time.


It's pretty simple, and to be honest I'm surprised this has been 
causing me such a headache implementing it in D.


Re: Compile-time variables

2018-04-06 Thread aliak via Digitalmars-d-learn

On Friday, 6 April 2018 at 02:20:29 UTC, Kayomn wrote:


Wrong example code, here's the correct example:

switch (queryString(childJson,"type")) {
case (typeof (Sprite).name):
child = this.addChild!(Sprite)(childName);

break;

case (typeof (Camera).name):
child = this.addChild!(Camera)(childName);

break;

default:
child = this.addChild!(Node)(childName);

break;
}



Hi, could you show a bit more implementation details in the C++ 
version that works? Maybe someone can translate that to the 
appropriate D version?


The problem seems to be that switching on a runtime value and 
pattern matching with a compile time value is hard to pull off 
automatically without some sort of bridge (or of course I've 
misunderstood this exercise)


So if you had:

class R {
  string type() { return R.stringof; }
}
class A: R {
  override string type() { return A.stringof; }
}
class B: R {
  override string type() { return B.stringof; }
}

string type(T: R)() {
return T.stringof;
}

void main() {
  R node = new A;
  final switch (node.type) {
  case type!R: writeln("R"); break;
  case type!A: writeln("A"); break;
  case type!B: writeln("B"); break;
  }
}

(maybe not a good idea to use stringof though, typeid probably 
better)


Cheers


Re: Compile-time variables

2018-04-06 Thread Alex via Digitalmars-d-learn

On Friday, 6 April 2018 at 02:20:29 UTC, Kayomn wrote:


Wrong example code, here's the correct example:

switch (queryString(childJson,"type")) {
case (typeof (Sprite).name):
child = this.addChild!(Sprite)(childName);

break;

case (typeof (Camera).name):
child = this.addChild!(Camera)(childName);

break;

default:
child = this.addChild!(Node)(childName);

break;
}



Ok, first of all, I'm not sure about the question, because of the 
following:


Does the default case handle an unspecified class or does it 
handle a class which is specified, but is not mentioned in any of 
previous cases?

Or does the default case handle both of these circumstances?
Without a master list this play an important role, I think.

Another information shortage is: are the tags exclusive or not? 
So, is it possible that a class has more then one tag (still 
being unique (tuple))?


Besides this, I tried something with types used as user defined 
attributes.

https://dlang.org/spec/attribute.html#uda

Automatic compile time tagging is not my speciality, however, I 
think is also achievable with mixins somehow?

But I don't know how to workaround the bug
https://issues.dlang.org/show_bug.cgi?id=18718
at this moment...

https://run.dlang.io/is/DmBhO5


Re: Compile-time variables

2018-04-05 Thread Kayomn via Digitalmars-d-learn

On Friday, 6 April 2018 at 02:18:28 UTC, Kayomn wrote:

On Friday, 6 April 2018 at 01:22:42 UTC, Kayomn wrote:

On Friday, 6 April 2018 at 01:14:37 UTC, ketmar wrote:

Kayomn wrote:


[...]


it is already done for you, free of charge.

class Node {}
class RootNode : Node {}
class SpriteNode : Node {}

void main () {
auto nodeId1 = typeid(Node);
auto nodeId2 = typeid(SpriteNode);
auto comparison = typeid(Node);

Node n = new SpriteNode();

assert(typeid(Node) is typeid(Node)); // obviously
assert(typeid(SpriteNode) !is typeid(Node)); // sure
assert(typeid(Node) is nodeId1);
assert(typeid(n) is nodeId2);
}


Oh neat, thanks. I've been really scraping my head over this, 
and was worried I wouldn't be able to keep supporting my D 
version of the project.


Hmm... doesn't seem to operate at compile-time, which is an 
issue if I want to use it in case switch table like I was going 
to:



switch (queryInteger(childJson,"type")) {
case NodeType.Sprite:
child = this.addChild!(Sprite)(childName);

break;

default:
child = this.addChild!(Node)(childName);

break;
}



Wrong example code, here's the correct example:

switch (queryString(childJson,"type")) {
case (typeof (Sprite).name):
child = this.addChild!(Sprite)(childName);

break;

case (typeof (Camera).name):
child = this.addChild!(Camera)(childName);

break;

default:
child = this.addChild!(Node)(childName);

break;
}



Re: Compile-time variables

2018-04-05 Thread Kayomn via Digitalmars-d-learn

On Friday, 6 April 2018 at 01:22:42 UTC, Kayomn wrote:

On Friday, 6 April 2018 at 01:14:37 UTC, ketmar wrote:

Kayomn wrote:


[...]


it is already done for you, free of charge.

class Node {}
class RootNode : Node {}
class SpriteNode : Node {}

void main () {
auto nodeId1 = typeid(Node);
auto nodeId2 = typeid(SpriteNode);
auto comparison = typeid(Node);

Node n = new SpriteNode();

assert(typeid(Node) is typeid(Node)); // obviously
assert(typeid(SpriteNode) !is typeid(Node)); // sure
assert(typeid(Node) is nodeId1);
assert(typeid(n) is nodeId2);
}


Oh neat, thanks. I've been really scraping my head over this, 
and was worried I wouldn't be able to keep supporting my D 
version of the project.


Hmm... doesn't seem to operate at compile-time, which is an issue 
if I want to use it in case switch table like I was going to:



switch (queryInteger(childJson,"type")) {
case NodeType.Sprite:
child = this.addChild!(Sprite)(childName);

break;

default:
child = this.addChild!(Node)(childName);

break;
}



Re: Compile-time variables

2018-04-05 Thread Kayomn via Digitalmars-d-learn

On Friday, 6 April 2018 at 01:14:37 UTC, ketmar wrote:

Kayomn wrote:


[...]


it is already done for you, free of charge.

class Node {}
class RootNode : Node {}
class SpriteNode : Node {}

void main () {
auto nodeId1 = typeid(Node);
auto nodeId2 = typeid(SpriteNode);
auto comparison = typeid(Node);

Node n = new SpriteNode();

assert(typeid(Node) is typeid(Node)); // obviously
assert(typeid(SpriteNode) !is typeid(Node)); // sure
assert(typeid(Node) is nodeId1);
assert(typeid(n) is nodeId2);
}


Oh neat, thanks. I've been really scraping my head over this, and 
was worried I wouldn't be able to keep supporting my D version of 
the project.


Re: Compile-time variables

2018-04-05 Thread Jonathan M Davis via Digitalmars-d-learn
On Friday, April 06, 2018 00:35:39 Kayomn via Digitalmars-d-learn wrote:
> On Friday, 6 April 2018 at 00:21:54 UTC, H. S. Teoh wrote:
> > On Thu, Apr 05, 2018 at 11:53:00PM +, Kayomn via
> > Digitalmars-d-learn wrote: [...]
> >
> >> [...]
> >
> > [...]
> >
> >> [...]
> >
> > `lastID`, as declared above, are runtime variables. The
> > 'static' in this case just means it's thread-local, rather than
> > allocated on the stack. You cannot modify these variables at
> > compile-time.
> >
> >> [...]
> >
> > You appear to be wanting to increment a global variable during
> > compile-time. Unfortunately, there is no such thing as a
> > compile-time global variable.  You will have to find some other
> > way to implement what you want.
> >
> > One way to do this would be to use compile-time introspection
> > to construct a list of nodes, and then use a CTFE function or
> >
> > static foreach to generate node IDs all at once.  For example:
> > string generateEnum(T...)()
> > {
> >
> > if (__ctfe) { // only run at compile-time
> >
> > string code = "enum NodeIds {";
> > foreach (ident; T) {
> >
> > code ~= ident ~ ", ";
> >
> > }
> > code ~= "}";
> > return code;
> >
> > }
> > else assert(0);
> >
> > }
> >
> > alias MyNodes = List!(
> >
> > // Just an example; you probably want to generate this
> > // list via introspection, e.g. via __traits(getMembers)
> > // or something like that.
> > identifier1,
> > identifier2,
> > ...
> >
> > );
> >
> > mixin(generateEnum!MyNodes); // defines `enum NodeIds`
> >
> > static assert(NodeIds.identifier1 == 0);
> > static assert(NodeIds.identifier2 == 1);
> > ...
> >
> > There are probably other ways to do it too, but I chose enum
> > because it naturally assigns incrementing IDs to its members,
> > so it's a convenient construct for this purpose.
> >
> >
> > T
>
> I think I didn't explain well enough, I'm not trying to generate
> an enum from a list of pre-defined known quantities. The idea is
> that a compile-time generic function exists and it generates a
> unique identifier for that node.
>
> My reasons for doing this is to remove the need to keep a master
> enum that holds an identifying value for each node. I've
> implemented this same thing in C++ before and it was extremely
> straightforward.

If you want to generate a value using CTFE, then you can use a normal
function that simply takes arguments and returns a value, and you can use
enums that already have a value. So, you can do something like

enum foo = "string";
enum bar = baz("2");

string baz(string str)
{
return foo ~ str;
}

void main()
{
assert(foo == "string");
assert(bar == "string2");
}

but you can't use any variables other than local variables unless they're
const or immutable. So, you can't do something like increment a global
variable. It's is absolutely impossible to do something like

enum id = getID();

and have getID return a different value on each call. So, if you're looking
to do something like

enum value1 = getID();
enum value2 = getID();
enum value3 = getID();
enum value4 = getID();

and have each of those enums have a unique ID, then you are completely out
of luck. You'll need go about solving the problem in a different way, be it
by passing different values to the function being used so that it can
generate different values on each call:

enum value1 = getID(1);
enum value2 = getID(2);
enum value3 = getID(3);
enum value4 = getID(4);

or by generating a string to mix in as code as H. S. Teoh suggested.
Eponymous templates or template mixins could also be used, but they don't
really do anything more, just differently, and usually less efficiently.

Basically, even though functions used during CTFE don't have to be marked
with pure, they effectively have to be pure in order to work. So, if you try
to initialize an enum with the result of a function call, the only way you
have to alter the output is to pass in different arguments. If you need any
kind of state to be maintained in order to initialize multiple enums with
values that relate to one another (e.g. an incrementing ID), then a mixin is
your only choice.

If you can initialize what you need to initialize at runtime (meaning that
it can't be an enum), then you have more options, but at compile time,
initializations have to be pure.

- Jonathan M Davis



Re: Compile-time variables

2018-04-05 Thread ketmar via Digitalmars-d-learn

Kayomn wrote:


I'll give a better example of what it is I'm trying to do.

These are node types. Their contents are not important in this 
explanation, only that they operate as a tree structure.


class Node;

class RootNode : Node;

class SpriteNode : Node;

The result of getNodeID on a specific type is always the same. A value 
representing it that is applied during compilation. The value does not 
matter to the programmer, only that it is unique and gets applied.


--
uint nodeId1 = getNodeID!(Node)(); // 0.

uint nodeId2 = getNodeID!(SpriteNode)(); // 1.

uint comparison = getNodeID!(Node)(); // 0.

// True.
if (getNodeID!(Node)() == getNodeID!(Node)()) {

}

// False.
if (getNodeID!(SpriteNode)() == getNodeID!(Node)()) {

}
--


it is already done for you, free of charge.

class Node {}
class RootNode : Node {}
class SpriteNode : Node {}

void main () {
auto nodeId1 = typeid(Node);
auto nodeId2 = typeid(SpriteNode);
auto comparison = typeid(Node);

Node n = new SpriteNode();

assert(typeid(Node) is typeid(Node)); // obviously
assert(typeid(SpriteNode) !is typeid(Node)); // sure
assert(typeid(Node) is nodeId1);
assert(typeid(n) is nodeId2);
}


Re: Compile-time variables

2018-04-05 Thread Kayomn via Digitalmars-d-learn

I'll give a better example of what it is I'm trying to do.

These are node types. Their contents are not important in this 
explanation, only that they operate as a tree structure.


class Node;

class RootNode : Node;

class SpriteNode : Node;

The result of getNodeID on a specific type is always the same. A 
value representing it that is applied during compilation. The 
value does not matter to the programmer, only that it is unique 
and gets applied.


--
uint nodeId1 = getNodeID!(Node)(); // 0.

uint nodeId2 = getNodeID!(SpriteNode)(); // 1.

uint comparison = getNodeID!(Node)(); // 0.

// True.
if (getNodeID!(Node)() == getNodeID!(Node)()) {

}

// False.
if (getNodeID!(SpriteNode)() == getNodeID!(Node)()) {

}
--


Re: Compile-time variables

2018-04-05 Thread Kayomn via Digitalmars-d-learn

On Friday, 6 April 2018 at 00:21:54 UTC, H. S. Teoh wrote:
On Thu, Apr 05, 2018 at 11:53:00PM +, Kayomn via 
Digitalmars-d-learn wrote: [...]

[...]

[...]

[...]


`lastID`, as declared above, are runtime variables. The 
'static' in this case just means it's thread-local, rather than 
allocated on the stack. You cannot modify these variables at 
compile-time.




[...]


You appear to be wanting to increment a global variable during 
compile-time. Unfortunately, there is no such thing as a 
compile-time global variable.  You will have to find some other 
way to implement what you want.


One way to do this would be to use compile-time introspection 
to construct a list of nodes, and then use a CTFE function or 
static foreach to generate node IDs all at once.  For example:


string generateEnum(T...)()
{
if (__ctfe) { // only run at compile-time
string code = "enum NodeIds {";
foreach (ident; T) {
code ~= ident ~ ", ";
}
code ~= "}";
return code;
}
else assert(0);
}

alias MyNodes = List!(
// Just an example; you probably want to generate this
// list via introspection, e.g. via __traits(getMembers)
// or something like that.
identifier1,
identifier2,
...
);

mixin(generateEnum!MyNodes); // defines `enum NodeIds`

static assert(NodeIds.identifier1 == 0);
static assert(NodeIds.identifier2 == 1);
...

There are probably other ways to do it too, but I chose enum 
because it naturally assigns incrementing IDs to its members, 
so it's a convenient construct for this purpose.



T


I think I didn't explain well enough, I'm not trying to generate 
an enum from a list of pre-defined known quantities. The idea is 
that a compile-time generic function exists and it generates a 
unique identifier for that node.


My reasons for doing this is to remove the need to keep a master 
enum that holds an identifying value for each node. I've 
implemented this same thing in C++ before and it was extremely 
straightforward.


Re: Compile-time variables

2018-04-05 Thread H. S. Teoh via Digitalmars-d-learn
On Thu, Apr 05, 2018 at 11:53:00PM +, Kayomn via Digitalmars-d-learn wrote:
[...]
> ---
> alias NodeTypeID = uint;
> 
> enum NodeTypeID getNodeID() {
>   static NodeTypeID lastID = 0;
> 
>   return lastID++;
> }
> 
> enum NodeTypeID getNodeID(T)() {
>   static NodeTypeID typeID = getNodeID();
> 
>   return typeID;
> }
> ---
> 
> The expectation is that this is executed at compile time,
[...]
> However, I've been struggling with an error pertaining to getNodeType.
> The return statement of lastID++ is flagging the error "Static
> variable cannot be read at compile time."

`lastID`, as declared above, are runtime variables. The 'static' in this
case just means it's thread-local, rather than allocated on the stack.
You cannot modify these variables at compile-time.


> I may just be taking too much of a C++ lens to this, but to me this
> seems like it should work? Am I missing a qualifier or something here?

You appear to be wanting to increment a global variable during
compile-time. Unfortunately, there is no such thing as a compile-time
global variable.  You will have to find some other way to implement what
you want.

One way to do this would be to use compile-time introspection to
construct a list of nodes, and then use a CTFE function or static
foreach to generate node IDs all at once.  For example:

string generateEnum(T...)()
{
if (__ctfe) { // only run at compile-time
string code = "enum NodeIds {";
foreach (ident; T) {
code ~= ident ~ ", ";
}
code ~= "}";
return code;
}
else assert(0);
}

alias MyNodes = List!(
// Just an example; you probably want to generate this
// list via introspection, e.g. via __traits(getMembers)
// or something like that.
identifier1,
identifier2,
...
);

mixin(generateEnum!MyNodes); // defines `enum NodeIds`

static assert(NodeIds.identifier1 == 0);
static assert(NodeIds.identifier2 == 1);
...

There are probably other ways to do it too, but I chose enum because it
naturally assigns incrementing IDs to its members, so it's a convenient
construct for this purpose.


T

-- 
Having a smoking section in a restaurant is like having a peeing section in a 
swimming pool. -- Edward Burr