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