1 /**
2  * The iz Runtime type informations.
3  */
4 module iz.rtti;
5 
6 import
7     std.format, std.traits, std.meta;
8 import
9     iz.memory, iz.types, iz.properties, iz.enumset, iz.streams, iz.containers;
10 
11 private __gshared HashMap_AB!(string, const(Rtti)*) _name2rtti;
12 private __gshared HashMap_AB!(const(Rtti)*, string) _rtti2name;
13 
14 shared static this()
15 {
16     _name2rtti.reserve(128);
17     _rtti2name.reserve(128);
18 }
19 
20 /**
21  * Enumerates the type constructors
22  */
23 enum TypeCtor
24 {
25     _const,
26     _immutable,
27     _inout,
28     _shared
29 }
30 
31 /// Set of TypeCtor.
32 alias TypeCtors = EnumSet!(TypeCtor, Set8);
33 
34 /**
35  * Enumerates the types supported by the Rtti
36  */
37 enum RtType: ubyte
38 {
39     _invalid,
40     _bool, _byte, _ubyte, _short, _ushort, _int, _uint, _long, _ulong,
41     _float, _double, _real,
42     _char, _wchar, _dchar,
43     _object, _struct, _union,
44     _enum,
45     _funptr,
46     _stream,
47     _aa,
48     _pointer,
49 }
50 
51 /**
52  * Enumerates the special struct type recognized by the Rtti
53  */
54 enum StructType: ubyte
55 {
56     _none,      /// no special traits.
57     _publisher, /// the struct has the traits of a PropertyPublisher.
58     _binary,    /// the struct can write and read itself to/from ubyte[].
59     _text,      /// the struct can write and read itself to/from char[].
60 }
61 
62 private static immutable RtTypeArr =
63 [
64     RtType._invalid,
65     RtType._bool, RtType._byte, RtType._ubyte, RtType._short, RtType._ushort,
66     RtType._int, RtType._uint, RtType._long, RtType._ulong,
67     RtType._float, RtType._double, RtType._real,
68     RtType._char, RtType._wchar, RtType._dchar,
69     RtType._object, RtType._struct, RtType._union,
70     RtType._enum,
71     RtType._funptr,
72     RtType._stream,
73     RtType._aa,
74     RtType._pointer
75 ];
76 
77 /// used as a wildcard to represent any struct.
78 package struct GenericStruct {}
79 /// used as a wildcard to represent any union.
80 package struct GenericUnion {}
81 /// used as a wildcard to represent any enum.
82 package struct GenericEnum {int value; alias value this;}
83 /// not reall used...
84 package struct GenericFunPtr{}
85 
86 package alias GenericRtTypes = AliasSeq!(
87     void,
88     bool, byte, ubyte, short, ushort, int, uint, long, ulong,
89     float, double, real,
90     char, wchar, dchar,
91     Object, GenericStruct, GenericUnion,
92     GenericEnum,
93     GenericFunPtr,
94     Stream,
95 );
96 
97 package alias BasicRtTypes = AliasSeq!(
98     void,
99     bool, byte, ubyte, short, ushort, int, uint, long, ulong,
100     float, double, real,
101     char, wchar, dchar
102 );
103 
104 /**
105  * Indicates if $(D T) is a basic runtime type (fixed length, not array, no type identifier)
106  */
107 template isBasicRtType(T)
108 {
109     enum i = staticIndexOf!(T, GenericRtTypes);
110     enum isBasicRtType = i > 0 && i <= staticIndexOf!(dchar, GenericRtTypes);
111 }
112 
113 /**
114  * Indicates the size of a variable according to its RtType
115  *
116  * Returns:
117  *      $(D 0) is returned if the size is variable otherwise the equivalent
118  *      of the $(D .sizeof) property.
119  */
120 ubyte size(RtType type)
121 {
122     with(RtType) final switch (type)
123     {
124         case _invalid:
125         case _object:
126         case _struct:
127         case _funptr:
128         case _stream:
129         case _enum:
130         case _aa:
131         case _pointer:
132         case _union:
133             return 0;
134         case _bool, _byte, _ubyte, _char:
135             return 1;
136         case _short, _ushort, _wchar:
137             return 2;
138         case _int, _uint, _dchar, _float:
139             return 4;
140         case _long, _ulong, _double:
141             return 8;
142         case _real:
143             return real.sizeof;
144     }
145 }
146 ///
147 unittest
148 {
149     struct St {} St st;
150     assert(st.getRtti.type.size == 0);
151     bool bo;
152     assert(bo.getRtti.type.size == 1);
153     ushort us;
154     assert(us.getRtti.type.size == 2);
155     uint ui;
156     assert(ui.getRtti.type.size == 4);
157     long lo;
158     assert(lo.getRtti.type.size == 8);
159     real re;
160     assert(re.getRtti.type.size == real.sizeof);
161 }
162 
163 /**
164  * Returns the identifier of a type.
165  *
166  * Params:
167  *      t = Either a RtType or a pointer to a Rtti.
168  * Returns:
169  *      For the basic types, always the equivalent of the keyword $(D .stringof) property.
170  *      For the other types, if $(D t) is a RtType then "struct", "enum", "Object", etc
171  *      are returned, otherwise if $(D t) is a pointer to a $(D Rtti) then the type
172  *      identifier is returned, e.g "E" for $(D enum E {e}).
173  */
174 string typeString(T)(T t)
175 {
176     static if (is(Unqual!T == RtType))
177     {
178         with(RtType) final switch (t)
179         {
180             case _pointer:  return "pointer";
181             case _aa:       return "aa";
182             case _invalid:  return "invalid";
183             case _bool:     return "bool";
184             case _byte:     return "byte";
185             case _ubyte:    return "ubyte";
186             case _short:    return "short";
187             case _ushort:   return "ushort";
188             case _int:      return "int";
189             case _uint:     return "uint";
190             case _long:     return "long";
191             case _ulong:    return "ulong";
192             case _float:    return "float";
193             case _double:   return "double";
194             case _real:     return "real";
195             case _char:     return "char";
196             case _wchar:    return "wchar";
197             case _dchar:    return "dchar";
198             case _object:   return "Object";
199             case _struct:   return "struct";
200             case _enum:     return "enum";
201             case _funptr:   return "funptr";
202             case _stream:   return "Stream";
203             case _union:    return "union";
204         }
205     }
206     else static if (is(T == const(Rtti)*))
207     {
208         if (auto s = t in _rtti2name)
209             return *s;
210         else
211             return "";
212     }
213     else static assert(0);
214 }
215 
216 private mixin template setContext()
217 {
218     private auto internalSetContext(bool restore)(void* context) const
219     {
220         static if (!restore) void* old;
221         foreach(member; __traits(allMembers, typeof(this)))
222             static if (is(typeof(__traits(getMember, typeof(this), member)) == delegate))
223             {
224                 static if (!restore) old = __traits(getMember, typeof(this), member).ptr;
225                 __traits(getMember, typeof(this), member).ptr = context;
226             }
227         static if (!restore) return old;
228     }
229 
230     /// Sets the context before usage.
231     public alias setContext = internalSetContext!false;
232 
233     /// Restores the context after usage.
234     public alias restoreContext = internalSetContext!true;
235 }
236 
237 /**
238  * Runtime information for the function pointers.
239  */
240 struct FunPtrInfo
241 {
242     /// Indicates the function type identifier.
243     string identifier;
244     /// If hasContext is true then it's a delegate, otherwise a pointer to a function.
245     bool hasContext;
246     /// Indicates the return type.
247     const Rtti* returnType;
248     /// Contains the Rtti of each parameter.
249     const Rtti*[] parameters;
250     /// Indicates the size of the pointer.
251     ubyte size() const
252     {
253         if (hasContext) return size_t.sizeof * 2;
254         else return size_t.sizeof;
255     }
256 }
257 
258 /**
259  * Runtime information for the classes.
260  */
261 struct ClassInfo
262 {
263     /// Indicates the class type identifier.
264     string identifier;
265     /// Stores the static address of the default __ctor.
266     Object function() constructor;
267     /// Stores the static initializer.
268     const void[] initialLayout;
269     /// Indicates wether the class is a iz.Stream
270     bool isStream;
271 }
272 
273 /**
274  * Runtime information for the enums.
275  */
276 struct EnumInfo
277 {
278     /// Indicates the enum type identifier.
279     string identifier;
280     /// Contains the identifier of each member.
281     string[] members;
282     /// Contains the value of each member.
283     uint[] values;
284     /// Indicates the type of the values.
285     const(Rtti)* valueType;
286 }
287 
288 /**
289  * Runtime information for a struct that can be saved and reloaded from an array
290  * of bytes.
291  */
292 struct BinTraits
293 {
294     /// Returns a delegate to the struct's restoreFromBytes() method.
295     void delegate(ubyte[]) loadFromBytes;
296 
297     /// Returns a delegate to the struct's saveToBytes() method.
298     ubyte[] delegate() saveToBytes;
299 
300     /// Sets the delegates context.
301     mixin setContext;
302 }
303 
304 /**
305  * Runtime information for a struct that can be saved and reloaded from a string
306  */
307 struct TextTraits
308 {
309     /// Returns a delegate to the struct's restoreFromText() method.
310     void delegate(const(char)[]) loadFromText;
311 
312     /// Returns a delegate to the struct's saveToText() method.
313     const(char)[] delegate() saveToText;
314 
315     /// Sets the delegates context.
316     mixin setContext;
317 }
318 
319 /**
320  * Runtime information for a struct that has the traits of a PropertyPublisher.
321  *
322  * Publishing structs have the ability to be used as any class
323  * that implements the PropertyPublisher interface.
324  */
325 struct PubTraits
326 {
327     /// Returns a delegate to the struct's publicationFromName() method.
328     GenericDescriptor* delegate(const(char)[]) publicationFromName;
329 
330     /// Returns a delegate to the struct's publicationFromIndex() method.
331     GenericDescriptor* delegate(size_t) publicationFromIndex;
332 
333     /// Returns a delegate to the struct's publicationCount() method.
334     size_t delegate() publicationCount;
335 
336     /// Sets the delegates context.
337     mixin setContext;
338 }
339 
340 private union StructTraits
341 {
342     BinTraits binTraits;
343     TextTraits textTraits;
344     PubTraits pubTraits;
345 }
346 
347 /**
348  * Runtime information for the structs.
349  */
350 struct StructInfo
351 {
352     /// Constructs the info for a "noname" struct
353     this(string identifier, StructType type)
354     {
355         this.identifier = identifier;
356         this.type = type;
357     }
358 
359     /// Constructs the info for a special struct.
360     this(T)(string identifier, StructType type, T t)
361     {
362         this.identifier = identifier;
363         this.type = type;
364         static if (is(T == PubTraits))
365             structInfo.publisherInfo = t;
366         else static if (is(T == TextTraits))
367             structInfo.textInfo = t;
368         else static if (is(T == BinTraits))
369             structInfo.binaryInfo = t;
370         else static assert(0, "third argument of Rtti ctor must be an Info");
371     }
372 
373     /// Indicates the struct type identifier.
374     string identifier;
375 
376     /// Indicates the special struct type.
377     StructType type;
378 
379     /// The information for the structure.
380     StructTraits structSpecialInfo;
381 
382     /// Returns: The struct information when structType is equal to StructType._binary
383     const(BinTraits)* binTraits() const
384     {
385         return &structSpecialInfo.binTraits;
386     }
387 
388     /// Returns: The struct information when structType is equal to StructType._text
389     const(TextTraits)* textTraits() const
390     {
391         return &structSpecialInfo.textTraits;
392     }
393 
394     /// Returns the struct information when structType is equal to StructType._publisher
395     const(PubTraits)* pubTraits() const
396     {
397         return &structSpecialInfo.pubTraits;
398     }
399 }
400 
401 /**
402  * Runtime information for the unions.
403  */
404 struct UnionInfo
405 {
406     this(A...)(string identifier, const(Rtti)*[] members)
407     {
408         membersType = members;
409         this.identifier = identifier;
410     }
411 
412     /// Indicates the union type identifier.
413     string identifier;
414 
415     /// An array with the Rtti of each member
416     const(Rtti)*[] membersType;
417 }
418 
419 /**
420  * Runtime information for the associative arrays
421  */
422 struct AAInfo
423 {
424     /// A pointer to the Rtti of the keys.
425     const(Rtti)* keyType;
426     /// A pointer to the Rtti of the values.
427     const(Rtti)* valueType;
428 }
429 
430 /**
431  * Runtime information for the pointers
432  */
433 struct PointerInfo
434 {
435     /// A pointer to the Rtti of the target.
436     const(Rtti)* type;
437 }
438 
439 private union Infos
440 {
441     FunPtrInfo funptrInfo;
442     ClassInfo classInfo;
443     EnumInfo enumInfo;
444     StructInfo structInfo;
445     AAInfo aaInfo;
446     PointerInfo pointerInfo;
447     UnionInfo unionInfo;
448 }
449 
450 /**
451  * Runtime type information
452  */
453 struct Rtti
454 {
455     /// Constructs a Rtti with a type and the info for this type.
456     this(T)(RtType type, ubyte dim, TypeCtors typeCtors, auto ref T t)
457     {
458         this.type = type;
459         this.dimension = dim;
460         this.typeCtors = typeCtors;
461         static if (is(T == FunPtrInfo))
462             infos.funptrInfo = t;
463         else static if (is(T == ClassInfo))
464             infos.classInfo = t;
465         else static if (is(T == EnumInfo))
466             infos.enumInfo = t;
467         else static if (is(T == StructInfo))
468             infos.structInfo = t;
469         else static if (is(T == AAInfo))
470             infos.aaInfo = t;
471         else static if (is(T == PointerInfo))
472             infos.pointerInfo = t;
473         else static if (is(T == UnionInfo))
474             infos.unionInfo = t;
475         else static assert(0, "last argument of Rtti ctor must be an Info");
476     }
477 
478     /// Constructs a Rtti with a (basic) type.
479     this(RtType rtType, ubyte dim, TypeCtors typeCtors)
480     in
481     {
482         import std.conv: to;
483         assert(rtType >= RtType._invalid &&rtType <= RtType._dchar,
484             "this ctor must only be used with basic types");
485     }
486     body
487     {
488         this.type = rtType;
489         this.dimension = dim;
490         this.typeCtors = typeCtors;
491     }
492 
493     /// The runtime type
494     RtType type;
495 
496     /// Type is an array
497     ubyte dimension;
498 
499     /// Attributes for this type
500     TypeCtors typeCtors;
501 
502     /// The information for this type.
503     Infos infos;
504 
505     /// Returns: A valid information when type is equal to $(D RtT._callable).
506     const(FunPtrInfo)* funptrInfo() const
507     {
508         return &infos.funptrInfo;
509     }
510 
511     /// Returns: A valid information when type is equal to $(D RtT._object).
512     const(ClassInfo)* classInfo() const
513     {
514         return &infos.classInfo;
515     }
516 
517     /// Returns: A valid information when type is equal to $(D RtT._enum).
518     const(EnumInfo)* enumInfo() const
519     {
520         return &infos.enumInfo;
521     }
522 
523     /// Returns: A valid information when type is equal to $(D RtT._struct).
524     const(StructInfo)* structInfo() const
525     {
526         return &infos.structInfo;
527     }
528 
529     /// Returns: A valid information when type is equal to $(D RtT._aa).
530     const(AAInfo)* aaInfo() const
531     {
532         return &infos.aaInfo;
533     }
534 
535     /// Returns: A valid information when type is equal to $(D RtT._pointer).
536     const(PointerInfo)* pointerInfo() const
537     {
538         return &infos.pointerInfo;
539     }
540 
541     /// Returns: A valid information when type is equal to $(D RtT._union).
542     const(UnionInfo)* unionInfo() const
543     {
544         return &infos.unionInfo;
545     }
546 }
547 
548 /**
549  * Returns the Rtti for the type that has its .stringof property
550  * equal to $(D typeString).
551  */
552 const(Rtti)* getRtti(const(char)[] typeString)
553 {
554     return *(typeString in _name2rtti);
555 }
556 
557 /**
558  * Registers and returns the Rtti for the type (or the variable)
559  * passed as argument.
560  */
561 const(Rtti)* getRtti(A = void, B...)(auto ref B b)
562 if (B.length < 2)
563 {
564     static if (!B.length)
565         alias TT = A;
566     else
567         alias TT = B[0];
568 
569     if (const(Rtti)** result = TT.stringof in _name2rtti)
570         return *result;
571 
572     enum err = "unsupported type \"" ~ TT.stringof ~ "\": ";
573 
574     alias UnAttr(T) = SetFunctionAttributes!(T, "D", 0);
575 
576     enum ubyte dim = dimensionCount!TT;
577     static assert(dim <= ubyte.max);
578     static if (dim > 0)
579         alias T = Unqual!(ArrayElementType!TT);
580     else
581         alias T = TT;
582 
583     TypeCtors typeCtors;
584     static if (is(T==const)) typeCtors += TypeCtor._const;
585     static if (is(T==immutable)) typeCtors += TypeCtor._immutable;
586     static if (is(T==inout)) typeCtors += TypeCtor._inout;
587     static if (is(T==shared)) typeCtors += TypeCtor._shared;
588 
589     static if (staticIndexOf!(Unqual!T, BasicRtTypes) != -1)
590     {
591         RtType i = RtTypeArr[staticIndexOf!(Unqual!T, BasicRtTypes)];
592         const(Rtti)* result = construct!Rtti(i, dim, typeCtors);
593     }
594     else static if (is(T == enum))
595     {
596         static if (isImplicitlyConvertible!(OriginalType!T, uint))
597         {
598             string[] members;
599             uint[] values;
600             foreach(e; __traits(allMembers, T))
601             {
602                 members ~= e;
603                 values  ~= __traits(getMember, T, e);
604                 static assert(__traits(getMember, T, e) > -1, err ~
605                     "negative enum values are not supported");
606                 static assert(!is(e == enum), err ~ "nested enums are not supported");
607             }
608             const(Rtti)* result = construct!Rtti(RtType._enum, dim, typeCtors,
609                 EnumInfo(T.stringof, members, values, getRtti!(OriginalType!T)));
610         }
611         else static assert(0, err ~ "only enums whose type is convertible to int are supported");
612     }
613     else static if (is(PointerTarget!T == function) || is(T == delegate))
614     {
615         alias R = ReturnType!T;
616         alias P = Parameters!T;
617 
618         const(Rtti*)[] pr;
619         foreach(Prm; P)
620             pr ~= getRtti!Prm;
621         const(Rtti)* result = construct!Rtti(RtType._funptr, dim, typeCtors,
622             FunPtrInfo(T.stringof, is(T == delegate), cast(Rtti*)getRtti!R, pr));
623     }
624     else static if (is(T == class) || is(T == Stream))
625     {
626         static if(!is(T == Stream))
627         {
628             static if (hasDefaultConstructor!T)
629                 enum ctor = cast(Object function()) defaultConstructor!T;
630             else
631                 enum ctor = cast(Object function()) null;
632         }
633         else
634             enum ctor = cast(Object function()) null;
635         auto init = typeid(T).initializer[];
636         const(RtType) tp = (is(T:Stream) | is(T==Stream)) ? RtType._stream : RtType._object;
637         const(Rtti)* result = construct!Rtti(tp, dim, typeCtors, ClassInfo(T.stringof, ctor, init));
638     }
639     else static if (is(T == struct) && __traits(hasMember, T, "publicationFromName"))
640     {
641         static if (!__traits(hasMember, T, "publicationFromName") ||
642             !is(typeof(__traits(getMember, T, "publicationFromName")) ==
643                 typeof(__traits(getMember, PropertyPublisher, "publicationFromName"))))
644             static assert(0, "no valid publicationFromName member");
645 
646         static if (!__traits(hasMember, T, "publicationFromIndex") ||
647             !is(typeof(__traits(getMember, T, "publicationFromIndex")) ==
648                 typeof(__traits(getMember, PropertyPublisher, "publicationFromIndex"))))
649             static assert(0, "no valid publicationFromIndex member");
650 
651         static if (!__traits(hasMember, T, "publicationCount") ||
652             !is(typeof(__traits(getMember, T, "publicationCount")) ==
653                 typeof(__traits(getMember, PropertyPublisher, "publicationCount"))))
654             static assert(0, "no valid publicationCount member");
655 
656         const(Rtti)* result = construct!Rtti(RtType._struct, dim, typeCtors,
657             StructInfo(T.stringof, StructType._publisher));
658 
659         const(StructInfo)* si = result.structInfo;
660         si.pubTraits.publicationFromName.funcptr = &__traits(getMember, T, "publicationFromName");
661         si.pubTraits.publicationFromIndex.funcptr = &__traits(getMember, T, "publicationFromIndex");
662         si.pubTraits.publicationCount.funcptr = &__traits(getMember, T, "publicationCount");
663     }
664     else static if (is(T == struct) && __traits(hasMember, T, "saveToBytes"))
665     {
666         static if (!__traits(hasMember, T, "saveToBytes") ||
667             !is(UnAttr!(typeof(&__traits(getMember, T, "saveToBytes"))) == ubyte[] function()))
668             static assert(0, "no valid saveToBytes member");
669 
670         static if (!__traits(hasMember, T, "loadFromBytes") ||
671             !is(UnAttr!(typeof(&__traits(getMember, T, "loadFromBytes"))) == void function(ubyte[])))
672             static assert(0, "no valid loadFromBytes member");
673 
674         const(Rtti)* result = construct!Rtti(RtType._struct, dim, typeCtors,
675             StructInfo(T.stringof, StructType._binary));
676         const(StructInfo)* si = result.structInfo;
677         si.binTraits.saveToBytes.funcptr = &__traits(getMember, T, "saveToBytes");
678         si.binTraits.loadFromBytes.funcptr = &__traits(getMember, T, "loadFromBytes");
679     }
680     else static if (is(T == struct) && __traits(hasMember, T, "saveToText"))
681     {
682         static if (!__traits(hasMember, T, "saveToText") ||
683             !is(UnAttr!(typeof(&__traits(getMember, T, "saveToText"))) == const(char)[] function()))
684             static assert(0, "no valid saveToText member");
685 
686         static if (!__traits(hasMember, T, "loadFromText") ||
687             !is(UnAttr!(typeof(&__traits(getMember, T, "loadFromText"))) == void function(const(char)[])))
688             static assert(0, "no valid loadFromText member");
689 
690         const(Rtti)* result = construct!Rtti(RtType._struct, dim, typeCtors,
691             StructInfo(T.stringof, StructType._text));
692         const(StructInfo)* si = result.structInfo;
693         si.textTraits.saveToText.funcptr = &__traits(getMember, T, "saveToText");
694         si.textTraits.loadFromText.funcptr = &__traits(getMember, T, "loadFromText");
695     }
696     else static if (is(T == struct))
697     {
698         const(Rtti)* result = construct!Rtti(RtType._struct, dim, typeCtors,
699             StructInfo(T.stringof, StructType._none));
700     }
701     else static if (is(T == union))
702     {
703         const(Rtti)*[] members;
704         foreach(member; __traits(allMembers, T))
705         {
706             members ~= getRtti!(typeof(__traits(getMember, T, member)));
707         }
708         const(Rtti)* result = construct!Rtti(RtType._union, dim, typeCtors, UnionInfo(T.stringof, members));
709     }
710     else static if (isAssociativeArray!T)
711     {
712         const(Rtti)* result = construct!Rtti(RtType._aa, dim, typeCtors,
713             AAInfo(getRtti!(KeyType!T), getRtti!(ValueType!T)));
714     }
715     else static if (isPointer!T)
716     {
717         const(Rtti)* result = construct!Rtti(RtType._pointer, dim, typeCtors,
718             PointerInfo(getRtti!(PointerTarget!T)));
719     }
720     else
721     {
722         typeCtors = 0;
723         version(none) static assert(0, err ~ "not handled at all");
724         else const(Rtti)* result = construct!Rtti(RtType._invalid, 0, typeCtors);
725     }
726 
727     _name2rtti[TT.stringof] = result;
728     const(Rtti)** res = TT.stringof in _name2rtti;
729     _rtti2name[*res] = TT.stringof;
730     return *res;
731 }
732 ///
733 unittest
734 {
735     enum Option: ubyte {o1 = 2, o2, o3}
736     Option option1, option2;
737     // first call will register
738     const(Rtti)* rtti1 = getRtti(option1);
739     const(Rtti)* rtti2 = getRtti(option2);
740     // variables of same type point to the same info.
741     assert(rtti1 is rtti2);
742     // get the Rtti without the static type.
743     const(Rtti)* rtti3 = getRtti("Option");
744     assert(rtti3 is rtti1);
745     assert(rtti3.enumInfo.identifier == "Option");
746     assert(rtti3.enumInfo.members == ["o1", "o2", "o3"]);
747     assert(rtti3.enumInfo.values == [2, 3, 4]);
748 
749     assert(rtti3.enumInfo.valueType is rtti1.enumInfo.valueType);
750     assert(rtti3.enumInfo.valueType is rtti2.enumInfo.valueType);
751     assert(rtti3.enumInfo.valueType is getRtti!ubyte);
752 }
753 
754 /**
755  * Returns true if the Rtti passed as argument are for a "publising" struct.
756  */
757 bool isPublisingStruct(const(Rtti)* ti)
758 {
759     bool result;
760     if (ti && ti.type == RtType._struct && ti.structInfo.type == StructType._publisher)
761         result = true;
762     return result;
763 }
764 
765 unittest
766 {
767     static struct PubStr
768     {
769         mixin PropertyPublisherImpl;
770     }
771     assert(isPublisingStruct(getRtti!PubStr));
772 }
773 
774 unittest
775 {
776     enum Option {o1 = 2, o2, o3}
777     Option[][] opts = [[Option.o1, Option.o2],[Option.o1, Option.o2]];
778     assert(getRtti(opts[0]) is getRtti(opts[1]));
779 }
780 
781 unittest
782 {
783     import std.algorithm.searching: countUntil;
784     enum Enumeration: ubyte {a1, a2, a3}
785     const(Rtti)* ati = getRtti!Enumeration;
786     assert(ati.enumInfo.values[0] == 0);
787     assert(ati.enumInfo.values[1] == 1);
788     assert(ati.enumInfo.values[2] == 2);
789     assert(ati.enumInfo.members[0] == "a1");
790     assert(ati.enumInfo.members[1] == "a2");
791     assert(ati.enumInfo.members[2] == "a3");
792     assert(0 == countUntil(ati.enumInfo.values, 0));
793     assert(1 == countUntil(ati.enumInfo.values, 1));
794     assert(2 == countUntil(ati.enumInfo.values, 2));
795 }
796 
797 unittest
798 {
799     void basicTest(T)()
800     {
801         const(Rtti)* inf = getRtti!T;
802         assert(inf.type == RtTypeArr[staticIndexOf!(T, BasicRtTypes)]);
803     }
804     foreach(T; BasicRtTypes[1..$])
805         basicTest!(T);
806 }
807 
808 unittest
809 {
810     void arrayTest(T)()
811     {
812         const(Rtti)* inf = getRtti!T;
813         assert(inf.type == RtTypeArr[staticIndexOf!(Unqual!(ArrayElementType!T), BasicRtTypes)]);
814         assert(inf.dimension == dimensionCount!T);
815     }
816     foreach(T; BasicRtTypes[1..$])
817     {
818         arrayTest!(T[]);
819         arrayTest!(T[][]);
820         arrayTest!(T[][][]);
821     }
822 }
823 
824 unittest
825 {
826     static struct Foo
827     {
828         uint delegate(uint) a;
829         string function(ulong,char) b;
830     }
831     Foo foo;
832     const(Rtti)* dgi = getRtti(foo.a);
833     assert(dgi.type == RtType._funptr);
834     assert(dgi.funptrInfo.hasContext);
835     assert(dgi.funptrInfo.returnType.type == RtType._uint);
836     assert(dgi.funptrInfo.parameters.length == 1);
837     assert(dgi.funptrInfo.parameters[0].type == RtType._uint);
838     assert(dgi.funptrInfo.size == size_t.sizeof * 2);
839 
840     const(Rtti)* fgi = getRtti(foo.b);
841     assert(fgi.type == RtType._funptr);
842     assert(!fgi.funptrInfo.hasContext);
843     assert(fgi.funptrInfo.returnType.type == RtType._char); // _string
844     assert(fgi.funptrInfo.parameters.length == 2);
845     assert(fgi.funptrInfo.parameters[0].type == RtType._ulong);
846     assert(fgi.funptrInfo.parameters[1].type == RtType._char);
847     assert(fgi.funptrInfo.size == size_t.sizeof);
848 }
849 
850 unittest
851 {
852     static struct Bar
853     {
854         size_t publicationCount(){return 0;}
855         GenericDescriptor* publicationFromName(const(char)[]){return null;}
856         GenericDescriptor* publicationFromIndex(size_t){return null;}
857     }
858     Bar bar;
859     const(Rtti)* rtti = getRtti(bar);
860     assert(rtti.type == RtType._struct);
861     assert(rtti.structInfo.type == StructType._publisher);
862     assert(rtti.structInfo.identifier == "Bar");
863     rtti.structInfo.pubTraits.setContext(cast(void*) &bar);
864     assert(rtti.structInfo.pubTraits.publicationCount == &bar.publicationCount);
865     assert(rtti.structInfo.pubTraits.publicationFromIndex == &bar.publicationFromIndex);
866     assert(rtti.structInfo.pubTraits.publicationFromName == &bar.publicationFromName);
867     // coverage
868     assert(rtti.structInfo.pubTraits.publicationCount() == bar.publicationCount);
869     assert(rtti.structInfo.pubTraits.publicationFromIndex(0) == bar.publicationFromIndex(0));
870     assert(rtti.structInfo.pubTraits.publicationFromName("") == bar.publicationFromName(""));
871 }
872 
873 unittest
874 {
875     class Gaz
876     {
877         this(){}
878     }
879     Gaz gaz = new Gaz;
880     import std.stdio;
881     const(Rtti)* rtti = getRtti(gaz);
882     assert(rtti.type == RtType._object);
883     assert(rtti.classInfo.identifier == "Gaz");
884     assert(rtti.classInfo.constructor == &Gaz.__ctor);
885     assert(rtti.classInfo.initialLayout == typeid(Gaz).initializer);
886 }
887 
888 unittest
889 {
890     struct Hop
891     {
892         const(char)[] saveToText(){return "hop";}
893         void loadFromText(const(char)[] value){}
894     }
895     Hop hop;
896     const(Rtti)* rtti = getRtti(hop);
897     assert(rtti.type == RtType._struct);
898     assert(rtti.structInfo.type == StructType._text);
899     assert(rtti.structInfo.identifier == "Hop");
900     rtti.structInfo.textTraits.setContext(cast(void*) &hop);
901     assert(rtti.structInfo.textTraits.saveToText == &hop.saveToText);
902     assert(rtti.structInfo.textTraits.loadFromText == &hop.loadFromText);
903     // coverage
904     assert(rtti.structInfo.textTraits.saveToText() == "hop");
905     rtti.structInfo.textTraits.loadFromText("hop");
906 }
907 
908 unittest
909 {
910     struct Boo
911     {
912         ubyte[] saveToBytes(){return [0x0];}
913         void loadFromBytes(ubyte[] value){}
914     }
915     Boo boo;
916     const(Rtti)* rtti = getRtti(boo);
917     assert(rtti.type == RtType._struct);
918     assert(rtti.structInfo.type == StructType._binary);
919     assert(rtti.structInfo.identifier == "Boo");
920     rtti.structInfo.binTraits.setContext(cast(void*) &boo);
921     assert(rtti.structInfo.binTraits.saveToBytes == &boo.saveToBytes);
922     assert(rtti.structInfo.binTraits.loadFromBytes == &boo.loadFromBytes);
923     // coverage
924     assert(rtti.structInfo.binTraits.saveToBytes() == [0x0]);
925     rtti.structInfo.binTraits.loadFromBytes([0x0]);
926 }
927 
928 unittest
929 {
930     shared(int) si;
931     assert(TypeCtor._shared in si.getRtti.typeCtors);
932     const(int) ci;
933     assert(TypeCtor._const in ci.getRtti.typeCtors);
934     immutable(int) ii;
935     assert(TypeCtor._immutable in ii.getRtti.typeCtors);
936     const(shared(int)) sii;
937     assert(TypeCtor._const in sii.getRtti.typeCtors);
938     assert(TypeCtor._shared in sii.getRtti.typeCtors);
939 }
940 
941 unittest
942 {
943     struct Gap
944     {
945         static void foo(ref const(int)){}
946         static void baz(ref const(int)){}
947         static void bar(out shared(int)){}
948     }
949     auto fti0 = getRtti(&Gap.foo);
950     assert(TypeCtor._const in fti0.funptrInfo.parameters[0].typeCtors);
951     auto fti1 = getRtti(&Gap.bar);
952     assert(TypeCtor._shared in fti1.funptrInfo.parameters[0].typeCtors);
953     auto fti2 = getRtti(&Gap.baz);
954     assert(fti0 is fti2);
955 }
956 
957 unittest
958 {
959     assert(getRtti!(const(int)) !is getRtti!(int));
960 }
961 
962 unittest
963 {
964     MemoryStream str;
965     assert(getRtti(str).type == RtType._stream);
966 }
967 
968 unittest
969 {
970     struct Cup
971     {
972         ubyte[] saveToBytes(){return [0x0];}
973         void loadFromBytes(ubyte[] value){}
974     }
975     Cup cup0;
976     Cup cup1;
977     const(Rtti)* rtti = getRtti(cup0);
978 
979     // to bind, two simultaneous xxxTraits must exist
980     const(BinTraits) bt0 = *rtti.structInfo.binTraits;
981     const(BinTraits) bt1 = *rtti.structInfo.binTraits;
982     bt0.setContext(&cup0);
983     bt1.setContext(&cup1);
984 
985     assert(bt0.saveToBytes.funcptr == bt1.saveToBytes.funcptr);
986     assert(bt0.saveToBytes.ptr != bt1.saveToBytes.ptr);
987 
988     bt0.loadFromBytes(bt0.saveToBytes());
989 }
990 
991 unittest
992 {
993     int[byte] k;
994     const(Rtti)* kti = getRtti(k);
995     assert(kti.type == RtType._aa);
996     assert(kti.aaInfo.keyType is getRtti!byte);
997     assert(kti.aaInfo.valueType is getRtti!int);
998 
999     long[string] h;
1000     const(Rtti)* hti = getRtti(h);
1001     assert(hti.type == RtType._aa);
1002     assert(hti.aaInfo.keyType is getRtti!string);
1003     assert(hti.aaInfo.valueType is getRtti!long);
1004 }
1005 
1006 unittest
1007 {
1008     int* i;
1009     const(Rtti)* iti = getRtti(i);
1010     assert(iti.type == RtType._pointer);
1011     assert(iti.pointerInfo.type is getRtti!int);
1012 
1013     int** pi;
1014     const(Rtti)* piti = getRtti(pi);
1015     assert(piti.type == RtType._pointer);
1016     assert(piti.pointerInfo.type is getRtti!(int*));
1017     assert(piti.pointerInfo.type.pointerInfo.type is getRtti!(int));
1018 }
1019 
1020 unittest
1021 {
1022     union U {int i; void* a;} U u;
1023     const(Rtti)* uti = getRtti(u);
1024     assert(uti.type == RtType._union);
1025     assert(uti.unionInfo.identifier == "U");
1026     assert(uti.unionInfo.membersType.length == 2);
1027     assert(uti.unionInfo.membersType[0] is getRtti!int);
1028     assert(uti.unionInfo.membersType[1] is getRtti!(void*));
1029 }
1030