1 /*
2 Copyright (c) 2006 Kirk McDonald
3 
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22 
23 /**
24   Mostly internal utilities.
25   */
26 module pyd.func_wrap;
27 
28 import deimos.python.Python;
29 import std.algorithm: max;
30 import std.exception: enforce;
31 import std.range;
32 import std.conv;
33 import util.typelist;
34 import util.typeinfo;
35 
36 import pyd.def;
37 import pyd.references;
38 import pyd.class_wrap;
39 import pyd.exception;
40 import pyd.make_object;
41 
42 import std.traits;
43 
44 template hasFunctionAttrs(T) {
45     static if(isDelegate!T || isFunctionPointer!T) {
46         enum bool hasFunctionAttrs = functionAttributes!T != 
47             FunctionAttribute.none;
48     }else{
49         enum bool hasFunctionAttrs = false;
50     }
51 }
52 
53 // Builds a callable Python object from a delegate or function pointer.
54 void PydWrappedFunc_Ready(S)() {
55     static if(hasFunctionAttrs!S) {
56         alias SetFunctionAttributes!(S, 
57                 functionLinkage!S, 
58                 FunctionAttribute.none) T;
59     }else{
60         alias S T;
61     }
62     alias PydTypeObject!(T) type;
63     alias wrapped_class_object!(T) obj;
64     if (!is_wrapped!(T)) {
65         init_PyTypeObject!T(type);
66         Py_SET_TYPE(&type, &PyType_Type);
67         type.tp_basicsize = obj.sizeof;
68         type.tp_name = "PydFunc".ptr;
69         type.tp_flags = Py_TPFLAGS_DEFAULT;
70 
71         type.tp_call = &wrapped_func_call!(T).call;
72 
73         PyType_Ready(&type);
74         is_wrapped!T = true;
75     }
76 }
77 
78 void setWrongArgsError(Py_ssize_t gotArgs, size_t minArgs, size_t maxArgs, string funcName="") {
79 
80     string argStr(size_t args) {
81         string temp = to!string(args) ~ " argument";
82         if (args > 1) {
83             temp ~= "s";
84         }
85         return temp;
86     }
87     string str = (funcName == ""?"function":funcName~"()") ~ "takes";
88 
89     if (minArgs == maxArgs) {
90         if (minArgs == 0) {
91             str ~= "no arguments";
92         } else {
93             str ~= "exactly " ~ argStr(minArgs);
94         }
95     }
96     else if (gotArgs < minArgs) {
97         str ~= "at least " ~ argStr(minArgs);
98     } else {
99         str ~= "at most " ~ argStr(maxArgs);
100     }
101     str ~= " (" ~ to!string(gotArgs) ~ " given)";
102 
103     PyErr_SetString(PyExc_TypeError, (str ~ "\0").dup.ptr);
104 }
105 
106 // compose a tuple without cast-breaking constness
107 // example:
108 // ----------
109 // alias TupleComposer!(immutable(int), immutable(string)) T1;
110 // T1* t = new T1(1);
111 // t = t.put!1("foo");
112 // // t.fields is a thing now
113 struct TupleComposer(Ts...) {
114     Ts fields;
115 
116     TupleComposer!Ts* put(size_t i)(Ts[i] val) {
117         static if(isAssignable!(Ts[i])) {
118             fields[i] = val;
119             return &this;
120         }else{
121             return new TupleComposer(fields[0 .. i], val);
122         }
123 
124     }
125 }
126 
127 // Calls callable alias fn with PyTuple args.
128 // kwargs may be null, args may not
129 ReturnType!fn applyPyTupleToAlias(alias fn, string fname)(PyObject* args, PyObject* kwargs) {
130     alias ParameterTypeTuple!fn T;
131     enum size_t MIN_ARGS = minArgs!fn;
132     alias maxArgs!fn MaxArgs;
133     alias ReturnType!fn RT;
134     bool argsoverwrote = false;
135 
136     Py_ssize_t argCount = 0;
137     // This can make it more convenient to call this with 0 args.
138     if(kwargs !is null && PyObject_Length(kwargs) > 0) {
139         args = arrangeNamedArgs!(fn,fname)(args, kwargs);
140         Py_ssize_t newlen = PyObject_Length(args);
141         argsoverwrote = true;
142     }
143     scope(exit) if(argsoverwrote) Py_DECREF(args);
144     if (args !is null) {
145         argCount += PyObject_Length(args);
146     }
147 
148     // Sanity check!
149     if (!supportsNArgs!(fn)(argCount)) {
150         setWrongArgsError(cast(int) argCount, MIN_ARGS, 
151                 (MaxArgs.hasMax ? MaxArgs.max:-1));
152         handle_exception();
153     }
154 
155     static if (MaxArgs.vstyle == Variadic.no && MIN_ARGS == 0) {
156         if (argCount == 0) {
157             return fn();
158         }
159     }
160     auto t = new TupleComposer!(MaxArgs.ps)();
161     foreach(i, arg; t.fields) {
162         enum size_t argNum = i+1;
163         static if(MaxArgs.vstyle == Variadic.no) {
164             alias ParameterDefaultValueTuple!fn Defaults;
165             if (i < argCount) {
166                 auto bpobj =  PyTuple_GetItem(args, cast(Py_ssize_t) i);
167                 if(bpobj) {
168                     auto pobj = Py_XINCREF(bpobj);
169                     t = t.put!i(python_to_d!(typeof(arg))(pobj));
170                     Py_DECREF(pobj);
171                 }else{
172                     static if(!is(Defaults[i] == void)) {
173                         t = t.put!i(Defaults[i]);
174                     }else{
175                         // should never happen
176                         enforce(0, "python non-keyword arg is NULL!");
177                     }
178                 }
179             }
180             static if (argNum >= MIN_ARGS && 
181                     (!MaxArgs.hasMax || argNum <= MaxArgs.max)) {
182                 if (argNum == argCount) {
183                     return fn(t.fields[0 .. argNum]);
184                 }
185             }
186         }else static if(MaxArgs.vstyle == Variadic.typesafe) {
187             static if (argNum < t.fields.length) {
188                 auto pobj = Py_XINCREF(PyTuple_GetItem(args, cast(Py_ssize_t) i));
189                 t = t.put!i(python_to_d!(typeof(arg))(pobj));
190                 Py_DECREF(pobj);
191             }else static if(argNum == t.fields.length) {
192                 alias Unqual!(ElementType!(typeof(t.fields[i]))) elt_t;
193                 auto varlen = argCount-i;
194                 if(varlen == 1) {
195                     auto  pobj = Py_XINCREF(PyTuple_GetItem(args, cast(Py_ssize_t) i));
196                     if(PyList_Check(pobj)) {
197                         try{
198                             t = t.put!i(cast(typeof(t.fields[i])) python_to_d!(elt_t[])(pobj));
199                         }catch(PythonException e) {
200                             t = t.put!i(cast(typeof(t.fields[i])) [python_to_d!elt_t(pobj)]);
201                         }
202                     }else{
203                         t = t.put!i(cast(typeof(t.fields[i])) [python_to_d!elt_t(pobj)]);
204                     }
205                     Py_DECREF(pobj);
206                 }else{
207                     elt_t[] vars = new elt_t[](argCount-i);
208                     foreach(j; i .. argCount) {
209                         auto  pobj = Py_XINCREF(PyTuple_GetItem(args, cast(Py_ssize_t) j));
210                         vars[j-i] = python_to_d!(elt_t)(pobj);
211                         Py_DECREF(pobj);
212                     }
213                     t = t.put!i(cast(typeof(t.fields[i])) vars);
214                 }
215                 return fn(t.fields);
216                 break;
217             }
218         }else static assert(0);
219     }
220     // This should never get here.
221     throw new Exception("applyPyTupleToAlias reached end! argCount = " ~ to!string(argCount));
222 }
223 
224 // wraps applyPyTupleToAlias to return a PyObject*
225 // kwargs may be null, args may not.
226 PyObject* pyApplyToAlias(alias fn, string fname) (PyObject* args, PyObject* kwargs) {
227     static if (is(ReturnType!fn == void)) {
228         applyPyTupleToAlias!(fn,fname)(args, kwargs);
229         return Py_INCREF(Py_None());
230     } else {
231         return d_to_python( applyPyTupleToAlias!(fn,fname)(args, kwargs) );
232     }
233 }
234 
235 ReturnType!(dg_t) applyPyTupleToDelegate(dg_t) (dg_t dg, PyObject* args) {
236     alias ParameterTypeTuple!(dg_t) T;
237     enum size_t ARGS = T.length;
238     alias ReturnType!(dg_t) RT;
239 
240     Py_ssize_t argCount = 0;
241     // This can make it more convenient to call this with 0 args.
242     if (args !is null) {
243         argCount = PyObject_Length(args);
244     }
245 
246     // Sanity check!
247     if (!supportsNArgs!(dg,dg_t)(argCount)) {
248         setWrongArgsError(argCount, ARGS, ARGS);
249         handle_exception();
250     }
251 
252     static if (ARGS == 0) {
253         if (argCount == 0) {
254             return dg();
255         }
256     }
257     T t;
258     foreach(i, arg; t) {
259         auto pi = Py_XINCREF(PyTuple_GetItem(args, cast(Py_ssize_t) i));
260         t[i] = python_to_d!(typeof(arg))(pi);
261         Py_DECREF(pi);
262     }
263     return dg(t);
264 }
265 
266 // wraps applyPyTupleToDelegate to return a PyObject*
267 PyObject* pyApplyToDelegate(dg_t) (dg_t dg, PyObject* args) {
268     static if (is(ReturnType!(dg_t) == void)) {
269         applyPyTupleToDelegate(dg, args);
270         return Py_INCREF(Py_None());
271     } else {
272         return d_to_python( applyPyTupleToDelegate(dg, args) );
273     }
274 }
275 
276 template wrapped_func_call(fn_t) {
277     enum size_t ARGS = ParameterTypeTuple!(fn_t).length;
278     alias ReturnType!(fn_t) RT;
279     // The entry for the tp_call slot of the PydFunc types.
280     // (Or: What gets called when you pass a delegate or function pointer to
281     // Python.)
282     extern(C)
283     PyObject* call(PyObject* self, PyObject* args, PyObject* kwds) {
284         if (self is null) {
285             PyErr_SetString(PyExc_TypeError, "Wrapped method didn't get a function pointer.");
286             return null;
287         }
288 
289         fn_t fn = get_d_reference!fn_t(self);
290 
291         return exception_catcher(delegate PyObject*() {
292             return pyApplyToDelegate(fn, args);
293         });
294     }
295 }
296 
297 // Wraps a function alias with a PyCFunctionWithKeywords.
298 template function_wrap(alias real_fn, string fnname) {
299     alias ParameterTypeTuple!real_fn Info;
300     enum size_t MAX_ARGS = Info.length;
301     alias ReturnType!real_fn RT;
302 
303     extern (C)
304     PyObject* func(PyObject* self, PyObject* args, PyObject* kwargs) {
305         return exception_catcher(delegate PyObject*() {
306             import thread = pyd.thread;
307             thread.ensureAttached();
308             return pyApplyToAlias!(real_fn,fnname)(args, kwargs);
309         });
310     }
311 }
312 
313 // Wraps a member function alias with a PyCFunction.
314 // func's args and kwargs may each be null.
315 template method_wrap(C, alias real_fn, string fname) {
316     static assert(constCompatible(constness!C, constness!(typeof(real_fn))), 
317             format("constness mismatch instance: %s function: %s", 
318                 C.stringof, typeof(real_fn).stringof));
319     alias ParameterTypeTuple!real_fn Info;
320     enum size_t ARGS = Info.length;
321     alias ReturnType!real_fn RT;
322     extern(C)
323     PyObject* func(PyObject* self, PyObject* args, PyObject* kwargs) {
324         return exception_catcher(delegate PyObject*() {
325             // Didn't pass a "self" parameter! Ack!
326             if (self is null) {
327                 PyErr_SetString(PyExc_TypeError, "Wrapped method didn't get a 'self' parameter.");
328                 return null;
329             }
330             C instance = get_d_reference!C(self);
331             if (instance is null) {
332                 PyErr_SetString(PyExc_ValueError, "Wrapped class instance is null!");
333                 return null;
334             }
335             
336             Py_ssize_t arglen = args is null ? 0 : PyObject_Length(args);
337             enforce(arglen != -1);
338             PyObject* self_and_args = PyTuple_New(cast(Py_ssize_t) arglen+1);
339             scope(exit) {
340                 Py_XDECREF(self_and_args);
341             }
342             enforce(self_and_args);
343             PyTuple_SetItem(self_and_args, 0, self);
344             Py_INCREF(self);
345             foreach(i; 0 .. arglen) {
346                 auto pobj = Py_XINCREF(PyTuple_GetItem(args, cast(Py_ssize_t) i));
347                 PyTuple_SetItem(self_and_args, cast(Py_ssize_t) i+1, pobj);
348             }
349             alias memberfunc_to_func!(C,real_fn).func func;
350             return pyApplyToAlias!(func,fname)(self_and_args, kwargs);
351         });
352     }
353 }
354 
355 template method_dgwrap(C, alias real_fn) {
356     alias ParameterTypeTuple!real_fn Info;
357     enum size_t ARGS = Info.length;
358     alias ReturnType!real_fn RT;
359     extern(C)
360     PyObject* func(PyObject* self, PyObject* args) {
361         return exception_catcher(delegate PyObject*() {
362             // Didn't pass a "self" parameter! Ack!
363             if (self is null) {
364                 PyErr_SetString(PyExc_TypeError, "Wrapped method didn't get a 'self' parameter.");
365                 return null;
366             }
367             C instance = get_d_reference!C(self);
368             if (instance is null) {
369                 PyErr_SetString(PyExc_ValueError, "Wrapped class instance is null!");
370                 return null;
371             }
372             auto dg = dg_wrapper!(C, typeof(&real_fn))(instance, &real_fn);
373             return pyApplyToDelegate(dg, args);
374         });
375     }
376 }
377 
378 
379 template memberfunc_to_func(T, alias memfn) {
380     alias ReturnType!memfn Ret;
381     alias ParameterTypeTuple!memfn PS;
382     alias ParameterIdentifierTuple!memfn ids;
383     alias ParameterDefaultValueTuple!memfn dfs;
384     enum params = getparams!(memfn,"PS","dfs");
385     enum t = gensym!ids();
386         
387     mixin(Replace!(q{
388         Ret func(T $t, $params) {
389             auto dg = dg_wrapper($t, &memfn);
390             return dg($ids);
391         }
392     }, "$params", params, "$fn", __traits(identifier, memfn), "$t",t,
393        "$ids",Join!(",",ids)));
394 
395 }
396 
397 string gensym(Taken...)() {
398     bool ok(string s) {
399         bool _ok = true;
400         foreach(t; Taken) {
401             if(s == t) _ok = false;
402         }
403         return _ok;
404     }
405     foreach(c; 'a' .. 'z'+1) {
406         string s = to!string(cast(char)c);
407         if (ok(s)) return s;
408     }
409     // teh heck? wat kind of function takes more than 26 user-typed params?
410     int i = 0;
411     while(true) {
412         string s = format("_%s",i);
413         if (ok(s)) return s;
414         i++;
415     }
416 }
417 
418 //-----------------------------------------------------------------------------
419 // And now the reverse operation: wrapping a Python callable with a delegate.
420 // These rely on a whole collection of nasty templates, but the result is both
421 // flexible and pretty fast.
422 // (Sadly, wrapping a Python callable with a regular function is not quite
423 // possible.)
424 //-----------------------------------------------------------------------------
425 // The steps involved when calling this function are as follows:
426 // 1) An instance of PydWrappedFunc is made, and the callable placed within.
427 // 2) The delegate type Dg is broken into its constituent parts.
428 // 3) These parts are used to get the proper overload of PydWrappedFunc.fn
429 // 4) A delegate to PydWrappedFunc.fn is returned.
430 // 5) When fn is called, it attempts to cram the arguments into the callable.
431 //    If Python objects to this, an exception is raised. Note that this means
432 //    any error in converting the callable to a given delegate can only be
433 //    detected at runtime.
434 
435 Dg PydCallable_AsDelegate(Dg) (PyObject* c) {
436     return _pycallable_asdgT!(Dg).func(c);
437 }
438 
439 private template _pycallable_asdgT(Dg) if(is(Dg == delegate)) {
440     alias ParameterTypeTuple!(Dg) Info;
441     alias ReturnType!(Dg) Tr;
442 
443     Dg func(PyObject* c) {
444         auto f = new PydWrappedFunc(c);
445         static if(isImmutableFunction!Dg) {
446             return &f.fn_i!(Tr,Info);
447         }else static if(isConstFunction!Dg) {
448             return &f.fn_c!(Tr,Info);
449         }else{
450             return &f.fn!(Tr,Info);
451         }
452     }
453 }
454 
455 private
456 class PydWrappedFunc {
457     PyObject* callable;
458 
459     this(PyObject* c) { 
460         callable = c; 
461         Py_INCREF(c); 
462     }
463 
464     ~this() { 
465         if(callable && !Py_Finalize_called) {
466             Py_DECREF(callable); 
467         }
468         callable = null;
469     }
470 
471     Tr fn(Tr, T ...) (T t) {
472         PyObject* ret = call(t);
473         if (ret is null) handle_exception();
474         scope(exit) Py_DECREF(ret);
475         return python_to_d!(Tr)(ret);
476     }
477     Tr fn_c(Tr, T ...) (T t) const {
478         PyObject* ret = call_c(t);
479         if (ret is null) handle_exception();
480         scope(exit) Py_DECREF(ret);
481         return python_to_d!(Tr)(ret);
482     }
483     Tr fn_i(Tr, T ...) (T t) immutable {
484         PyObject* ret = call_i(t);
485         if (ret is null) handle_exception();
486         scope(exit) Py_DECREF(ret);
487         return python_to_d!(Tr)(ret);
488     }
489 
490     PyObject* call(T ...) (T t) {
491         enum size_t ARGS = T.length;
492         PyObject* pyt = PyTuple_FromItems(t);
493         if (pyt is null) return null;
494         scope(exit) Py_DECREF(pyt);
495         return PyObject_CallObject(callable, pyt);
496     }
497     PyObject* call_c(T ...) (T t) const {
498         enum size_t ARGS = T.length;
499         PyObject* pyt = PyTuple_FromItems(t);
500         if (pyt is null) return null;
501         scope(exit) Py_DECREF(pyt);
502         return PyObject_CallObject(cast(PyObject*) callable, pyt);
503     }
504     PyObject* call_i(T ...) (T t) immutable {
505         enum size_t ARGS = T.length;
506         PyObject* pyt = PyTuple_FromItems(t);
507         if (pyt is null) return null;
508         scope(exit) Py_DECREF(pyt);
509         return PyObject_CallObject(cast(PyObject*) callable, pyt);
510     }
511 }
512 
513 PyObject* arrangeNamedArgs(alias fn, string fname)(PyObject* args, PyObject* kwargs) {
514     alias ParameterIdentifierTuple!fn ids;
515     string[] allfnnames = new string[](ids.length);
516     size_t[string] allfnnameset;
517     foreach(i,id; ids) {
518         allfnnames[i] = id;
519         allfnnameset[id] = i;
520     }
521     alias variadicFunctionStyle!fn vstyle;
522     size_t firstDefaultValueIndex = ids.length;
523     static if(vstyle == Variadic.no) {
524         alias ParameterDefaultValueTuple!fn Defaults;
525         foreach(i, v; Defaults) {
526             static if(!is(v == void)) {
527                 firstDefaultValueIndex = i;
528                 break;
529             }
530         }
531     }
532 
533     Py_ssize_t arglen = PyObject_Length(args);
534     enforce(arglen != -1);
535     Py_ssize_t kwarglen = PyObject_Length(kwargs);
536     enforce(kwarglen != -1);
537     // variadic args might give us a count greater than ids.length
538     // (but in that case there should be no kwargs)
539     auto allargs = PyTuple_New(cast(Py_ssize_t) 
540             max(ids.length, arglen+kwarglen));
541 
542     foreach(i; 0 .. arglen) {
543         auto pobj = Py_XINCREF(PyTuple_GetItem(args, cast(Py_ssize_t) i));
544         PyTuple_SetItem(allargs, cast(Py_ssize_t) i, pobj);
545     }
546     PyObject* keys = PyDict_Keys(kwargs);
547     enforce(keys);
548     for(size_t _n = 0; _n < kwarglen; _n++) {
549         PyObject* pkey = PySequence_GetItem(keys, cast(Py_ssize_t) _n);
550         auto name = python_to_d!string(pkey);
551         if(name !in allfnnameset) {
552             enforce(false, format("%s() got an unexpected keyword argument '%s'",fname, name));
553             
554 
555         }
556         size_t n = allfnnameset[name];
557         auto bval = PyDict_GetItem(kwargs, pkey);
558         if(bval) {
559             auto val = Py_XINCREF(bval);
560             PyTuple_SetItem(allargs, cast(Py_ssize_t) n, val);
561         }else if(vstyle == Variadic.no && n >= firstDefaultValueIndex) {
562             // ok, we can get the default value 
563         }else{
564             enforce(false, format("argument '%s' is NULL! <%s, %s, %s, %s>", 
565                         name, n, firstDefaultValueIndex, ids.length, 
566                         vstyle == Variadic.no));
567         }
568     }
569     Py_DECREF(keys);
570     return allargs;
571 }
572 
573 template minNumArgs_impl(alias fn, fnT) {
574     alias ParameterTypeTuple!(fnT) Params;
575     alias ParameterDefaultValueTuple!(fn) Defaults;
576     alias variadicFunctionStyle!fn vstyle;
577     static if(Params.length == 0) {
578         // handle func(), func(...)
579         enum res = 0;
580     }else static if(vstyle == Variadic.typesafe){
581         // handle func(nondefault T1 t1, nondefault T2 t2, etc, TN[]...)
582         enum res = Params.length-1;
583     }else {
584         size_t count_nondefault() {
585             size_t result = 0;
586             foreach(i, v; Defaults) {
587                 static if(is(v == void)) {
588                     result ++;
589                 }else break;
590             }
591             return result;
592         }
593         enum res = count_nondefault();
594     }
595 }
596 
597 /**
598   Finds the minimal number of arguments a given function needs to be provided
599  */
600 template minArgs(alias fn, fnT = typeof(&fn)) {
601     enum size_t minArgs = minNumArgs_impl!(fn, fnT).res;
602 }
603 
604 /**
605   Finds the maximum number of arguments a given function may be provided
606   and/or whether the function has a maximum number of arguments.
607   */
608 template maxArgs(alias fn, fn_t = typeof(&fn)) {
609     alias variadicFunctionStyle!fn vstyle;
610     alias ParameterTypeTuple!fn ps;
611     /// _
612     enum bool hasMax = vstyle == Variadic.no;
613     /// _
614     enum size_t max = ps.length;
615 }
616 
617 /**
618   Determines at runtime whether the function can be given n arguments.
619   */
620 bool supportsNArgs(alias fn, fn_t = typeof(&fn))(size_t n) {
621     if(n < minArgs!(fn,fn_t)) {
622         return false;
623     }
624     alias variadicFunctionStyle!fn vstyle;
625     alias ParameterTypeTuple!fn ps;
626     alias ParameterDefaultValueTuple!fn defaults;
627     static if(vstyle == Variadic.no) {
628         return (n >= minArgs!(fn,fn_t) && n <= maxArgs!(fn,fn_t).max);
629     }else static if(vstyle == Variadic.c) {
630         return true;
631     }else static if(vstyle == Variadic.d) {
632         return true;
633     }else static if(vstyle == Variadic.typesafe) {
634         return true;
635     }else static assert(0);
636 }
637 
638 /**
639   Get the parameters of function as a string.
640 
641   pt_alias refers to an alias of ParameterTypeTuple!fn
642   visible to wherever you want to mix in the results.
643   pd_alias refers to an alias of ParameterDefaultValueTuple!fn
644   visible to wherever you want to mix in the results.
645 Example:
646 ---
647 void foo(int i, int j=2) {
648 }
649 
650 static assert(getparams!(foo,"P","Pd") == "P[0] i, P[1] j = Pd[1]");
651 ---
652   */
653 template getparams(alias fn, string pt_alias, string pd_alias) {
654     alias ParameterIdentifierTuple!fn Pi;
655     alias ParameterDefaultValueTuple!fn Pd;
656     enum var = variadicFunctionStyle!fn;
657 
658     string inner() {
659         static if(var == Variadic.c || var == Variadic.d) {
660             return "...";
661         }else{
662             string ret = "";
663             foreach(size_t i, id; Pi) {
664                 ret ~= format("%s[%s] %s", pt_alias, i, id);
665                 static if(!is(Pd[i] == void)) {
666                     ret ~= format(" = %s[%s]", pd_alias, i);
667                 }
668                 static if(i != Pi.length-1) {
669                     ret ~= ", ";
670                 }
671             }
672             static if(var == Variadic.typesafe) {
673                 ret ~= "...";
674             } 
675             return ret;
676         }
677     }
678 
679     enum getparams = inner();
680 
681 }
682 
683 template isImmutableFunction(T...) if (T.length == 1) {
684     alias funcTarget!T func_t;
685     enum isImmutableFunction = is(func_t == immutable);
686 }
687 template isConstFunction(T...) if (T.length == 1) {
688     alias funcTarget!T func_t;
689     enum isConstFunction = is(func_t == const);
690 }
691 template isMutableFunction(T...) if (T.length == 1) {
692     alias funcTarget!T func_t;
693     enum isMutableFunction = !is(func_t == inout) && !is(func_t == const) && !is(func_t == immutable);
694 }
695 template isWildcardFunction(T...) if (T.length == 1) {
696     alias funcTarget!T func_t;
697     enum isWildcardFunction = is(func_t == inout);
698 }
699 template isSharedFunction(T...) if (T.length == 1) {
700     alias funcTarget!T func_t;
701     enum isSharedFunction = is(func_t == shared);
702 }
703 
704 template funcTarget(T...) if(T.length == 1) {
705     static if(isPointer!(T[0]) && is(pointerTarget!(T[0]) == function)) {
706         alias pointerTarget!(T[0]) funcTarget;
707     }else static if(is(T[0] == function)) {
708         alias T[0] funcTarget;
709     }else static if(is(T[0] == delegate)) {
710         alias pointerTarget!(typeof((T[0]).init.funcptr)) funcTarget;
711     }else static assert(false);
712 }
713 
714 string tattrs_to_string(fn_t)() {
715     string s;
716     if(isConstFunction!fn_t) {
717         s ~= " const";
718     }
719     if(isImmutableFunction!fn_t) {
720         s ~= " immutable";
721     }
722     if(isSharedFunction!fn_t) {
723         s ~= " shared";
724     }
725     if(isWildcardFunction!fn_t) {
726         s ~= " inout";
727     }
728     return s;
729 }
730 
731 bool constnessMatch2(fn...)(Constness c) if(fn.length == 1) {
732     static if(isImmutableFunction!(fn)) return c == Constness.Immutable;
733     static if(isMutableFunction!(fn)) return c == Constness.Mutable; 
734     static if(isConstFunction!(fn)) return c != Constness.Wildcard;
735     else return false;
736 }
737 
738 /*
739  * some more or less dirty hacks for converting
740  * between function and delegate types. As of DMD 0.174, the language has
741  * built-in support for hacking apart delegates like this. Hooray!
742  */
743 
744 template fn_to_dgT(Fn) {
745     alias ParameterTypeTuple!(Fn) T;
746     alias ReturnType!(Fn) Ret;
747 
748     mixin("alias Ret delegate(T) " ~ tattrs_to_string!(Fn)() ~ " type;");
749 }
750 
751 /**
752  * This template converts a function type into an equivalent delegate type.
753  */
754 template fn_to_dg(Fn) {
755     alias fn_to_dgT!(Fn).type fn_to_dg;
756 }
757 
758 /**
759  * This template function converts a pointer to a member function into a
760  * delegate.
761  */
762 auto dg_wrapper(T, Fn) (T t, Fn fn) {
763     fn_to_dg!(Fn) dg;
764     dg.ptr = cast(void*) t;
765     static if(variadicFunctionStyle!fn == Variadic.typesafe) {
766         // trying to stuff a Ret function(P[]...) into a Ret function(P[])
767         // it'll totally work!
768         dg.funcptr = cast(typeof(dg.funcptr)) fn;
769     }else{
770         dg.funcptr = fn;
771     }
772 
773     return dg;
774 }
775