1 /****************************************************************************
2 **
3 ** DQt - D bindings for the Qt Toolkit
4 **
5 ** GNU Lesser General Public License Usage
6 ** This file may be used under the terms of the GNU Lesser
7 ** General Public License version 3 as published by the Free Software
8 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
9 ** packaging of this file. Please review the following information to
10 ** ensure the GNU Lesser General Public License version 3 requirements
11 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
12 **
13 ****************************************************************************/
14 module qt.helpers;
15 
16 import std.traits;
17 import std.conv;
18 import std.array;
19 
20 private string nextCodePart(string code)
21 {
22     size_t i;
23     char c = code[0];
24     if(c == '"' || c == '\'')
25     {
26         char stringDelim = c;
27         i++;
28         while(i < code.length && code[i] != stringDelim)
29         {
30             if(code[i] == '\\')
31                 i++;
32             i++;
33         }
34     }
35     else if(c == '/' && i + 1 < code.length && code[i + 1] == '/')
36     {
37         i++;
38         i++;
39         while(i < code.length && code[i] != '\n')
40             i++;
41     }
42     else if(c == '/' && i + 1 < code.length && code[i + 1] == '*')
43     {
44         i++;
45         i++;
46         while(i < code.length)
47         {
48             if(i + 1 < code.length && code[i] == '*' && code[i+1] == '/')
49             {
50                 i++;
51                 break;
52             }
53             i++;
54         }
55     }
56     else if(c == '/' && i + 1 < code.length && code[i + 1] == '+')
57     {
58         i++;
59         i++;
60         size_t nesting = 1;
61         while(i < code.length && nesting)
62         {
63             if(i + 1 < code.length && code[i] == '+' && code[i+1] == '/')
64             {
65                 nesting--;
66                 i++;
67                 if(nesting == 0)
68                     break;
69             }
70             else if(i + 1 < code.length && code[i] == '/' && code[i+1] == '+')
71             {
72                 nesting++;
73                 i++;
74             }
75             i++;
76         }
77     }
78     i++;
79     return code[0..i];
80 }
81 
82 string interpolateMixin(string code)
83 {
84     string r = "\"";
85     for(size_t i=0; i<code.length;)
86     {
87         string part = nextCodePart(code[i..$]);
88         if(i < code.length + 1 && code[i] == '$' && code[i+1] == '(')
89         {
90             r ~= "\" ~ ";
91             i += 2;
92             size_t numParens = 1;
93             while(i < code.length)
94             {
95                 if(i + 1 < code.length && code[i] == 'q' && code[i+1] == '{')
96                 {
97                     i += 2;
98                     size_t start = i;
99                     size_t numBraces = 1;
100                     while(i < code.length)
101                     {
102                         string part2 = nextCodePart(code[i..$]);
103                         if(code[i] == '{')
104                             numBraces++;
105                         else if(code[i] == '}')
106                         {
107                             numBraces--;
108                             if(numBraces == 0)
109                             {
110                                 r ~= interpolateMixin(code[start..i]);
111                                 i++;
112                                 break;
113                             }
114                         }
115                         i += part2.length;
116                     }
117                     continue;
118                 }
119                 if(code[i] == ')')
120                 {
121                     numParens--;
122                     if(numParens == 0)
123                     {
124                         i++;
125                         break;
126                     }
127                 }
128                 else if(code[i] == '(')
129                     numParens++;
130                 string part2 = nextCodePart(code[i..$]);
131                 r ~= part2;
132                 i += part2.length;
133             }
134             r ~= " ~ \"";
135             continue;
136         }
137         else
138         {
139             foreach(char c; part)
140             {
141                 if(c == '\\')
142                     r ~= "\\\\";
143                 else if(c == '"')
144                     r ~= "\\\"";
145                 else
146                     r ~= c;
147             }
148         }
149         i += part.length;
150     }
151     r ~= "\"";
152     return r;
153 }
154 
155 string stringifyMacroParameter(string s)
156 {
157     string r = "\"";
158     bool hasWS;
159     for(size_t i=0; i<s.length;)
160     {
161         string part = nextCodePart(s[i..$]);
162         if(part[0] == ' ' || part[0] == '\t' || part[0] == '\r' || part[0] == '\n'
163             || (part.length >= 2 && part[0] == '/' && (part[1] == '/' || part[1] == '*')))
164             hasWS = true;
165         else
166         {
167             if(r.length > 1 && hasWS)
168                 r ~= " ";
169             hasWS = false;
170             foreach(char c; part)
171             {
172                 if(c == '\"')
173                     r ~= "\\\"";
174                 else if(c == '\\')
175                     r ~= "\\\\";
176                 else
177                     r ~= c;
178             }
179         }
180         i += part.length;
181     }
182     r ~= "\"";
183     return r;
184 }
185 
186 template ExternCFunc(F)// if(is(F == function))
187 {
188     static if(variadicFunctionStyle!F == Variadic.c)
189         alias ExternCFunc = extern(C) ReturnType!F function(ParameterTypeTuple!F, ...);
190     else
191         alias ExternCFunc = extern(C) ReturnType!F function(ParameterTypeTuple!F);
192 }
193 
194 template ExternCPPFunc(F)// if(is(F == function))
195 {
196     static if(variadicFunctionStyle!F == Variadic.c)
197         alias ExternCPPFunc = extern(C++) ReturnType!F function(ParameterTypeTuple!F, ...);
198     else
199         alias ExternCPPFunc = extern(C++) ReturnType!F function(ParameterTypeTuple!F);
200 }
201 
202 T static_cast(T, S)(S x)
203 {
204     static if(isCallable!T && isCallable!S)
205     {
206         static assert(functionLinkage!T == functionLinkage!S);
207     }
208     return cast(T)x;
209 }
210 
211 T reinterpret_cast(T, S)(S x)
212 {
213     return cast(T)x;
214 }
215 
216 T const_cast(T, S)(S x)
217 {
218     return cast(T)x;
219 }
220 
221 private template globalInitVarImpl(T)
222 {
223     immutable __gshared T globalInitVar = immutable(T).init;
224     shared static this()
225     {
226         (*cast(T*)&globalInitVar).rawConstructor;
227     }
228 }
229 
230 template globalInitVar(T)
231 {
232     static if(__traits(hasMember, T, "rawConstructor"))
233     {
234         version(Windows)
235             alias globalInitVar = globalInitVarImpl!T.globalInitVar;
236         else
237             static assert(0, "globalInitVar!"~T.stringof~" needs complex construction");
238     }
239     else static if(__traits(compiles, {T x;}))
240         immutable __gshared T globalInitVar/* = immutable(T).init*/;
241     else
242         static assert(false, T.stringof);
243 }
244 
245 alias defaultConstructorMangling = function string(string name)
246 {
247     version(Windows)
248         static if(size_t.sizeof == 8)
249             return "??0" ~ name ~ "@@QEAA@XZ";
250         else
251             return "??0" ~ name ~ "@@QAE@XZ";
252     else
253         return text("_ZN", name.length, name, "C1Ev");
254 };
255 
256 alias mangleWindows = function string(string mangling, string code)
257 {
258     version(Windows)
259         static if(size_t.sizeof == 8)
260             return "pragma(mangle, \"" ~ mangling ~ "\") " ~ code;
261         else
262             return "pragma(mangle, \""
263                 ~ mangling.replace("@@QEBAKPE", "@@QBEKP")
264                     .replace("@@QEBAJPE", "@@QBEJP")
265                     .replace("@@QEBAHPE", "@@QBEHP")
266                     .replace("@@QEBAPEB", "@@QBEPB")
267                     .replace("@@MEAA_NPE", "@@MAE_NP")
268                     .replace("@@MEAA_NHHPE", "@@MAE_NHHP")
269                     .replace("@@MEAA_NHPE", "@@MAE_NHP")
270                     .replace("@@UEAA_NAE", "@@UAE_NA")
271                     .replace("@@MEBA_NPE", "@@MBE_NP")
272                     .replace("@@UEAAXPEA", "@@UAEXPA")
273                     .replace("@@MEAAXPE", "@@MAEXP")
274                     .replace("@@UEAA_NPE", "@@UAE_NP")
275                     .replace("@@UEAA", "@@UAE")
276                     .replace("@@QEBA", "@@QBE")
277                     .replace("@@MEBA", "@@MBE")
278                     .replace("@@0PEAV1@EA", "@@0PAV1@A")
279                     .replace("@@PEBV1@PEAPEAX01PE", "@@PBV1@PAPAX01P")
280                     .replace("@@PEBHPE", "@@PBHP")
281                     .replace("@@MEAA_NAE", "@@MAE_NA")
282                     .replace("@@MEAA", "@@MAE")
283                     .replace("@@PEAXPE", "@@PAXP")
284                     .replace("@@UEBA_NPE", "@@UBE_NP")
285                     .replace("@@UEBA", "@@UBE")
286                     .replace("@@AEBV", "@@ABV")
287                     .replace("@@HPE", "@@HP")
288                     .replace("@@EEAAXPE", "@@EAEXP")
289                     .replace("@@PE", "@@P")
290                     .replace("@@HHAE", "@@HHA")
291                 ~ "\") " ~ code;
292     else
293         return code;
294 };
295 
296 alias mangleOpLess = function string(string name)
297 {
298     version(Windows)
299         static if(size_t.sizeof == 8)
300             return "??M" ~ name ~ "@@UEBA_NAEBV0@@Z";
301         else
302             return "??M" ~ name ~ "@@UBE_NABV0@@Z";
303     else
304         return text("_ZNK", name.length, name, "ltERKS_");
305 };
306 
307 // Workaround for https://issues.dlang.org/show_bug.cgi?id=19660, which can be removed later.
308 version(Windows)
309     enum exportOnWindows = " export ";
310 else
311     enum exportOnWindows = "";
312 
313 // TODO: can be removed when a released dmd contains imported.
314 static if(!__traits(compiles, imported))
315 template imported(string moduleName)
316 {
317     mixin("import imported = " ~ moduleName ~ ";");
318 }