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