1 /** 2 C++ template translations 3 */ 4 module dpp.translation.template_; 5 6 7 import dpp.from; 8 9 10 string templateParamList(R)(R range) { 11 import std.array: join; 12 return `(` ~ () @trusted { return range.join(", "); }() ~ `)`; 13 } 14 15 string templateSpelling(R)(in from!"clang".Cursor cursor, R range) { 16 return cursor.spelling ~ templateParamList(range); 17 } 18 19 20 // Deal with full and partial template specialisations 21 // returns a range of string 22 package string[] translateSpecialisedTemplateParams( 23 in from!"clang".Cursor cursor, 24 ref from!"dpp.runtime.context".Context context) 25 @safe 26 { 27 import dpp.translation.type: translate, templateArgumentKind, TemplateArgumentKind, 28 translateTemplateParamSpecialisation; 29 import clang: Type; 30 import std.algorithm: map; 31 import std.range: iota; 32 import std.array: array, join; 33 import std.typecons: No; 34 35 assert(cursor.type.numTemplateArguments != -1); 36 37 if(isFromVariadicTemplate(cursor)) 38 return translateSpecialisedTemplateParamsVariadic(cursor, context); 39 40 // get the original list of template parameters and translate them 41 // e.g. template<bool, bool, typename> -> (bool V0, bool V1, T) 42 const translatedTemplateParams = () @trusted { 43 return translateTemplateParams(cursor, context, No.defaults) 44 .array; 45 }(); 46 47 // e.g. for template<> struct foo<false, true, int32_t> 48 // 0 -> `bool V0: false`, 1 -> `bool V1: true`, 2 -> `T0: int` 49 string element(in Type templateArgType, in int index) { 50 51 import dpp.translation.exception: UntranslatableException; 52 53 if(templateArgType.isConstQualified) 54 throw new UntranslatableException("Cannot translate const template type parameter specialisation"); 55 56 if(templateArgType.isVolatileQualified) 57 throw new UntranslatableException("Cannot translate volatile template type parameter specialisation"); 58 59 string ret = translatedTemplateParams[index]; // e.g. `T`, `bool V0` 60 const maybeSpecialisation = translateTemplateParamSpecialisation(cursor.type, templateArgType, index, context); 61 const templateArgKind = templateArgumentKind(templateArgType); 62 63 with(TemplateArgumentKind) { 64 const isSpecialised = 65 templateArgKind == SpecialisedType || 66 (templateArgKind == Value && isValueOfType(cursor, context, index, maybeSpecialisation)); 67 68 if(isSpecialised) ret ~= ": " ~ maybeSpecialisation; 69 } 70 71 return ret; 72 } 73 74 return () @trusted { 75 return 76 cursor.type.numTemplateArguments 77 .iota 78 .map!(i => element(cursor.type.typeTemplateArgument(i), i)) 79 .array 80 ; 81 }(); 82 } 83 84 // FIXME: refactor 85 private auto translateSpecialisedTemplateParamsVariadic(in from!"clang".Cursor cursor, 86 ref from!"dpp.runtime.context".Context context) 87 @safe 88 { 89 import dpp.translation.type: translate; 90 91 assert(isFromVariadicTemplate(cursor)); 92 assert(cursor.type.numTemplateArguments != -1); 93 94 string[] ret; 95 96 foreach(i; 0 .. cursor.type.numTemplateArguments) { 97 ret ~= translate(cursor.type.typeTemplateArgument(i), context); 98 } 99 100 return ret; 101 } 102 103 // In the case cursor is a partial or full template specialisation, 104 // check to see if `maybeSpecialisation` can be converted to the 105 // indexth template parater of the cursor's original template. 106 // If it can, then it's a value of that type. 107 private bool isValueOfType( 108 in from!"clang".Cursor cursor, 109 ref from!"dpp.runtime.context".Context context, 110 in int index, 111 in string maybeSpecialisation, 112 ) 113 @safe 114 { 115 import dpp.translation.type: translate; 116 import std.array: array; 117 import std.exception: collectException; 118 import std.conv: to; 119 import core.stdc.config: c_long, c_ulong; 120 121 // the original template cursor (no specialisations) 122 const templateCursor = cursor.specializedCursorTemplate; 123 // the type of the indexth template parameter 124 const templateParamCursor = () @trusted { return templateCursor.templateParams.array[index]; }(); 125 // the D translation of that type 126 const dtype = translate(templateParamCursor.type, context); 127 128 Exception conversionException; 129 130 void tryConvert(T)() { 131 conversionException = collectException(maybeSpecialisation.to!T); 132 } 133 134 switch(dtype) { 135 default: throw new Exception("isValueOfType cannot handle type `" ~ dtype ~ "`"); 136 case "bool": tryConvert!bool; break; 137 case "char": tryConvert!char; break; 138 case "wchar": tryConvert!wchar; break; 139 case "dchar": tryConvert!dchar; break; 140 case "short": tryConvert!short; break; 141 case "ushort": tryConvert!ushort; break; 142 case "int": tryConvert!int; break; 143 case "uint": tryConvert!uint; break; 144 case "long": tryConvert!long; break; 145 case "ulong": tryConvert!ulong; break; 146 case "c_ulong": tryConvert!c_ulong; break; 147 case "c_long": tryConvert!c_long; break; 148 } 149 150 return conversionException is null; 151 } 152 153 154 // Translates a C++ template parameter (value or type) to a D declaration 155 // e.g. `template<typename, bool, typename>` -> ["T0", "bool V0", "T1"] 156 // Returns a range of string 157 package auto translateTemplateParams( 158 in from!"clang".Cursor cursor, 159 ref from!"dpp.runtime.context".Context context, 160 from!"std.typecons".Flag!"defaults" defaults = from!"std.typecons".Yes.defaults, 161 ) @safe 162 { 163 import dpp.translation.type: translate, translateString; 164 import clang: Cursor; 165 import std.conv: text; 166 import std.algorithm: map, filter, countUntil; 167 import std.array: array; 168 import std.range: enumerate; 169 170 int templateParamIndex; // used to generate names when there are none 171 172 string newTemplateParamName() { 173 // FIXME 174 // the naming convention is to match what libclang gives, but there's no 175 // guarantee that it'll always match. 176 return text("type_parameter_0_", templateParamIndex++); 177 } 178 179 string translateTemplateParam(in Cursor cursor) { 180 import dpp.translation.type: translate; 181 import dpp.translation.tokens: translateTokens; 182 import clang: Token; 183 184 // The template parameter might be a value (bool, int, etc.) 185 // or a type. If it's a value we get its type here. 186 const maybeType = cursor.kind == Cursor.Kind.TemplateTypeParameter 187 ? "" // a type doesn't have a type 188 : translate(cursor.type, context) ~ " "; 189 190 // D requires template parameters to have names 191 const spelling = cursor.spelling == "" ? newTemplateParamName : cursor.spelling; 192 193 // There's no direct way to extract default template parameters from libclang 194 // so we search for something like `T = Foo` in the tokens 195 const equalIndex = cursor.tokens.countUntil!(t => t.kind == Token.Kind.Punctuation && t.spelling == "="); 196 197 const maybeDefault = equalIndex == -1 || !defaults 198 ? "" 199 : cursor.tokens[equalIndex .. $] 200 .array 201 .translateTokens 202 ; 203 204 // e.g. "bool param", "T0" 205 return maybeType ~ spelling ~ maybeDefault; 206 } 207 208 auto templateParams = cursor.templateParams; 209 auto translated = templateParams.map!translateTemplateParam.array; 210 211 // might need to be a variadic parameter 212 string maybeVariadic(in long index, in string name) { 213 return cursor.isVariadicTemplate && index == translated.length -1 214 // If it's variadic, come up with a new name in case it's variadic 215 // values. D doesn't really care. 216 ? newTemplateParamName ~ "..." 217 : name; 218 } 219 220 return () @trusted { 221 return translated 222 .enumerate 223 .map!(a => maybeVariadic(a[0], a[1])) 224 ; 225 }(); 226 } 227 228 // If the original template is variadic 229 private bool isFromVariadicTemplate(in from!"clang".Cursor cursor) @safe { 230 return isVariadicTemplate(cursor.specializedCursorTemplate); 231 } 232 233 private bool isVariadicTemplate(in from!"clang".Cursor cursor) @safe { 234 import clang: Cursor, Token; 235 import std.array: array; 236 import std.algorithm: canFind, countUntil; 237 238 const templateParamChildren = () @trusted { return cursor.templateParams.array; }(); 239 240 // There might be a "..." token inside the body of the struct/class, and we don't want to 241 // look at that. So instead we stop looking at tokens when the struct/class definition begins. 242 const openCurlyBracketIndex = cursor.tokens.countUntil!(a => a.kind == 243 Token.Kind.Punctuation && a.spelling == "{"); 244 const tokens = openCurlyBracketIndex == -1 ? cursor.tokens : cursor.tokens[0 .. openCurlyBracketIndex]; 245 246 return 247 templateParamChildren.length > 0 && 248 (templateParamChildren[$-1].kind == Cursor.Kind.TemplateTypeParameter || 249 templateParamChildren[$-1].kind == Cursor.Kind.NonTypeTemplateParameter) && 250 tokens.canFind(Token(Token.Kind.Punctuation, "...")); 251 }