1 /// This module enables allocating a runtime ID for symbols. This makes it possible to uniquely identify node tags,
2 /// input actions and I/O modules.
3 ///
4 /// Use `staticID` to produce new static IDs.
5 module fluid.future.static_id;
6 
7 import std.traits;
8 
9 @safe:
10 
11 /// This function will produce a unique ID associated with the given symbol by allocating a small piece
12 /// of static memory. Its address will be then used as an ID.
13 ///
14 /// Each symbol will be associated with its own, unique ID. The ID will be the same for every call with
15 /// the same symbol.
16 ///
17 /// Returns: A unique ID produced from the symbol.
18 StaticID staticID(alias symbol)() {
19 
20     align(1)
21     static immutable bool _id;
22 
23     debug {
24         return StaticID(
25             cast(size_t) &_id,
26             fullyQualifiedName!symbol,
27         );
28     }
29     else {
30         return StaticID(
31             cast(size_t) &_id,
32         );
33     }
34 
35 }
36 
37 /// Unique ID generated from a symbol.
38 ///
39 /// See `staticID` for generating static IDs.
40 struct StaticID {
41 
42     /// The ID.
43     size_t id;
44 
45     /// Name of the symbol holding the ID.
46     debug string name;
47 
48     /// Returns: True if the IDs are the same.
49     bool opEquals(StaticID other) const {
50         return id == other.id;
51     }
52 
53     /// Compare two static IDs, enabling sorting. Note that this order is only guaranteed to be the same during
54     /// the same program run, as the IDs may change.
55     /// Returns:
56     ///     A negative value if this ID comes before the other. `0` if they're equal.
57     ///     A positive value if this ID comes after the other.
58     int opCmp(const StaticID other) const {
59         if (this.id < other.id) return -1;
60         if (this.id > other.id) return +1;
61         return 0;
62     }
63 
64     /// Returns: The ID, which by itself is a sufficient hash.
65     size_t toHash() const {
66 
67         return id;
68 
69     }
70 
71 }
72 
73 @("staticIDs are unique at runtime")
74 unittest {
75 
76     enum One;
77     enum Two;
78 
79     auto one = staticID!One;
80     auto two = staticID!Two;
81 
82     assert(one == staticID!One);
83     assert(two == staticID!Two);
84     assert(one == one);
85     assert(two == two);
86     assert(one != two);
87 
88     alias SecondOne = One;
89     alias SecondTwo = Two;
90 
91     assert(one == staticID!SecondOne);
92     assert(two == staticID!SecondTwo);
93     assert(one != staticID!SecondTwo);
94     assert(two != staticID!SecondOne);
95 
96 }
97 
98 @("staticIDs are global across threads")
99 @system
100 unittest {
101 
102     import std.concurrency;
103 
104     enum One;
105     enum Two;
106 
107     // IDs are global across threads
108     auto t0 = staticID!One;
109 
110     spawn({
111 
112         ownerTid.send(staticID!One);
113 
114         spawn({
115             ownerTid.send(staticID!One);
116         });
117 
118         ownerTid.send(receiveOnly!StaticID);
119         ownerTid.send(staticID!Two);
120 
121     });
122 
123     auto t1 = receiveOnly!StaticID;
124     auto t2 = receiveOnly!StaticID;
125 
126     auto c0 = staticID!Two;
127     auto c1 = receiveOnly!StaticID;
128 
129     assert(t0 == t1);
130     assert(t1 == t2);
131 
132     assert(c0 != t0);
133     assert(c1 != t1);
134     assert(c0 != t1);
135     assert(c1 != t0);
136 
137     assert(t0 == t1);
138 
139 }