1 /+
2 +            Copyright 2022 – 2024 Aya Partridge
3 + Distributed under the Boost Software License, Version 1.0.
4 +     (See accompanying file LICENSE_1_0.txt or copy at
5 +           http://www.boost.org/LICENSE_1_0.txt)
6 +/
7 module bindbc.common.codegen;
8 
9 import bindbc.common.versions;
10 
11 ///TODO: split into codegen.fn, codegen.enum_, and codegen.util
12 
13 /**
14 Data for function bindings.
15 
16 Only the locations of the first 3 fields (`retn`, `iden`, and `params`) should be
17 relied on. Every other field should always be written/read by using its identifier.
18 Example:
19 ```d
20 FnBbind one = {q{void}, q{someCppFn}, q{int si, uint ui}, ext: `C++`}; //OK
21 FnBbind two = {q{void}, q{someCppFn}, q{uint ui, int si}, `C++`}; //`ext` was not specified by identifier; might break in the future!
22 ```
23 
24 Never place whitespace around strings provided to FnBind, as it may cause incorrect binding generation.
25 */
26 struct FnBind{
27 	/**
28 	Return type.
29 	Passing `const char*` instead of '`const(char)*` to a member function will
30 	erroneously cause the function to be `const`. You should use `attrib` for this instead.
31 	Must be void for constructors and destructors.
32 	Must be non-null, except where it is ignored.
33 	*/
34 	string retn;
35 	
36 	/**
37 	The identifier of the function in the library you are binding.
38 	Use `this` when binding a constructor, `~this` when binding a destructor.
39 	Must be non-null.
40 	*/
41 	string iden;
42 	
43 	/**
44 	Comma-separated named function parameters.
45 	Constructors can have no parameters. Constructors with all-default parameters will not currently work!
46 	
47 	Default: no parameters
48 	*/
49 	string params = "";
50 	
51 	/**
52 	Specifies which `extern` linkage to use.
53 	| Support status    | Input           | Generated code          |
54 	|-------------------|-----------------|-------------------------|
55 	| Fully supported   | `C`             | `extern(C)`             |
56 	| Mostly supported  | `C++`           | `extern(C++)`           |
57 	| Mostly supported  | `C++. "name"`   | `extern(C++, "name")`   |
58 	| Mostly supported  | `C++. "a", "b"` | `extern(C++, "a", "b")` |
59 	| Not yet supported | `Objective-C`   | `extern(Objective-C)`   |
60 	
61 	Default: C
62 	*/
63 	string ext = `C`;
64 	
65 	/**
66 	Whether or not a member function is static. Should not be `true` for non-member functions.
67 	*/
68 	bool isStatic = false;
69 	
70 	/**
71 	Optional: Function attributes. `pure` will be removed from dynamic bindings.
72 	*/
73 	string attr;
74 	
75 	/**
76 	Optional: If populated, `iden` will be private, and a public alias with the name `pubIden` will be created.
77 	*/
78 	string pubIden;
79 	
80 	/**
81 	Optional: Member function attributes.
82 	*/
83 	string memAttr;
84 	
85 	/**
86 	Optional:
87 	Anything to be placed immediately before the public version of the function, like `deprecated`, etc.
88 	*/
89 	string pfix;
90 	
91 	/**
92 	Optional: A list of identifiers to be aliases of the function.
93 	*/
94 	string[] aliases;
95 }
96 
97 /**
98 Returns: A mixin string with code for creating function bindings.
99 
100 Params:
101 	staticBinding = Whether the functions generate static bindings, or otherwise dynamic ones.
102 	version = The version of BindBC-Common that your code is written for.
103 */
104 enum makeFnBindFns = (bool staticBinding, Version version_=Version(0,1,1)) nothrow pure @safe{
105 	if(version_ >= Version(0,1,1) && version_ <= bindBCCommonVersion){
106 		return
107 "alias joinFnBinds = bindbc.common.codegen.joinFnBinds!" ~ (staticBinding ? "true" : "false") ~ ";
108 alias FnBind = bindbc.common.codegen.FnBind;";
109 	}else assert(0, "Invalid version supplied.");
110 };
111 
112 /*
113 Regex sub:
114 ^[ \t]*([A-Za-z0-9_()*\[\]]+) (\w+) ?\(([A-Za-z0-9_()*, .=\[\]]*)\);
115 with:
116 \t\t{q{$1}, q{$2}, q{$3}},
117 */
118 enum joinFnBinds(bool staticBinding) = (FnBind[] fns, string membersWithFns=null) nothrow pure @safe{
119 	string ret;
120 	
121 	static if(staticBinding){
122 		ret ~= "nothrow @nogc{\n";
123 		foreach(fn; fns){
124 			string pfix = (fn.pfix.length ? fn.pfix~" " : "") ~ (fn.pubIden.length ? "package " : "") ~ "extern("~fn.ext~") ";
125 			if(fn.isStatic){
126 				pfix ~= "static ";
127 			}
128 			string attr = (fn.attr.length ? " "~fn.attr : "");
129 			string memAttr = (fn.memAttr.length ? " "~fn.memAttr : "");
130 			
131 			if(fn.iden == "this"){
132 				if(fn.params.length){
133 					ret ~= "\t" ~ pfix ~ "this("~fn.params~")" ~ memAttr ~ attr ~ ";\n";
134 				}else{
135 					ret ~= "\t\timport bindbc.common.codegen: mangleofCppDefaultCtor;\n";
136 					ret ~= "\t" ~ pfix ~ "pragma(mangle, [__traits(getCppNamespaces, typeof(this)), __traits(identifier, typeof(this))].mangleofCppDefaultCtor()) this(int _)" ~ (fn.memAttr.length ? " "~fn.memAttr : "") ~ ";\n";
137 				}
138 			}else{
139 				ret ~= "\t" ~ pfix ~ fn.retn ~ " " ~ fn.iden ~ "("~fn.params~")" ~ memAttr ~ attr ~ ";\n";
140 			}
141 			
142 			if(fn.pubIden.length){
143 				ret ~= "\talias " ~ fn.pubIden ~ " = " ~ fn.iden ~ ";\n";
144 			}
145 			foreach(alias_; fn.aliases){
146 				ret ~= "\talias " ~ alias_ ~ " = " ~ fn.iden ~ ";\n";
147 			}
148 		}
149 		ret ~= "}";
150 	}else{
151 		string types = "private nothrow @nogc{\n";
152 		string ptrs = "__gshared nothrow @nogc{\n";
153 		string dyn =
154 `import bindbc.loader: SharedLib, bindSymbol;
155 static void bindModuleSymbols(SharedLib lib) nothrow @nogc{
156 	alias here = ` ~ makeOuterScope() ~ `;`;
157 		
158 		//Helps us see if functions have overloads.
159 		uint[string] usedIdens;
160 		foreach(fn; fns){
161 			if(fn.ext != "C"){ //Could this function have overloads?
162 				if(auto num = fn.iden in usedIdens){
163 					(*num) = 1;
164 				}else{
165 					usedIdens[fn.iden] = 0;
166 				}
167 			}
168 		}
169 		
170 		foreach(fn; fns){
171 			//Are there overloads of this function?
172 			uint overload = 0;
173 			if(fn.ext != "C"){
174 				if(auto num = fn.iden in usedIdens){
175 					overload = *num;
176 					(*num)++;
177 				}
178 			}
179 			
180 			string ext = "extern("~fn.ext~") ";
181 			string pfix = (fn.pfix.length ? fn.pfix~" " : "") ~ (fn.pubIden.length ? "package " : "") ~ ext;
182 			if(fn.isStatic){
183 				pfix ~= "static ";
184 			}
185 			
186 			string memAttr = (fn.memAttr.length ? " "~fn.memAttr : "");
187 			string attr = (fn.attr.length ? " "~fn.attr : "");
188 			//remove `pure` for function wrapper
189 			string wrapAttr = attr;
190 			if(fn.attr.length >= 4){
191 				foreach(ind; 0..fn.attr.length-3){
192 					if(fn.attr[ind..ind+4] == q{pure}){
193 						wrapAttr = " "~fn.attr[0..ind] ~ fn.attr[ind+4..$];
194 						break;
195 					}
196 				}
197 			}
198 			
199 			//Is this a variadic function?
200 			bool variadic = fn.params.length > 3 && fn.params[$-3..$] == "...";
201 			//Is this a member function?
202 			bool memberFn = membersWithFns is null && fn.ext != "C";
203 			
204 			//`iden` is either the identifier for this function, or an internal name if the function identifier is special (e.g. `this`)
205 			string iden = (){
206 				switch(fn.iden){
207 					case "this": return "__ctor";
208 					case "~this": return "__dtor";
209 					default: return fn.iden;
210 				}
211 			}();
212 			//Whether a special identifier (`__ctor`/`__dtor`) is in-use.
213 			//bool specialIden = iden != fn.iden;
214 			
215 			//The function pointer's parameters, and how to call it from the public function.
216 			string ptrParams = fn.params;
217 			string ptrCall = "__traits(parameters)";
218 			if(memberFn && !fn.isStatic){
219 				if(fn.params.length){
220 					ptrParams = "ref inout(typeof(this)) this_, " ~ ptrParams;
221 					ptrCall = "this, " ~ ptrCall;
222 				}else{
223 					ptrParams = "ref inout(typeof(this)) this_";
224 					ptrCall = "this";
225 				}
226 				ptrParams = (fn.memAttr.length ? fn.memAttr~" " : "") ~ ptrParams;
227 			}
228 			
229 			string ptrIden = "_"~iden;
230 			string typeIden = "_p"~iden;
231 			string typeExt = "extern("~((fn.ext.length > 3 && fn.ext[0..3] == "C++") ? "C++" : fn.ext)~") ";
232 			
233 			if(overload){
234 				string overloadStr = overload.toStrCT();
235 				ptrIden ~= overloadStr;
236 				typeIden ~= overloadStr;
237 				//if(!specialIden){
238 					//iden ~= "_"~overloadStr;
239 					//pfix = "package " ~ pfix;
240 				//}
241 			}else if(variadic){
242 				types ~= "\talias " ~ typeIden ~ " = " ~ typeExt ~ fn.retn ~ " function(" ~ ptrParams ~ ")" ~ attr ~ ";\n";
243 				ptrs ~= "\t" ~ typeIden ~ " " ~ iden ~ ";\n";
244 			}
245 			
246 			ptrCall = ptrIden ~ "("~ptrCall~");";
247 			
248 			if(overload){
249 				dyn ~= `
250 	{
251 		alias FnCmp = void(`~fn.params~`);
252 		static if(is(FnCmp ArgsCmp == function)){
253 			static foreach(Fn; __traits(getOverloads, here, "` ~ iden ~ `")){{
254 				static if(is(typeof(Fn) Args == function)){
255 					static if(is(Args == ArgsCmp)){
256 						lib.bindSymbol(cast(void**)&` ~ ptrIden ~ `, Fn.mangleof);
257 					}
258 				}else static assert(0);
259 			}}
260 		}else static assert(0);
261 	}`;
262 			}else if(variadic){
263 				if((fn.ext.length >= 3 && fn.ext[0..3] == "C++") || fn.ext == "Objective-C"){
264 					//Variadic functions always act like they're overloaded and return no mangled parameters. :(
265 					dyn ~= `
266 	{
267 		alias FnCmp = void(`~fn.params~`);
268 		static if(is(FnCmp ArgsCmp == function)){
269 			static foreach(Fn; __traits(getOverloads, here, "` ~ iden ~ `")){{
270 				static if(is(typeof(Fn) Args == function)){
271 					static if(is(Args == ArgsCmp)){
272 						lib.bindSymbol(cast(void**)&` ~ iden ~ `, Fn.mangleof);
273 					}
274 				}else static assert(0);
275 			}}
276 		}else static assert(0);
277 	}`;
278 				}else{
279 					dyn ~= `
280 	lib.bindSymbol(cast(void**)&` ~ iden ~ `, "` ~ iden ~ `");`;
281 				}
282 				continue;
283 				
284 			}else{
285 				dyn ~= `
286 	lib.bindSymbol(cast(void**)&` ~ ptrIden ~ `, here.` ~ iden ~ `.mangleof);`;
287 				
288 			}
289 			
290 			//We separate the type so that the compiler doesn't extern our function pointer; only makes the function pointer type extern.
291 			types ~= "\talias " ~ typeIden ~ " = " ~ typeExt ~ fn.retn ~ " function(" ~ ptrParams ~ ")" ~ attr ~ ";\n";
292 			//Private function pointer declaration.
293 			ptrs ~= "\tpackage " ~ typeIden ~ " " ~ ptrIden ~ ";\n";
294 			
295 			//Function wrapper delcaration.
296 			string fnParams = fn.params;
297 			if(variadic && overload){
298 				fnParams = "T...)("~fnParams[0..$-3]~"T _variadics";
299 			}
300 			if(fn.iden == "this"){ //Constructor.
301 				if(fn.params.length){
302 					//TODO: Check if the parameters are all defaults here :(
303 					ptrs ~= "\t" ~ pfix ~ "this("~fnParams~")" ~ memAttr ~ wrapAttr ~ "{ " ~ ptrCall ~ " }\n";
304 				}else if(fn.ext.length >= 3 && fn.ext[0..3] == "C++"){ //Default constructor; must have no parameters.
305 					ptrs ~= "\timport bindbc.common.codegen: mangleofCppDefaultCtor;\n";
306 					ptrs ~= "\t" ~ pfix ~ "pragma(mangle, [__traits(getCppNamespaces, typeof(this)), __traits(identifier, typeof(this))].mangleofCppDefaultCtor()) this(int _)" ~ memAttr ~ attr ~ "{ " ~ ptrCall ~ " }\n";
307 				}else assert(0, "Default constructor mangling for extern("~fn.ext~") is unknown.");
308 			}else if(fn.iden == "~this"){ //Destructor.
309 				ptrs ~= "\t" ~ pfix ~ "~this("~fnParams~")" ~ memAttr ~ wrapAttr ~ "{ " ~ ptrCall ~ " }\n";
310 			}else{
311 				if(fn.retn != "void"){
312 					ptrCall = "return " ~ ptrCall;
313 				}
314 				ptrs ~= "\t" ~ pfix ~ fn.retn ~ " " ~ fn.iden ~ "("~fnParams~")" ~ memAttr ~ wrapAttr ~ "{ " ~ ptrCall ~ " }\n";
315 			}
316 			
317 			if(fn.pubIden.length){
318 				ptrs ~= "\talias " ~ fn.pubIden ~ " = " ~ iden ~ ";\n";
319 			}
320 			foreach(aliasIden; fn.aliases){
321 				ptrs ~= "\talias " ~ aliasIden ~ " = " ~ iden ~ ";"; 
322 			}
323 		}
324 		
325 		if(membersWithFns.length){
326 			//This method is easier to rewrite to use __traits(allMembers) later
327 			dyn ~= `
328 	alias AliasSeq(T...) = T;
329 	static foreach(item; AliasSeq!(` ~ membersWithFns ~ `)){ 
330 		static assert((is(item == struct) || is(item == class)) && (__traits(getLinkage, item) == "C++" || __traits(getLinkage, item) == "Objective-C") && __traits(hasMember, item, "bindModuleSymbols"));
331 		mixin(__traits(fullyQualifiedName, item),".bindModuleSymbols(lib);");
332 	}`;
333 		}
334 		
335 		ret = types ~ "}\n" ~ ptrs ~ "}\n\n" ~ dyn ~ "\n}";
336 	}
337 	return ret;
338 };
339 
340 /*//TODO: some time after __traits(allMembers) is fixed, remove "membersWithFns" and check over each type it returns automatically! :)
341 enum joinFnBinds = (string[][] list, string membersWithFns="") nothrow pure @safe{;
342 	static if(staticBinding){
343 		string joined = "{";
344 	}else{ 
345 	string joined = "\n@nogc nothrow __gshared{";;
346 	}
347 	foreach(item; list){
348 		joined ~= item[0];
349 	}
350 	joined ~= "\n}";;
351 	
352 	static if(!staticBinding){
353 	joined ~= "\n\nimport bindbc.loader: SharedLib, bindSymbol;\nstatic void bindModuleSymbols(SharedLib lib) @nogc nothrow{";
354 	foreach(item; list){
355 		if(item[2] == "this") item[2] = "__ctor";
356 		if(item[3].length > 0){
357 			joined ~= "
358 	static if("~((item[3].length > 3 && item[3][$-3..$] == "...") ? "true" : "false")~" || __traits(getOverloads, "~outerScope~", \""~item[2]~"\").length > 0){
359 		static foreach(Fn; __traits(getOverloads, "~outerScope~", \""~item[2]~"\")){
360 			{
361 				void Fn2("~item[3]~"){}
362 				static if(is(typeof(Fn) Args1 == __parameters) && is(typeof(Fn2) Args2 == __parameters)){
363 					static if(is(Args1 == Args2)){
364 						lib.bindSymbol(cast(void**)&"~item[1]~", Fn.mangleof);
365 					}
366 				}else static assert(0);
367 			}
368 		}
369 	}else{
370 		lib.bindSymbol(cast(void**)&"~item[1]~", "~outerScope~"."~item[2]~".mangleof);
371 	}";
372 		}else{
373 			joined ~= "\n\tlib.bindSymbol(cast(void**)&"~item[1]~", "~outerScope~"."~item[2]~".mangleof);";
374 		}
375 	}
376 	if(membersWithFns.length > 0){
377 		joined ~= q{
378 		alias AliasSeq(T...) = T;
379 		static foreach(member; AliasSeq!(}~membersWithFns~q{)){
380 			static if( (is(member == struct) || is(member == class) || is(member == struct)) && (__traits(getLinkage, member) == "C++" || __traits(getLinkage, member) == "Objective-C") ){
381 				mixin(member,".bindModuleSymbols(lib);");
382 			}
383 		}
384 		};
385 	}
386 	joined ~= "\n}";;
387 	}
388 	
389 	return joined;
390 };*/
391 
392 ///For internal use only.
393 enum makeIsMember = () nothrow pure @safe{
394 	return "__traits(compiles, typeof(this))";
395 };
396 unittest{
397 	static assert(mixin(makeIsMember()) == false);
398 	struct XXX{
399 		static assert(mixin(makeIsMember()) == true);
400 	}
401 }
402 
403 ///For internal use only.
404 package enum makeOuterScope = () nothrow pure @safe{
405 	return
406 "mixin((string mod=__MODULE__){
407 	static if(__traits(compiles, typeof(this))) return __traits(identifier, typeof(this));
408 	else return mod;
409 }())";
410 };
411 unittest{
412 	static assert(__traits(isSame, mixin(makeOuterScope()), bindbc.common.codegen));
413 	struct XXX{
414 		static assert(is(mixin(makeOuterScope()) == XXX));
415 	}
416 }
417 
418 ///For internal use only.
419 enum mangleofCppDefaultCtor = (string[] syms) nothrow pure @safe{
420 	static if((){
421 		version(CppRuntime_Clang)    return true;
422 		else version(CppRuntime_Gcc) return true;
423 		else version(Android)        return true;
424 		else version(WebAssembly)    return true;
425 		else return false;
426 	}()){
427 		string ret = "_ZN";
428 		foreach(sym; syms){
429 			ret ~= toStrCT(sym.length) ~ sym;
430 		}
431 		return  ret ~ "C1Ev";
432 	}else static if((){
433 		version(CppRuntime_Microsoft)        return true;
434 		else version(CppRuntime_DigitalMars) return true;
435 		else return false;
436 	}()){
437 		string ret = "??0";
438 		foreach(sym; syms){
439 			ret ~= sym ~ "@";
440 		}
441 		version(D_X32){
442 			return ret ~ "@QAE@XZ";
443 		}else{
444 			return ret ~ "@QEAA@XZ";
445 		}
446 	}else static assert(0, "Unknown runtime, not sure what mangling to use. Please check how your compiler mangles C++ struct constructors and add a case for it to `bindbc.common.codegen.mangleofCppDefaultCtor`.");
447 };
448 unittest{
449 	static if((){
450 		version(CppRuntime_Clang)    return true;
451 		else version(CppRuntime_Gcc) return true;
452 		else return false;
453 	}()){
454 		static assert(["ImGuiListClipper"].mangleofCppDefaultCtor() == "_ZN16ImGuiListClipperC1Ev");
455 		static assert(["bgfx", "Init"].mangleofCppDefaultCtor() == "_ZN4bgfx4InitC1Ev");
456 	}else static if((){
457 		version(CppRuntime_Microsoft)        return true;
458 		else version(CppRuntime_DigitalMars) return true;
459 		else return false;
460 	}()){
461 		version(D_X32){
462 			static assert(["ImGuiListClipper"].mangleofCppDefaultCtor() == "??0ImGuiListClipper@@QAE@XZ");
463 			static assert(["bgfx", "Init"].mangleofCppDefaultCtor() == "??0Init@bgfx@@QAE@XZ");
464 		}else{
465 			static assert(["ImGuiListClipper"].mangleofCppDefaultCtor() == "??0ImGuiListClipper@@QEAA@XZ");
466 			static assert(["bgfx", "Init"].mangleofCppDefaultCtor() == "??0Init@bgfx@@QEAA@XZ");
467 		}
468 	}else static assert(0);
469 }
470 
471 ///For internal use only.
472 size_t badHash(string s) nothrow pure @safe{
473 	size_t ret = 0;
474 	for(size_t i = 0; i < s.length; i += 2){
475 		ret += s[i]; //yes, this is a *really* bad hashing algorithm!
476 	}
477 	return ret * s.length;
478 }
479 unittest{
480 	static assert(badHash("const(void)* ptr") != badHash("char alpha"));
481 	static assert(badHash("const(void)* ptr") != badHash("const(void)* chr"));
482 	static assert(badHash("ubyte id, size_t ind, char* buf") != badHash("wchar utf16, char* utf8"));
483 	static assert(badHash("dchar utf16, char* utf8, bool er") != badHash("wchar utf16, char* utf8, bool er"));
484 }
485 
486 ///For internal use only.
487 enum toStrCT = (size_t val) nothrow pure @safe{
488 	enum base = 10;
489 	
490 	if(!val) return "0";
491 
492 	string ret;
493 	while(val > 0){
494 		ret = cast(char)('0' + (val % base)) ~ ret;
495 		val /= base;
496 	}
497 	return ret;
498 };
499 unittest{
500 	static assert(0.toStrCT() == "0");
501 	static assert(4.toStrCT() == "4");
502 	static assert(120.toStrCT() == "120");
503 	static assert(589_000.toStrCT() == "589000");
504 	static assert(9_396_460_865_079_328.toStrCT() == "9396460865079328");
505 	static assert(1_046_259_925_731_862_221.toStrCT() == "1046259925731862221");
506 }
507 
508 /**
509 Data for a member of an enum binding.
510 
511 Only the locations of the first 2 fields (`iden`, and `val`) should be
512 relied on. Every other field should always be written/read by using its identifier.
513 */
514 struct EnumMember{
515 	EnumIden iden;
516 	
517 	/**
518 	Optional: The value of both versions of the enum member.
519 	*/
520 	string val;
521 	
522 	/**
523 	Optional: A list of identifiers to be aliases of the member.
524 	*/
525 	EnumIden[] aliases;
526 }
527 
528 struct EnumIden{
529 	/**
530 	The identifier for a D-style version of this enum member.
531 	For example: `mouseButtonDown` for an enum named `SDLEvent`
532 	Must be non-null, except in `aliases`.
533 	*/
534 	string d;
535 	
536 	/**
537 	The identifier for a C-style version of this enum member.
538 	For example: `SDL_EVENT_MOUSE_BUTTON_DOWN`
539 	Must be non-null, except in `aliases`.
540 	*/
541 	string c;
542 }
543 
544 /**
545 Returns: A mixin string with code for creating enum bindings.
546 
547 Params:
548 	cStyle = Whether or not to generate C-style 
549 	version = The version of BindBC-Common that your code is written for.
550 */
551 enum makeEnumBindFns = (bool cStyle, bool dStyle) nothrow pure @safe{
552 	return
553 "alias makeEnumBind = bindbc.common.codegen.makeEnumBind!(" ~
554 	(cStyle ? "true" : "false") ~ ", " ~
555 	(dStyle ? "true" : "false") ~
556 ");
557 alias EnumMember = bindbc.common.codegen.EnumMember;
558 alias EnumIden = bindbc.common.codegen.EnumIden;";
559 };
560 
561 /**
562 Create an enum with optional D-style and C-style versions.
563 
564 Only the first two parameters (`dIden`, and `baseType`) should be
565 supplied sequentially. The rest should be called using named parameters.
566 
567 Params:
568 	dIden = An identifer for the D-style version of this enum. (i.e. `enum TypeIdentifier{`)
569 	baseType = (Optional) A base type for both versions of the enum. (i.e. `enum MyEnum: BaseType{`)
570 	aliases = (Optional) Aliases to the D-style version of this enum.
571 	members = Each member of the enum.
572 */
573 enum makeEnumBind(bool cStyle, bool dStyle) = (string dIden, string baseType=null, string[] aliases=null, EnumMember[] members, string fullModule=__MODULE__) nothrow pure @safe{
574 	string dRet;
575 	static if(!dStyle)
576 		dRet ~= "package ";
577 	dRet ~= "enum " ~ dIden;
578 	if(baseType)
579 		dRet ~= ": " ~ baseType;
580 	dRet ~= "{\n";
581 	
582 	string cRet;
583 	static if(cStyle){
584 		cRet ~= "enum: " ~ fullModule~"."~dIden ~ "{\n";
585 	}
586 	
587 	foreach(member; members){
588 		dRet ~= "\t" ~ member.iden.d;
589 		if(member.val)
590 			dRet ~= " = " ~ member.val;
591 		dRet ~= ",\n";
592 		
593 		static if(cStyle){
594 			if(member.iden.c){
595 				cRet ~= "\t" ~ member.iden.c ~ " = " ~ fullModule~"."~dIden~"."~member.iden.d;
596 				cRet ~= ",\n";
597 			}
598 		}
599 		
600 		foreach(aliasIden; member.aliases){
601 			if(aliasIden.d)
602 				dRet ~= "\t" ~ aliasIden.d ~ " = " ~ member.iden.d ~ ",\n";
603 			
604 			static if(cStyle){
605 				if(aliasIden.c)
606 					cRet ~= "\t" ~ aliasIden.c ~ " = " ~ member.iden.c ~ ",\n";
607 			}
608 		}
609 	}
610 	
611 	dRet ~= "}\n";
612 	foreach(dAlias; aliases){
613 		static if(!dStyle)
614 			dRet ~= "package ";
615 		dRet ~= "alias "~dAlias~" = "~fullModule~"."~dIden~";\n";
616 	}
617 	
618 	return dRet ~ cRet ~ "}";
619 };
620 
621 /**
622 A workaround that BindBC used to use for C-style enums.
623 It is included here mostly for preservation.
624 */
625 enum makeExpandedEnum(Enum) = () nothrow pure @safe{
626 	string ret;
627 	foreach(member; __traits(allMembers, Enum)){
628 		ret ~= "\nenum "~member~" = "~Enum.stringof~"."~member~";";
629 	}
630 	return ret;
631 }();