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