@@ -15,6 +15,7 @@ import numem.core.exception;
1515import numem.core.hooks ;
1616import numem.optional;
1717import numem.lifetime;
18+ import numem.object;
1819
1920/**
2021 A type which can contain any type in the language, stored on the heap.
@@ -27,26 +28,99 @@ import numem.lifetime;
2728struct Variant {
2829private :
2930 // Helpers that are not nogc, to allow usage with druntime.
30- static bool isType (T)(TypeInfo id) => id == typeid (T);
31+ static bool isType (T)(TypeInfo id) {
32+ static if (is (T == class ))
33+ auto selfTid = cast (TypeInfo_Class )typeid (T);
34+ else static if (is (T == interface ))
35+ auto selfTid = cast (TypeInfo_Interface )typeid (T);
36+ else
37+ auto selfTid = typeid (T);
38+
39+ static if (is (T == class ) || is (T == interface )) {
40+ if (auto tid = cast (TypeInfo_Class )typeid (T)) {
41+ return tid.isBaseOf(selfTid);
42+ }
43+ if (auto tid = cast (TypeInfo_Interface )typeid (T)) {
44+ return tid.isBaseOf(selfTid);
45+ }
46+ }
47+ return id == typeid (T);
48+ }
3149 static string getTypeName (TypeInfo id) => id.toString();
50+ alias destroyFuncT = void function (void * data) @nogc ;
3251
3352@nogc :
53+
3454 TypeInfo id;
55+ destroyFuncT destroyFunc;
3556 void * data;
3657
3758 // Internal ctor
38- this (TypeInfo id, void * data) @trusted nothrow {
59+ this (TypeInfo id, void * data, void function ( void * data) @nogc destroyFunc ) @trusted nothrow {
3960 this .id = id;
61+ this .destroyFunc = destroyFunc;
4062 this .data = data;
4163 }
4264
65+ // / Helper that frees values.
66+ pragma (inline, true )
67+ void freeValue () @trusted nothrow {
68+ if (destroyFunc && data ! is null )
69+ assumeNoThrowNoGC(destroyFunc, data);
70+
71+ this .id = null ;
72+ this .destroyFunc = null ;
73+ this .data = null ;
74+ }
75+
76+ // / Helper that sets values.
77+ pragma (inline, true )
78+ void setValue (T)(auto ref T value) @trusted nothrow {
79+ static if (is (T == Variant )) {
80+ this .id = value.id;
81+ this .destroyFunc = value.destroyFunc;
82+ this .data = value.data;
83+ } else {
84+ this .id = typeid (T);
85+
86+ static if (isArray! T) {
87+ this .destroyFunc = (void * value) {
88+ T tmp = * cast (T* )value;
89+ nogc_trydelete(tmp[0 .. $]);
90+ nu_free(value);
91+ };
92+ } else static if (hasAnyDestructor! T) {
93+ this .destroyFunc = (void * value) {
94+ static if (isHeapAllocated! T)
95+ T* tmp = cast (T* )&value;
96+ else
97+ T* tmp = cast (T* )value;
98+
99+ nogc_trydelete(* tmp);
100+ };
101+ } else {
102+ this .destroyFunc = (void * value) {
103+ nu_free(value);
104+ };
105+ }
106+
107+ static if (isHeapAllocated! T) {
108+ this .data = cast (void * )value;
109+ } else {
110+
111+ this .data = nu_malloc(T.sizeof);
112+ * (cast (T* )this .data) = value.move();
113+ }
114+ }
115+ }
116+
43117public :
44118 alias isInitialized this ;
45119
46120 /**
47121 An empty variant.
48122 */
49- enum empty = Variant (null , null );
123+ enum empty = Variant (null , null , null );
50124
51125 /**
52126 Whether the variant is empty.
@@ -58,22 +132,16 @@ public:
58132 */
59133 @property bool isInitialized() @trusted nothrow pure => id ! is null && data ! is null ;
60134
135+ /**
136+ The type of the value stored in the variant.
137+ */
138+ @property TypeInfo type() @trusted nothrow pure => id;
139+
61140 /**
62141 Constructor
63142 */
64143 this (T)(auto ref T value) @trusted nothrow {
65- static if (is (T == Variant )) {
66- this .id = value.id;
67- this .data = value.data;
68- } else {
69- this .id = typeid (T);
70- static if (isHeapAllocated! T)
71- this .data = cast (void * )value;
72- else {
73- this .data = nu_malloc(T.sizeof);
74- * (cast (T* )this .data) = value.move();
75- }
76- }
144+ this .setValue! T(value);
77145 }
78146
79147 /**
@@ -125,18 +193,7 @@ public:
125193
126194 // / Allows assigning the variant to a value.
127195 void opAssign (T)(auto ref T value) @trusted nothrow {
128- static if (is (T == Variant )) {
129- this .id = value.id;
130- this .data = value.data;
131- } else {
132- this .id = typeid (T);
133- static if (isHeapAllocated! T)
134- this .data = cast (void * )value;
135- else {
136- this .data = nu_malloc(T.sizeof);
137- * (cast (T* )this .data) = value.move();
138- }
139- }
196+ this .setValue! T(value);
140197 }
141198
142199 /**
@@ -154,11 +211,7 @@ public:
154211 question.
155212 */
156213 void free () @trusted nothrow {
157- this .id = null ;
158- if (data) {
159- nu_free(data);
160- this .data = null ;
161- }
214+ this .freeValue();
162215 }
163216}
164217
@@ -189,6 +242,18 @@ unittest {
189242 assert (v == v2 && ! v);
190243}
191244
245+ @(" Variant: classes" )
246+ unittest {
247+ static bool destroyed__ = false ;
248+ static class TestClass { ~this () { destroyed__ = true ; } uint t = 0 ; }
249+
250+ Variant obj = nogc_new! TestClass();
251+ assert (obj.get ! Object ());
252+
253+ obj.free();
254+ assert (destroyed__);
255+ }
256+
192257@(" Variant: arrays" )
193258unittest {
194259 Variant v = [0 , 1 , 2 , 3 ].nu_dup();
0 commit comments