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