root/trunk/meta/nameof.d

Revision 213, 8.3 kB (checked in by Don Clugston, 6 years ago)

Deleted some old workarounds that haven't been required since about DMD 0.145.

Line 
1 /**
2  *   Convert any D symbol or type to a human-readable string, at compile time.
3  *
4  *   Given any D symbol (class, template, function, module name, or non-local variable)
5  *   or any D type, convert it to a compile-time string literal,
6  *   optionally containing the fully qualified and decorated name.
7  *
8  *   Limitations (as of DMD 0.162):
9  *   1. Names of local variables cannot be determined, because they are not permitted
10  *      as template alias parameters. Technically, it's possible to determine the name by using
11  *      a mixin hack, but it's so ugly that it cannot be recommended.
12  *   2. The name mangling for symbols declared inside extern(Windows), extern(C) and extern(Pascal)
13  *      functions is inherently ambiguous, so such inner symbols are not always correctly displayed.
14  */
15 module meta.nameof;
16 private import meta.demangle;
17
18 private {
19     // --------------------------------------------
20     // Here's the magic...
21     //
22     // Make a unique type for each identifier; but don't actually
23     // use the identifier for anything.
24     // This works because any class always needs to be fully qualified.
25     template inner(alias F)
26     {
27       class inner { }
28     }
29    
30     // If you take the .mangleof an alias parameter, you are only
31     // told that it is an alias.
32     // So, we put the type as a function parameter.
33     template outer(alias B)
34     {
35       void function( inner!(B) ) outer;
36     }
37
38     // We will get the .mangleof for a pointer to this function pointer.
39     template rawmanglednameof(alias A)
40     {
41       const char [] rawmanglednameof  =
42                  typeof(&outer!(A)).mangleof;
43     }
44
45 // If the identifier is "MyIdentifier" and this module is "QualModule"
46 // The return value will be:
47 //  "PPF"   -- because it's a pointer to a pointer to a function
48 //   "C"     -- because the first parameter is a class
49 //    "10QualModule"  -- the name of this module
50 //      "45" -- the number of characters in the remainder of the mangled name.
51 //         Note that this could be more than 2 characters, but will be at least "10".
52 //      "__T"    -- because it's a class inside a template
53 //       "5inner" -- the name of the template "inner"
54 //       "T" MyIdentifer -- Here's our prize!
55 //       "Z"  -- marks the end of the template parameters for "inner"
56 //    "5inner" -- this is the class "inner"
57 //  "Z"  -- the return value of the function is coming
58 //  "v"  -- the function returns void
59
60 // The only unknown parts above are:
61 // (1) the name of this source file
62 // (it could move or be renamed). So we do a simple case:
63 //  "C"   -- it's a class
64 //   "10QualModule" -- the name of this module
65 //   "15establishMangle" -- the name of the class
66 // and (2) the number of characters in the remainder of the name
67  
68     class establishMangle {}
69     // Get length of this (fully qualified) module name
70     const int modulemanglelength = establishMangle.mangleof.length - "C15establishMangle".length;
71
72     // Get the number of chars at the start relating to the pointer
73     const int pointerstartlength = "PPFC".length + modulemanglelength + "__T5inner".length;
74     // And the number of chars at the end
75     const int pointerendlength = "Z5innerZv".length;
76 }
77
78 // --------------------------------------------------------------
79 // Now, some functions which massage the mangled name to give something more useful.
80
81
82 /**
83  * Like .mangleof, except that it works for an alias template parameter instead of a type.
84  */
85 template manglednameof(alias A)
86 {
87     static if (rawmanglednameof!(A).length - pointerstartlength <= 100 + 1) {
88         // the length of the template argument requires 2 characters
89         const char [] manglednameof  =
90              rawmanglednameof!(A)[ pointerstartlength + 2 .. $ - pointerendlength];
91     } else
92         const char [] manglednameof  =
93              rawmanglednameof!(A)[ pointerstartlength + 3 .. $ - pointerendlength];
94 }
95
96 /**
97  * The symbol as it was declared, but including full type qualification.
98  *
99  * example: "int mymodule.myclass.myfunc(uint, class otherclass)"
100  */
101 template prettynameof(alias A)
102 {
103   const char [] prettynameof = prettyTemplateArg!(manglednameof!(A), MangledNameType.PrettyName);
104 }
105
106 /** Convert any D type to a human-readable string literal
107  *
108  * example: "int function(double, char[])"
109  */
110 template prettytypeof(A)
111 {
112   const char [] prettytypeof = demangleType!(A.mangleof, MangledNameType.PrettyName);
113 }
114
115 /**
116  * Returns the qualified name of the symbol A.
117  *
118  * This will be a sequence of identifiers, seperated by dots.
119  * eg "mymodule.myclass.myfunc"
120  * This is the same as prettynameof(), except that it doesn't include any type information.
121  */
122 template qualifiednameof(alias A)
123 {
124   const char [] qualifiednameof = prettyTemplateArg!(manglednameof!(A), MangledNameType.QualifiedName);
125 }
126
127 /**
128  * Returns the unqualified name, as a single text string.
129  *
130  * eg. "myfunc"
131  */
132 template symbolnameof(alias A)
133 {
134   const char [] symbolnameof = prettyTemplateArg!(manglednameof!(A), MangledNameType.SymbolName);
135 }
136
137 //----------------------------------------------
138 //                Unit Tests
139 //----------------------------------------------
140
141 version(testmeta) {
142
143 // Declare some structs, classes, enums, functions, and templates.
144
145 template ClassTemplate(A)
146 { 
147    class ClassTemplate {}
148 }
149
150 struct OuterClass  {
151 class SomeClass {}
152 }
153
154 alias double delegate (int, OuterClass) SomeDelegate;
155
156 template IntTemplate(int F)
157 {
158   class IntTemplate { }
159 }
160
161 template MyInt(int F)
162 {
163     const int MyIntX = F;
164 }
165
166
167 enum SomeEnum { ABC = 2 }
168 SomeEnum SomeInt;
169
170 // remove the ".d" from the end
171 const char [] THISFILE = "meta.nameof";
172
173 static assert( prettytypeof!(real) == "real");
174 static assert( prettytypeof!(OuterClass.SomeClass) == "class " ~ THISFILE ~".OuterClass.SomeClass");
175
176 // Test that it works with module names (for example, this module)
177 static assert( qualifiednameof!(meta.nameof) == "meta.nameof");
178 static assert( symbolnameof!(meta.nameof) == "nameof");
179
180 static assert( prettynameof!(SomeInt)
181     == "enum " ~ THISFILE ~ ".SomeEnum " ~ THISFILE ~ ".SomeInt");
182 static assert( qualifiednameof!(OuterClass) == THISFILE ~".OuterClass");
183 static assert( symbolnameof!(SomeInt) == "SomeInt");
184
185 static assert( prettynameof!(inner!( MyInt!(68u) ))
186     ==  "class " ~ THISFILE ~ ".inner!(" ~ THISFILE ~ ".MyInt!(uint = 68)).inner");
187 static assert( symbolnameof!(inner!( MyInt!(68u) )) ==  "inner");
188 static assert( prettynameof!(ClassTemplate!(OuterClass.SomeClass))
189     == "class "~ THISFILE ~ ".ClassTemplate!(class "~ THISFILE ~ ".OuterClass.SomeClass).ClassTemplate");
190 static assert( symbolnameof!(ClassTemplate!(OuterClass.SomeClass))  == "ClassTemplate");
191
192 // Extern(D) declarations have full type information.
193 extern int pig();
194 extern int pog;
195 static assert( prettynameof!(pig) == "int " ~ THISFILE ~ ".pig()");
196 static assert( prettynameof!(pog) == "int " ~ THISFILE ~ ".pog");
197 static assert( symbolnameof!(pig) == "pig");
198
199 // Extern(Windows) declarations contain no type information.
200 extern (Windows) {
201     extern int dog();
202     extern int dig;
203 }
204
205 static assert( prettynameof!(dog) == "dog");
206 static assert( prettynameof!(dig) == "dig");
207
208 // There are some nasty corner cases involving classes that are inside functions.
209 // Corner case #1: class inside nested function inside template
210
211 extern (Windows) {
212 template aardvark(X) {
213     int aardvark(short goon) {
214         class wolf {}
215         static assert(prettynameof!(wolf)== "class extern (Windows) int " ~ THISFILE ~ ".aardvark!(struct "
216             ~ THISFILE ~ ".OuterClass).aardvark(short).wolf");
217         static assert(qualifiednameof!(wolf)== THISFILE ~ ".aardvark.aardvark.wolf");
218         static assert( symbolnameof!(wolf) == "wolf");
219         return 3;
220         }
221     }
222 }
223
224 // This is just to ensure that the static assert actually gets executed.
225 const test_aardvark = is (aardvark!(OuterClass) == function);
226
227 // Corner case #2: template inside function. This is currently possible only with mixins.
228 template fox(B, ushort C) {
229     class fox {}
230 }
231
232 void wolf() {
233         mixin fox!(cfloat, 21);
234         static assert(prettynameof!(fox)== "class void " ~ THISFILE ~ ".wolf().fox!(cfloat, int = 21).fox");
235         static assert(qualifiednameof!(fox)== THISFILE ~ ".wolf.fox.fox");
236         static assert(symbolnameof!(fox)== "fox");
237 }
238
239 }
Note: See TracBrowser for help on using the browser.