1 /* 2 Copyright 2006, 2007 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 Contains utilities for wrapping D classes. 25 */ 26 module pyd.class_wrap; 27 28 import deimos.python.Python; 29 30 import std.algorithm: countUntil; 31 import std.traits; 32 import std.conv; 33 import std.exception: enforce; 34 import std.functional; 35 import std.typetuple; 36 import std.string: format; 37 import std.typecons: Tuple; 38 import util.typelist; 39 import util.replace: Replace; 40 import util.typeinfo; 41 import pyd.references; 42 import pyd.ctor_wrap; 43 import pyd.def; 44 import pyd.exception; 45 import pyd.func_wrap; 46 import pyd.make_object; 47 import pyd.make_wrapper; 48 import pyd.op_wrap; 49 import pyd.struct_wrap; 50 51 version(Pyd_with_StackThreads) static assert(0, "sorry - stackthreads are gone"); 52 53 PyTypeObject*[ClassInfo] wrapped_classes; 54 template shim_class(T) { 55 PyTypeObject* shim_class; 56 } 57 58 // kill 59 template wrapped_class_object(T) { 60 alias PyObject wrapped_class_object; 61 } 62 63 void init_PyTypeObject(T)(ref PyTypeObject tipo) { 64 Py_SET_REFCNT(&tipo, 1); 65 tipo.tp_dealloc = &wrapped_methods!(T).wrapped_dealloc; 66 tipo.tp_new = &wrapped_methods!(T).wrapped_new; 67 } 68 69 70 71 // The list of wrapped methods for this class. 72 template wrapped_method_list(T) { 73 PyMethodDef[] wrapped_method_list = [ 74 { null, null, 0, null } 75 ]; 76 } 77 78 // The list of wrapped properties for this class. 79 template wrapped_prop_list(T) { 80 static PyGetSetDef[] wrapped_prop_list = [ 81 { null, null, null, null, null } 82 ]; 83 } 84 85 //-/////////////////// 86 // STANDARD METHODS // 87 //-/////////////////// 88 89 // Various wrapped methods 90 template wrapped_methods(T) { 91 /// The generic "__new__" method 92 extern(C) 93 PyObject* wrapped_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { 94 return type.tp_alloc(type, 0); 95 } 96 97 // The generic dealloc method. 98 extern(C) 99 void wrapped_dealloc(PyObject* self) { 100 // EMN: the *&%^^%! generic dealloc method is triggering a call to 101 // *&^%*%(! malloc for that delegate during a @(*$76*&! 102 // garbage collection 103 // Solution: don't use a *&%%^^! delegate in a destructor! 104 static struct StackDelegate{ 105 PyObject* x; 106 void dg() { 107 remove_pyd_mapping!T(x); 108 x.ob_type.tp_free(x); 109 } 110 } 111 StackDelegate x; 112 x.x = self; 113 exception_catcher_nogc(&x.dg); 114 } 115 } 116 117 // why we no use method_wrap ? 118 template wrapped_repr(T, alias fn) { 119 static assert(constCompatible(constness!T, constness!(typeof(fn))), 120 format("constness mismatch instance: %s function: %s", 121 T.stringof, typeof(fn).stringof)); 122 alias dg_wrapper!(T, typeof(&fn)) get_dg; 123 /// The default repr method calls the class's toString. 124 extern(C) 125 PyObject* repr(PyObject* self) { 126 return exception_catcher(delegate PyObject*() { 127 auto dg = get_dg(get_d_reference!T(self), &fn); 128 return d_to_python(dg()); 129 }); 130 } 131 } 132 133 private template ID(A){ alias A ID; } 134 private struct CW(A...){ alias A C; } 135 136 template IsProperty(alias T) { 137 enum bool IsProperty = 138 (functionAttributes!(T) & FunctionAttribute.property) != 0; 139 } 140 141 template IsGetter(alias T) { 142 enum bool IsGetter = ParameterTypeTuple!T .length == 0 && 143 !is(ReturnType!T == void); 144 } 145 146 template IsSetter(RT) { 147 template IsSetter(alias T) { 148 enum bool IsSetter = ParameterTypeTuple!T .length == 1 && 149 is(ParameterTypeTuple!(T)[0] == RT); 150 } 151 } 152 template IsAnySetter(alias T) { 153 enum bool IsAnySetter = ParameterTypeTuple!T .length == 1; 154 } 155 156 // This template gets an alias to a property and derives the types of the 157 // getter form and the setter form. It requires that the getter form return the 158 // same type that the setter form accepts. 159 struct property_parts(alias p, string _mode) { 160 alias ID!(__traits(parent, p)) Parent; 161 enum nom = __traits(identifier, p); 162 alias TypeTuple!(__traits(getOverloads, Parent, nom)) Overloads; 163 static if(_mode == "" || countUntil(_mode, "r") != -1) { 164 alias Filter!(IsGetter,Overloads) Getters; 165 static if(_mode == "" && Getters.length == 0) { 166 enum isgproperty = false; 167 enum rmode = ""; 168 }else { 169 import std.string; 170 static assert(Getters.length != 0, 171 format!("can't find property %s.%s getter", 172 Parent.stringof, nom)); 173 static assert(Getters.length == 1, 174 format!("can't handle property overloads of %s.%s getter (types %s)", 175 Parent.stringof, nom, staticMap!(ReturnType,Getters).stringof)); 176 alias Getters[0] GetterFn; 177 alias typeof(&GetterFn) getter_type; 178 enum isgproperty = IsProperty!GetterFn; 179 enum rmode = "r"; 180 } 181 }else { 182 enum isgproperty = false; 183 enum rmode = ""; 184 } 185 //enum bool pred1 = _mode == "" || countUntil(_mode, "w") != -1; 186 static if(_mode == "" || countUntil(_mode, "w") != -1) { 187 static if(rmode == "r") { 188 alias Filter!(IsSetter!(ReturnType!getter_type), Overloads) Setters; 189 }else { 190 alias Filter!(IsAnySetter, Overloads) Setters; 191 } 192 193 //enum bool pred2 = _mode == "" && Setters.length == 0; 194 static if(_mode == "" && Setters.length == 0) { 195 enum bool issproperty = false; 196 enum string wmode = ""; 197 }else{ 198 static assert(Setters.length != 0, format("can't find property %s.%s setter", Parent.stringof, nom)); 199 static assert(Setters.length == 1, 200 format("can't handle property overloads of %s.%s setter %s", 201 Parent.stringof, nom, Setters.stringof)); 202 alias Setters[0] SetterFn; 203 alias typeof(&SetterFn) setter_type; 204 static if(rmode == "r") { 205 static assert(!(IsProperty!GetterFn ^ IsProperty!(Setters[0])), 206 format("%s.%s: getter and setter must both be @property or not @property", 207 Parent.stringof, nom)); 208 } 209 enum issproperty = IsProperty!SetterFn; 210 enum wmode = "w"; 211 } 212 }else{ 213 enum issproperty = false; 214 enum wmode = ""; 215 } 216 217 static if(rmode != "") { 218 alias ReturnType!(GetterFn) Type; 219 }else static if(wmode != "") { 220 alias ParameterTypeTuple!(SetterFn)[0] Type; 221 } 222 223 enum mode = rmode ~ wmode; 224 enum bool isproperty = isgproperty || issproperty; 225 } 226 227 // 228 template wrapped_get(string fname, T, Parts) { 229 // A generic wrapper around a "getter" property. 230 extern(C) 231 PyObject* func(PyObject* self, void* closure) { 232 // method_wrap already catches exceptions 233 return method_wrap!(T, Parts.GetterFn, fname).func(self, null, null); 234 } 235 } 236 237 // 238 template wrapped_set(string fname, T, Parts) { 239 // A generic wrapper around a "setter" property. 240 extern(C) 241 int func(PyObject* self, PyObject* value, void* closure) { 242 PyObject* temp_tuple = PyTuple_New(1); 243 if (temp_tuple is null) return -1; 244 scope(exit) Py_DECREF(temp_tuple); 245 Py_INCREF(value); 246 PyTuple_SetItem(temp_tuple, 0, value); 247 PyObject* res = method_wrap!(T, Parts.SetterFn, fname).func(self, temp_tuple, null); 248 // If we get something back, we need to DECREF it. 249 if (res) Py_DECREF(res); 250 // If we don't, propagate the exception 251 else return -1; 252 // Otherwise, all is well. 253 return 0; 254 } 255 } 256 257 //-/////////////////////////// 258 // CLASS WRAPPING INTERFACE // 259 //-/////////////////////////// 260 261 //enum ParamType { Def, StaticDef, Property, Init, Parent, Hide, Iter, AltIter } 262 struct DoNothing { 263 static void call(string classname, T) () {} 264 } 265 266 /** 267 Wraps a member function of the class. 268 269 Supports default arguments, typesafe variadic arguments, and python's 270 keyword arguments. 271 272 Params: 273 fn = The member function to wrap. 274 Options = Optional parameters. Takes Docstring!(docstring), PyName!(pyname), 275 and fn_t. 276 fn_t = The type of the function. It is only useful to specify this 277 if more than one function has the same name as this one. 278 pyname = The name of the function as it will appear in Python. Defaults to 279 fn's name in D 280 docstring = The function's docstring. Defaults to "". 281 */ 282 struct Def(alias fn, Options...) { 283 alias Args!("","", __traits(identifier,fn), "",Options) args; 284 static if(args.rem.length) { 285 alias args.rem[0] fn_t; 286 }else { 287 alias typeof(&fn) fn_t; 288 } 289 mixin _Def!(fn, args.pyname, fn_t, args.docstring); 290 } 291 292 template _Def(alias _fn, string name, fn_t, string docstring) { 293 alias def_selector!(_fn,fn_t).FN func; 294 static assert(!__traits(isStaticFunction, func)); // TODO 295 static assert((functionAttributes!fn_t & ( 296 FunctionAttribute.nothrow_| 297 FunctionAttribute.pure_| 298 FunctionAttribute.trusted| 299 FunctionAttribute.safe)) == 0, 300 "pyd currently does not support pure, nothrow, @trusted, or @safe member functions"); 301 alias /*StripSafeTrusted!*/fn_t func_t; 302 enum realname = __traits(identifier,func); 303 enum funcname = name; 304 enum min_args = minArgs!(func); 305 enum bool needs_shim = false; 306 307 static void call(string classname, T) () { 308 alias ApplyConstness!(T, constness!(typeof(func))) cT; 309 static PyMethodDef empty = { null, null, 0, null }; 310 alias wrapped_method_list!(T) list; 311 list[$-1].ml_name = (name ~ "\0").ptr; 312 list[$-1].ml_meth = cast(PyCFunction) &method_wrap!(cT, func, classname ~ "." ~ name).func; 313 list[$-1].ml_flags = METH_VARARGS | METH_KEYWORDS; 314 list[$-1].ml_doc = (docstring~"\0").ptr; 315 list ~= empty; 316 // It's possible that appending the empty item invalidated the 317 // pointer in the type struct, so we renew it here. 318 PydTypeObject!(T).tp_methods = list.ptr; 319 } 320 template shim(size_t i, T) { 321 enum shim = Replace!(q{ 322 alias Params[$i] __pyd_p$i; 323 $override ReturnType!(__pyd_p$i.func_t) $realname(ParameterTypeTuple!(__pyd_p$i.func_t) t) $attrs { 324 return __pyd_get_overload!("$realname", __pyd_p$i.func_t).func!(ParameterTypeTuple!(__pyd_p$i.func_t))("$name", t); 325 } 326 alias T.$realname $realname; 327 }, "$i",i,"$realname",realname, "$name", name, 328 "$attrs", attrs_to_string(functionAttributes!func_t) ~ " " ~ tattrs_to_string!(func_t)(), 329 "$override", 330 // todo: figure out what's going on here 331 (variadicFunctionStyle!func == Variadic.no ? "override":"")); 332 } 333 } 334 335 /** 336 Wraps a static member function of the class. Similar to pyd.def.def 337 338 Supports default arguments, typesafe variadic arguments, and python's 339 keyword arguments. 340 341 Params: 342 fn = The member function to wrap. 343 Options = Optional parameters. Takes Docstring!(docstring), PyName!(pyname), 344 and fn_t 345 fn_t = The type of the function. It is only useful to specify this 346 if more than one function has the same name as this one. 347 pyname = The name of the function as it will appear in Python. Defaults to fn's 348 name in D. 349 docstring = The function's docstring. Defaults to "". 350 */ 351 struct StaticDef(alias fn, Options...) { 352 alias Args!("","", __traits(identifier,fn), "",Options) args; 353 static if(args.rem.length) { 354 alias args.rem[0] fn_t; 355 }else { 356 alias typeof(&fn) fn_t; 357 } 358 mixin _StaticDef!(fn, args.pyname, fn_t, args.docstring); 359 } 360 361 mixin template _StaticDef(alias fn, string name, fn_t, string docstring) { 362 alias def_selector!(fn,fn_t).FN func; 363 static assert(__traits(isStaticFunction, func)); // TODO 364 alias /*StripSafeTrusted!*/fn_t func_t; 365 enum funcname = name; 366 enum bool needs_shim = false; 367 static void call(string classname, T) () { 368 //pragma(msg, "class.static_def: " ~ name); 369 static PyMethodDef empty = { null, null, 0, null }; 370 alias wrapped_method_list!(T) list; 371 list[$-1].ml_name = (name ~ "\0").ptr; 372 list[$-1].ml_meth = cast(PyCFunction) &function_wrap!(func, classname ~ "." ~ name).func; 373 list[$-1].ml_flags = METH_VARARGS | METH_STATIC | METH_KEYWORDS; 374 list[$-1].ml_doc = (docstring~"\0").ptr; 375 list ~= empty; 376 PydTypeObject!(T).tp_methods = list.ptr; 377 } 378 template shim(size_t i,T) { 379 enum shim = ""; 380 } 381 } 382 383 /** 384 Wraps a property of the class. 385 386 Params: 387 fn = The property to wrap. 388 Options = Optional parameters. Takes Docstring!(docstring), PyName!(pyname), 389 and Mode!(mode) 390 pyname = The name of the property as it will appear in Python. Defaults to 391 fn's name in D. 392 mode = specifies whether this property is readable, writable. possible values 393 are "r", "w", "rw", and "" (in the latter case, automatically determine which 394 mode to use based on availability of getter and setter forms of fn). Defaults 395 to "". 396 docstring = The function's docstring. Defaults to "". 397 */ 398 struct Property(alias fn, Options...) { 399 alias Args!("","", __traits(identifier,fn), "",Options) args; 400 static assert(args.rem.length == 0, "Propery takes no other parameter"); 401 mixin _Property!(fn, args.pyname, args.mode, args.docstring); 402 } 403 404 template _Property(alias fn, string pyname, string _mode, string docstring) { 405 alias property_parts!(fn, _mode) parts; 406 //pragma(msg, "property: ", parts.nom); 407 static if(parts.isproperty) { 408 mixin _Member!(parts.nom, pyname, parts.mode, docstring, parts); 409 410 template shim(size_t i, T) { 411 enum shim = ""; 412 } 413 }else { 414 static if(countUntil(parts.mode,"r") != -1) { 415 alias parts.getter_type get_t; 416 } 417 static if(countUntil(parts.mode,"w") != -1) { 418 alias parts.setter_type set_t; 419 } 420 enum realname = __traits(identifier, fn); 421 enum funcname = pyname; 422 enum bool needs_shim = false; 423 static void call(string classname, T) () { 424 static PyGetSetDef empty = { null, null, null, null, null }; 425 wrapped_prop_list!(T)[$-1].name = (pyname ~ "\0").dup.ptr; 426 static if (countUntil(parts.mode, "r") != -1) { 427 alias ApplyConstness!(T, constness!(typeof(parts.GetterFn))) 428 cT_g; 429 wrapped_prop_list!(T)[$-1].get = 430 &wrapped_get!(classname ~ "." ~ pyname, cT_g, parts).func; 431 } 432 static if (countUntil(parts.mode, "w") != -1) { 433 alias ApplyConstness!(T, constness!(typeof(parts.SetterFn))) 434 cT_s; 435 wrapped_prop_list!(T)[$-1].set = 436 &wrapped_set!(classname ~ "." ~ pyname,cT_s, parts).func; 437 } 438 wrapped_prop_list!(T)[$-1].doc = (docstring~"\0").dup.ptr; 439 wrapped_prop_list!(T)[$-1].closure = null; 440 wrapped_prop_list!(T) ~= empty; 441 // It's possible that appending the empty item invalidated the 442 // pointer in the type struct, so we renew it here. 443 PydTypeObject!(T).tp_getset = 444 wrapped_prop_list!(T).ptr; 445 } 446 template shim(size_t i, T) { 447 static if(countUntil(parts.mode, "r") != -1) { 448 enum getter = Replace!(q{ 449 override ReturnType!(__pyd_p$i.get_t) $realname() { 450 return __pyd_get_overload!("$realname", __pyd_p$i.get_t).func("$name"); 451 } 452 } , "$i",i,"$realname",realname, "$name", pyname); 453 }else{ 454 enum getter = ""; 455 } 456 static if(countUntil(parts.mode, "w") != -1) { 457 enum setter = Replace!(q{ 458 override ReturnType!(__pyd_p$i.set_t) $realname(ParameterTypeTuple!(__pyd_p$i.set_t) t) { 459 return __pyd_get_overload!("$realname", __pyd_p$i.set_t).func("$name", t); 460 } 461 }, "$i", i, "$realname",realname, "$name", pyname); 462 }else { 463 enum setter = ""; 464 } 465 enum shim = Replace!(q{ 466 alias Params[$i] __pyd_p$i; 467 $getter 468 $setter; 469 }, "$i",i, "$getter", getter, "$setter",setter); 470 } 471 } 472 } 473 474 /** 475 Wraps a method as the class's ___repr__ in Python. 476 477 Params: 478 fn = The property to wrap. Must have the signature string function(). 479 */ 480 struct Repr(alias _fn) { 481 alias def_selector!(_fn, string function()).FN fn; 482 enum bool needs_shim = false; 483 static void call(string classname, T)() { 484 alias ApplyConstness!(T, constness!(typeof(fn))) cT; 485 alias PydTypeObject!(T) type; 486 type.tp_repr = &wrapped_repr!(cT, fn).repr; 487 } 488 template shim(size_t i,T) { 489 enum shim = ""; 490 } 491 } 492 493 /** 494 Wraps the constructors of the class. 495 496 This template takes a single specialization of the ctor template 497 (see ctor_wrap.d), which describes a constructor that the class 498 supports. The default constructor need not be 499 specified, and will always be available if the class supports it. 500 501 Supports default arguments, typesafe variadic arguments, and python's 502 keyword arguments. 503 504 Params: 505 cps = Parameter list of the constructor to be wrapped. 506 507 Bugs: 508 This currently does not support having multiple constructors with 509 the same number of arguments. 510 */ 511 struct Init(cps ...) { 512 alias cps CtorParams; 513 enum bool needs_shim = false; 514 template Inner(T) { 515 alias NewParamT!T BaseT; 516 alias TypeTuple!(__traits(getOverloads, BaseT, "__ctor")) Overloads; 517 template IsDesired(alias ctor) { 518 alias ParameterTypeTuple!ctor ps; 519 enum bool IsDesired = is(ps == CtorParams); 520 } 521 alias Filter!(IsDesired, Overloads) VOverloads; 522 static assert(VOverloads.length != 0, 523 format("%s: Cannot find constructor with params %s", 524 T.stringof, CtorParams.stringof)); 525 alias VOverloads[0] FN; 526 527 alias ParameterTypeTuple!FN Pt; 528 alias ParameterDefaultValueTuple!FN Pd; 529 } 530 static void call(string classname, T)() { 531 } 532 template shim(size_t i, T) { 533 enum params = getparams!(Inner!T.FN, 534 format("__pyd_p%s.Inner!T.Pt",i), 535 format("__pyd_p%s.Inner!T.Pd",i)); 536 alias ParameterIdentifierTuple!(Inner!T.FN) paramids; 537 enum shim = Replace!(q{ 538 alias Params[$i] __pyd_p$i; 539 this($params) { 540 super($ids); 541 } 542 }, "$i", i, "$params", params, "$ids", Join!(",", paramids)); 543 } 544 } 545 546 template IsInit(T) { 547 enum bool IsInit = __traits(hasMember, T, "CtorParams"); 548 } 549 550 enum binaryslots = [ 551 "+": "type.tp_as_number.nb_add", 552 "+=": "type.tp_as_number.nb_inplace_add", 553 "-": "type.tp_as_number.nb_subtract", 554 "-=": "type.tp_as_number.nb_inplace_subtract", 555 "*": "type.tp_as_number.nb_multiply", 556 "*=": "type.tp_as_number.nb_inplace_multiply", 557 "/": "type.tp_as_number.nb_divide", 558 "/=": "type.tp_as_number.nb_inplace_divide", 559 "%": "type.tp_as_number.nb_remainder", 560 "%=": "type.tp_as_number.nb_inplace_remainder", 561 "^^": "type.tp_as_number.nb_power", 562 "^^=": "type.tp_as_number.nb_inplace_power", 563 "<<": "type.tp_as_number.nb_lshift", 564 "<<=": "type.tp_as_number.nb_inplace_lshift", 565 ">>": "type.tp_as_number.nb_rshift", 566 ">>=": "type.tp_as_number.nb_inplace_rshift", 567 "&": "type.tp_as_number.nb_and", 568 "&=": "type.tp_as_number.nb_inplace_and", 569 "^": "type.tp_as_number.nb_xor", 570 "^=": "type.tp_as_number.nb_inplace_xor", 571 "|": "type.tp_as_number.nb_or", 572 "|=": "type.tp_as_number.nb_inplace_or", 573 "~": "type.tp_as_sequence.sq_concat", 574 "~=": "type.tp_as_sequence.sq_inplace_concat", 575 "in": "type.tp_as_sequence.sq_contains", 576 ]; 577 578 bool IsPyBinary(string op) { 579 foreach(_op, slot; binaryslots) { 580 if (op[$-1] != '=' && op == _op) return true; 581 } 582 return false; 583 } 584 bool IsPyAsg(string op0) { 585 auto op = op0~"="; 586 foreach(_op, slot; binaryslots) { 587 if (op == _op) return true; 588 } 589 return false; 590 } 591 592 enum unaryslots = [ 593 "+": "type.tp_as_number.nb_positive", 594 "-": "type.tp_as_number.nb_negative", 595 "~": "type.tp_as_number.nb_invert", 596 ]; 597 598 bool IsPyUnary(string op) { 599 foreach(_op, slot; unaryslots) { 600 if(op == _op) return true; 601 } 602 return false; 603 } 604 605 // string mixin to initialize tp_as_number or tp_as_sequence or tp_as_mapping 606 // if necessary. Scope mixed in must have these variables: 607 // slot: a value from binaryslots or unaryslots 608 // type: a PyObjectType. 609 string autoInitializeMethods() { 610 return q{ 611 static if(countUntil(slot, "tp_as_number") != -1) { 612 if(type.tp_as_number is null) 613 type.tp_as_number = new PyNumberMethods; 614 }else static if(countUntil(slot, "tp_as_sequence") != -1) { 615 if(type.tp_as_sequence is null) 616 type.tp_as_sequence = new PySequenceMethods; 617 }else static if(countUntil(slot, "tp_as_mapping") != -1) { 618 if(type.tp_as_mapping is null) 619 type.tp_as_mapping = new PyMappingMethods; 620 } 621 }; 622 } 623 624 private struct Guess{} 625 626 struct BinaryOperatorX(string _op, bool isR, rhs_t) { 627 enum op = _op; 628 enum isRight = isR; 629 630 static if(isR) enum nom = "opBinaryRight"; 631 else enum nom = "opBinary"; 632 633 enum bool needs_shim = false; 634 635 template Inner(C) { 636 enum fn_str1 = "Alias!(C."~nom~"!(op))"; 637 enum fn_str2 = "C."~nom~"!(op,rhs_t)"; 638 enum string OP = op; 639 static if(!__traits(hasMember, C, nom)) { 640 static assert(0, C.stringof ~ " has no "~(isR ?"reflected ":"")~ 641 "binary operator overloads"); 642 } 643 template Alias(alias fn) { 644 alias fn Alias; 645 } 646 static if(is(typeof(mixin(fn_str1)) == function)) { 647 alias ParameterTypeTuple!(typeof(mixin(fn_str1)))[0] RHS_T; 648 alias ReturnType!(typeof(mixin(fn_str1))) RET_T; 649 mixin("alias " ~ fn_str1 ~ " FN;"); 650 static if(!is(rhs_t == Guess)) 651 static assert(is(RHS_T == rhs_t), 652 format("expected typeof(rhs) = %s, found %s", 653 rhs.stringof, RHS_T.stringof)); 654 }else static if(is(rhs_t == Guess)) { 655 static assert(false, 656 format("Operator %s: Cannot determine type of rhs", op)); 657 } else static if(is(typeof(mixin(fn_str2)) == function)) { 658 alias rhs_t RHS_T; 659 alias ReturnType!(typeof(mixin(fn_str2))) RET_T; 660 mixin("alias "~fn_str2~" FN;"); 661 } else static assert(false, "Cannot get operator overload"); 662 } 663 664 static void call(string classname, T)() { 665 // can't handle __op__ __rop__ pairs here 666 } 667 668 template shim(size_t i, T) { 669 // bah 670 enum shim = ""; 671 } 672 } 673 674 /** 675 Wrap a binary operator overload. 676 677 Example: 678 --- 679 class Foo{ 680 int _j; 681 int opBinary(string op)(int i) if(op == "+"){ 682 return i+_j; 683 } 684 int opBinaryRight(string op)(int i) if(op == "+"){ 685 return i+_j; 686 } 687 } 688 689 class_wrap!(Foo, 690 OpBinary!("+"), 691 OpBinaryRight!("+")); 692 --- 693 694 Params: 695 op = Operator to wrap 696 rhs_t = (optional) Type of opBinary's parameter for disambiguation if 697 there are multiple overloads. 698 Bugs: 699 Issue 8602 prevents disambiguation for case X opBinary(string op, T)(T t); 700 */ 701 template OpBinary(string op, rhs_t = Guess) if(IsPyBinary(op) && op != "in"){ 702 alias BinaryOperatorX!(op, false, rhs_t) OpBinary; 703 } 704 705 /// ditto 706 template OpBinaryRight(string op, lhs_t = Guess) if(IsPyBinary(op)) { 707 alias BinaryOperatorX!(op, true, lhs_t) OpBinaryRight; 708 } 709 710 /** 711 Wrap a unary operator overload. 712 */ 713 struct OpUnary(string _op) if(IsPyUnary(_op)) { 714 enum op = _op; 715 enum bool needs_shim = false; 716 717 template Inner(C) { 718 enum string OP = op; 719 static if(!__traits(hasMember, C, "opUnary")) { 720 static assert(0, C.stringof ~ " has no unary operator overloads"); 721 } 722 static if(is(typeof(C.init.opUnary!(op)) == function)) { 723 alias ReturnType!(C.opUnary!(op)) RET_T; 724 alias C.opUnary!(op) FN; 725 } else static assert(false, "Cannot get operator overload"); 726 } 727 static void call(string classname, T)() { 728 alias PydTypeObject!T type; 729 enum slot = unaryslots[op]; 730 mixin(autoInitializeMethods()); 731 mixin(slot ~ " = &opfunc_unary_wrap!(T, Inner!T .FN).func;"); 732 } 733 template shim(size_t i,T) { 734 // bah 735 enum shim = ""; 736 } 737 } 738 739 /** 740 Wrap an operator assignment overload. 741 742 Example: 743 --- 744 class Foo{ 745 int _j; 746 void opOpAssign(string op)(int i) if(op == "+"){ 747 _j = i; 748 } 749 } 750 751 class_wrap!(Foo, 752 OpAssign!("+")); 753 --- 754 Params: 755 op = Base operator to wrap 756 rhs_t = (optional) Type of opOpAssign's parameter for disambiguation if 757 there are multiple overloads. 758 */ 759 struct OpAssign(string _op, rhs_t = Guess) if(IsPyAsg(_op)) { 760 enum op = _op~"="; 761 762 enum bool needs_shim = false; 763 764 template Inner(C) { 765 enum string OP = op; 766 static if(!__traits(hasMember, C, "opOpAssign")) { 767 static assert(0, C.stringof ~ " has no operator assignment overloads"); 768 } 769 static if(is(typeof(C.init.opOpAssign!(_op)) == function)) { 770 alias ParameterTypeTuple!(typeof(C.opOpAssign!(_op)))[0] RHS_T; 771 alias ReturnType!(typeof(C.opOpAssign!(_op))) RET_T; 772 alias C.opOpAssign!(_op) FN; 773 static if(!is(rhs_t == Guess)) 774 static assert(is(RHS_T == rhs_t), 775 format("expected typeof(rhs) = %s, found %s", 776 rhs.stringof, RHS_T.stringof)); 777 }else static if(is(rhs_t == Guess)) { 778 static assert(false, "Cannot determine type of rhs"); 779 } else static if(is(typeof(C.opOpAssign!(_op,rhs_t)) == function)) { 780 alias rhs_t RHS_T; 781 alias ReturnType!(typeof(C.opOpAssign!(_op,rhs_t))) RET_T; 782 alias C.opOpAssign!(_op,rhs_t) FN; 783 } else static assert(false, "Cannot get operator assignment overload"); 784 } 785 static void call(string classname, T)() { 786 alias PydTypeObject!T type; 787 enum slot = binaryslots[op]; 788 mixin(autoInitializeMethods()); 789 alias CW!(TypeTuple!(OpAssign)) OpAsg; 790 alias CW!(TypeTuple!()) Nop; 791 static if(op == "^^=") 792 mixin(slot ~ " = &powopasg_wrap!(T, Inner!T.FN).func;"); 793 else 794 mixin(slot ~ " = &binopasg_wrap!(T, Inner!T.FN).func;"); 795 } 796 797 template shim(size_t i,T) { 798 // bah 799 enum shim = ""; 800 } 801 } 802 803 // struct types could probably take any parameter type 804 // class types must take Object 805 /** 806 Wrap opCmp. 807 808 Params: 809 rhs_t = (optional) Type of opCmp's parameter for disambiguation if there 810 are multiple overloads (for classes it will always be Object). 811 */ 812 struct OpCompare(_rhs_t = Guess) { 813 enum bool needs_shim = false; 814 815 816 template Inner(C) { 817 static if(is(_rhs_t == Guess) && is(C == class)) { 818 alias Object rhs_t; 819 }else { 820 alias _rhs_t rhs_t; 821 } 822 static if(!__traits(hasMember, C, "opCmp")) { 823 static assert(0, C.stringof ~ " has no comparison operator overloads"); 824 } 825 static if(!is(typeof(C.init.opCmp) == function)) { 826 static assert(0, format("why is %s.opCmp not a function?",C)); 827 } 828 alias TypeTuple!(__traits(getOverloads, C, "opCmp")) Overloads; 829 static if(is(rhs_t == Guess) && Overloads.length > 1) { 830 static assert(0, format("Cannot choose between %s", Overloads)); 831 }else static if(Overloads.length == 1) { 832 static if(!is(rhs_t == Guess) && 833 !is(ParameterTypeTuple!(Overloads[0])[0] == rhs_t)) { 834 static assert(0, format("%s.opCmp: expected param %s, got %s", 835 C, rhs_t, ParameterTypeTuple!(Overloads[0]))); 836 }else{ 837 alias Overloads[0] FN; 838 } 839 }else{ 840 template IsDesiredOverload(alias fn) { 841 enum bool IsDesiredOverload = is(ParameterTypeTuple!(fn)[0] == rhs_t); 842 } 843 alias Filter!(IsDesiredOverload, Overloads) Overloads1; 844 static assert(Overloads1.length == 1, 845 format("Cannot choose between %s", Overloads1)); 846 alias Overloads1[0] FN; 847 } 848 } 849 static void call(string classname, T)() { 850 alias PydTypeObject!T type; 851 alias ApplyConstness!(T, constness!(typeof(Inner!T.FN))) cT; 852 type.tp_richcompare = &rich_opcmp_wrap!(cT, Inner!T.FN).func; 853 } 854 template shim(size_t i,T) { 855 // bah 856 enum shim = ""; 857 } 858 } 859 860 /** 861 Wrap opIndex, opIndexAssign. 862 863 Params: 864 index_t = (optional) Types of opIndex's parameters for disambiguation if 865 there are multiple overloads. 866 */ 867 struct OpIndex(index_t...) { 868 enum bool needs_shim = false; 869 template Inner(C) { 870 static if(!__traits(hasMember, C, "opIndex")) { 871 static assert(0, C.stringof ~ " has no index operator overloads"); 872 } 873 static if(is(typeof(C.init.opIndex) == function)) { 874 alias TypeTuple!(__traits(getOverloads, C, "opIndex")) Overloads; 875 static if(index_t.length == 0 && Overloads.length > 1) { 876 static assert(0, 877 format("%s.opIndex: Cannot choose between %s", 878 C.stringof,Overloads.stringof)); 879 }else static if(index_t.length == 0) { 880 alias Overloads[0] FN; 881 }else{ 882 template IsDesiredOverload(alias fn) { 883 enum bool IsDesiredOverload = is(ParameterTypeTuple!fn == index_t); 884 } 885 alias Filter!(IsDesiredOverload, Overloads) Overloads1; 886 static assert(Overloads1.length == 1, 887 format("%s.opIndex: Cannot choose between %s", 888 C.stringof,Overloads1.stringof)); 889 alias Overloads1[0] FN; 890 } 891 }else static if(is(typeof(C.init.opIndex!(index_t)) == function)) { 892 alias C.opIndex!(index_t) FN; 893 }else{ 894 static assert(0, 895 format("cannot get a handle on %s.opIndex", C.stringof)); 896 } 897 } 898 static void call(string classname, T)() { 899 /* 900 alias PydTypeObject!T type; 901 enum slot = "type.tp_as_mapping.mp_subscript"; 902 mixin(autoInitializeMethods()); 903 mixin(slot ~ " = &opindex_wrap!(T, Inner!T.FN).func;"); 904 */ 905 } 906 template shim(size_t i,T) { 907 // bah 908 enum shim = ""; 909 } 910 } 911 912 /// ditto 913 struct OpIndexAssign(index_t...) { 914 static assert(index_t.length != 1, 915 "opIndexAssign must have at least 2 parameters"); 916 enum bool needs_shim = false; 917 template Inner(C) { 918 static if(!__traits(hasMember, C, "opIndexAssign")) { 919 static assert(0, C.stringof ~ " has no index operator overloads"); 920 } 921 static if(is(typeof(C.init.opIndex) == function)) { 922 alias TypeTuple!(__traits(getOverloads, C, "opIndexAssign")) Overloads; 923 template IsValidOverload(alias fn) { 924 enum bool IsValidOverload = ParameterTypeTuple!fn.length >= 2; 925 } 926 alias Filter!(IsValidOverload, Overloads) VOverloads; 927 static if(VOverloads.length == 0 && Overloads.length != 0) 928 static assert(0, 929 "opIndexAssign must have at least 2 parameters"); 930 static if(index_t.length == 0 && VOverloads.length > 1) { 931 static assert(0, 932 format("%s.opIndexAssign: Cannot choose between %s", 933 C.stringof,VOverloads.stringof)); 934 }else static if(index_t.length == 0) { 935 alias VOverloads[0] FN; 936 }else{ 937 template IsDesiredOverload(alias fn) { 938 enum bool IsDesiredOverload = is(ParameterTypeTuple!fn == index_t); 939 } 940 alias Filter!(IsDesiredOverload, VOverloads) Overloads1; 941 static assert(Overloads1.length == 1, 942 format("%s.opIndex: Cannot choose between %s", 943 C.stringof,Overloads1.stringof)); 944 alias Overloads1[0] FN; 945 } 946 }else static if(is(typeof(C.init.opIndexAssign!(index_t)) == function)) { 947 alias C.opIndexAssign!(index_t) FN; 948 }else{ 949 static assert(0, 950 format("cannot get a handle on %s.opIndexAssign", C.stringof)); 951 } 952 } 953 static void call(string classname, T)() { 954 /* 955 alias PydTypeObject!T type; 956 enum slot = "type.tp_as_mapping.mp_ass_subscript"; 957 mixin(autoInitializeMethods()); 958 mixin(slot ~ " = &opindexassign_wrap!(T, Inner!T.FN).func;"); 959 */ 960 } 961 template shim(size_t i,T) { 962 // bah 963 enum shim = ""; 964 } 965 } 966 967 /** 968 Wrap opSlice. 969 970 Requires signature 971 --- 972 Foo.opSlice(Py_ssize_t, Py_ssize_t); 973 --- 974 This is a limitation of the C/Python API. 975 */ 976 struct OpSlice() { 977 enum bool needs_shim = false; 978 template Inner(C) { 979 static if(!__traits(hasMember, C, "opSlice")) { 980 static assert(0, C.stringof ~ " has no slice operator overloads"); 981 } 982 static if(is(typeof(C.init.opSlice) == function)) { 983 alias TypeTuple!(__traits(getOverloads, C, "opSlice")) Overloads; 984 template IsDesiredOverload(alias fn) { 985 enum bool IsDesiredOverload = is(ParameterTypeTuple!fn == 986 TypeTuple!(Py_ssize_t,Py_ssize_t)); 987 } 988 alias Filter!(IsDesiredOverload, Overloads) Overloads1; 989 static assert(Overloads1.length != 0, 990 format("%s.opSlice: must have overload %s", 991 C.stringof,TypeTuple!(Py_ssize_t,Py_ssize_t).stringof)); 992 static assert(Overloads1.length == 1, 993 format("%s.opSlice: cannot choose between %s", 994 C.stringof,Overloads1.stringof)); 995 alias Overloads1[0] FN; 996 }else{ 997 static assert(0, format("cannot get a handle on %s.opSlice", 998 C.stringof)); 999 } 1000 } 1001 static void call(string classname, T)() { 1002 /* 1003 alias PydTypeObject!T type; 1004 enum slot = "type.tp_as_sequence.sq_slice"; 1005 mixin(autoInitializeMethods()); 1006 mixin(slot ~ " = &opslice_wrap!(T, Inner!T.FN).func;"); 1007 */ 1008 } 1009 template shim(size_t i,T) { 1010 // bah 1011 enum shim = ""; 1012 } 1013 } 1014 1015 /** 1016 Wrap opSliceAssign. 1017 1018 Requires signature 1019 --- 1020 Foo.opSliceAssign(Value,Py_ssize_t, Py_ssize_t); 1021 --- 1022 This is a limitation of the C/Python API. 1023 */ 1024 struct OpSliceAssign(rhs_t = Guess) { 1025 enum bool needs_shim = false; 1026 template Inner(C) { 1027 static if(!__traits(hasMember, C, "opSliceAssign")) { 1028 static assert(0, C.stringof ~ " has no slice assignment operator overloads"); 1029 } 1030 static if(is(typeof(C.init.opSliceAssign) == function)) { 1031 alias TypeTuple!(__traits(getOverloads, C, "opSliceAssign")) Overloads; 1032 template IsDesiredOverload(alias fn) { 1033 alias ParameterTypeTuple!fn ps; 1034 enum bool IsDesiredOverload = 1035 is(ps[1..3] == TypeTuple!(Py_ssize_t,Py_ssize_t)); 1036 } 1037 alias Filter!(IsDesiredOverload, Overloads) Overloads1; 1038 static assert(Overloads1.length != 0, 1039 format("%s.opSliceAssign: must have overload %s", 1040 C.stringof,TypeTuple!(Guess,Py_ssize_t,Py_ssize_t).stringof)); 1041 static if(is(rhs_t == Guess)) { 1042 static assert(Overloads1.length == 1, 1043 format("%s.opSliceAssign: cannot choose between %s", 1044 C.stringof,Overloads1.stringof)); 1045 alias Overloads1[0] FN; 1046 }else{ 1047 template IsDesiredOverload2(alias fn) { 1048 alias ParameterTypeTuple!fn ps; 1049 enum bool IsDesiredOverload2 = is(ps[0] == rhs_t); 1050 } 1051 alias Filter!(IsDesiredOverload2, Overloads1) Overloads2; 1052 static assert(Overloads2.length == 1, 1053 format("%s.opSliceAssign: cannot choose between %s", 1054 C.stringof,Overloads2.stringof)); 1055 alias Overloads2[0] FN; 1056 } 1057 }else{ 1058 static assert(0, format("cannot get a handle on %s.opSlice", 1059 C.stringof)); 1060 } 1061 } 1062 static void call(string classname, T)() { 1063 /* 1064 alias PydTypeObject!T type; 1065 enum slot = "type.tp_as_sequence.sq_ass_slice"; 1066 mixin(autoInitializeMethods()); 1067 mixin(slot ~ " = &opsliceassign_wrap!(T, Inner!T.FN).func;"); 1068 */ 1069 } 1070 template shim(size_t i,T) { 1071 // bah 1072 enum shim = ""; 1073 } 1074 } 1075 1076 /** 1077 wrap opCall. The parameter types of opCall must be specified. 1078 */ 1079 struct OpCall(Args_t...) { 1080 enum bool needs_shim = false; 1081 1082 template Inner(T) { 1083 alias TypeTuple!(__traits(getOverloads, T, "opCall")) Overloads; 1084 template IsDesiredOverload(alias fn) { 1085 alias ParameterTypeTuple!fn ps; 1086 enum bool IsDesiredOverload = is(ps == Args_t); 1087 } 1088 alias Filter!(IsDesiredOverload, Overloads) VOverloads; 1089 static if(VOverloads.length == 0) { 1090 static assert(0, 1091 format("%s.opCall: cannot find signature %s", T.stringof, 1092 Args_t.stringof)); 1093 }else static if(VOverloads.length == 1){ 1094 alias VOverloads[0] FN; 1095 }else static assert(0, 1096 format("%s.%s: cannot choose between %s", T.stringof, nom, 1097 VOverloads.stringof)); 1098 } 1099 static void call(string classname, T)() { 1100 alias PydTypeObject!T type; 1101 alias Inner!T.FN fn; 1102 alias ApplyConstness!(T, constness!(typeof(fn))) cT; 1103 type.tp_call = &opcall_wrap!(cT, fn).func; 1104 } 1105 template shim(size_t i,T) { 1106 // bah 1107 enum shim = ""; 1108 } 1109 } 1110 1111 /** 1112 Wraps Foo.length or another function as python's ___len__ function. 1113 1114 Requires signature 1115 --- 1116 Py_ssize_t length(); 1117 --- 1118 This is a limitation of the C/Python API. 1119 */ 1120 template Len() { 1121 alias _Len!() Len; 1122 } 1123 1124 /// ditto 1125 template Len(alias fn) { 1126 alias _Len!(fn) Len; 1127 } 1128 1129 struct _Len(fnt...) { 1130 enum bool needs_shim = false; 1131 template Inner(T) { 1132 static if(fnt.length == 0) { 1133 enum nom = "length"; 1134 }else{ 1135 enum nom = __traits(identifier, fnt[0]); 1136 } 1137 alias TypeTuple!(__traits(getOverloads, T, nom)) Overloads; 1138 template IsDesiredOverload(alias fn) { 1139 alias ParameterTypeTuple!fn ps; 1140 alias ReturnType!fn rt; 1141 enum bool IsDesiredOverload = isImplicitlyConvertible!(rt,Py_ssize_t) && ps.length == 0; 1142 } 1143 alias Filter!(IsDesiredOverload, Overloads) VOverloads; 1144 static if(VOverloads.length == 0 && Overloads.length != 0) { 1145 static assert(0, 1146 format("%s.%s must have signature %s", T.stringof, nom, 1147 (Py_ssize_t function()).stringof)); 1148 }else static if(VOverloads.length == 1){ 1149 alias VOverloads[0] FN; 1150 }else static assert(0, 1151 format("%s.%s: cannot choose between %s", T.stringof, nom, 1152 VOverloads.stringof)); 1153 } 1154 static void call(string classname, T)() { 1155 alias PydTypeObject!T type; 1156 enum slot = "type.tp_as_sequence.sq_length"; 1157 mixin(autoInitializeMethods()); 1158 mixin(slot ~ " = &length_wrap!(T, Inner!T.FN).func;"); 1159 } 1160 template shim(size_t i,T) { 1161 // bah 1162 enum shim = ""; 1163 } 1164 } 1165 1166 1167 template param1(C) { 1168 template param1(T) {alias ParameterTypeTuple!(T.Inner!C .FN)[0] param1; } 1169 } 1170 1171 alias Pred!q{__traits(hasMember, A,"op")} IsOp; 1172 alias Pred!q{A.stringof.startsWith("OpUnary!")} IsUn; 1173 template IsBin(T...) { 1174 static if(T[0].stringof.startsWith("BinaryOperatorX!")) 1175 enum bool IsBin = !T[0].isRight; 1176 else 1177 enum bool IsBin = false; 1178 } 1179 template IsBinR(T...) { 1180 static if(T[0].stringof.startsWith("BinaryOperatorX!")) 1181 enum IsBinR = T[0].isRight; 1182 else 1183 enum IsBinR = false; 1184 } 1185 1186 // handle all operator overloads. Ops must only contain operator overloads. 1187 struct Operators(Ops...) { 1188 enum bool needs_shim = false; 1189 1190 template BinOp(string op, T) { 1191 alias Pred!(Replace!(q{A.op == "OP"},"OP",op)) IsThisOp; 1192 alias Filter!(IsThisOp, Ops) Ops0; 1193 alias Filter!(IsBin, Ops0) OpsL; 1194 alias staticMap!(param1!T, OpsL) OpsLparams; 1195 static assert(OpsL.length <= 1, 1196 Replace!("Cannot overload $T1 $OP x with types $T2", 1197 "$OP", op, "$T1", T.stringof, "$T2", OpsLparams.stringof)); 1198 alias Filter!(IsBinR, Ops0) OpsR; 1199 alias staticMap!(param1, OpsR) OpsRparams; 1200 static assert(OpsR.length <= 1, 1201 Replace!("Cannot overload x $OP $T1 with types $T2", 1202 "$OP", op, "$T1", T.stringof, "$T2", OpsRparams.stringof)); 1203 static assert(op[$-1] != '=' || OpsR.length == 0, 1204 "Cannot reflect assignment operator"); 1205 1206 static void call() { 1207 static if(OpsL.length + OpsR.length != 0) { 1208 alias PydTypeObject!T type; 1209 enum slot = binaryslots[op]; 1210 mixin(autoInitializeMethods()); 1211 static if(op == "in") { 1212 mixin(slot ~ " = &inop_wrap!(T, CW!OpsL, CW!OpsR).func;"); 1213 }else static if(op == "^^" || op == "^^=") { 1214 mixin(slot ~ " = &powop_wrap!(T, CW!OpsL, CW!OpsR).func;"); 1215 }else { 1216 mixin(slot ~ " = &binop_wrap!(T, CW!OpsL, CW!OpsR).func;"); 1217 } 1218 } 1219 } 1220 1221 } 1222 struct UnOp(string op, T) { 1223 alias Pred!(Replace!(q{A.op == "OP"},"OP",op)) IsThisOp; 1224 alias Filter!(IsUn, Filter!(IsThisOp, Ops)) Ops1; 1225 static assert(Ops1.length <= 1, 1226 Replace!("Cannot have overloads of $OP$T1", 1227 "$OP", op, "$T1", T.stringof)); 1228 static void call() { 1229 static if(Ops1.length != 0) { 1230 alias PydTypeObject!T type; 1231 alias Ops1[0] Ops1_0; 1232 alias Ops1_0.Inner!T .FN fn; 1233 enum slot = unaryslots[op]; 1234 mixin(autoInitializeMethods()); 1235 mixin(slot ~ " = &opfunc_unary_wrap!(T, fn).func;"); 1236 } 1237 } 1238 } 1239 1240 static void call(T)() { 1241 alias NoDuplicates!(staticMap!(Pred!"A.op", Ops)) str_op_tuple; 1242 enum binops = binaryslots.keys(); 1243 foreach(_op; str_op_tuple) { 1244 BinOp!(_op, T).call(); // noop if op is unary 1245 UnOp!(_op, T).call(); // noop if op is binary 1246 } 1247 } 1248 } 1249 1250 struct Constructors(string classname, Ctors...) { 1251 enum bool needs_shim = true; 1252 1253 static void call(T, Shim)() { 1254 alias PydTypeObject!T type; 1255 alias NewParamT!T U; 1256 static if(Ctors.length) { 1257 type.tp_init = &wrapped_ctors!(classname, T, Shim, Ctors).func; 1258 }else { 1259 // If a ctor wasn't supplied, try the default. 1260 // If the default ctor isn't available, and no ctors were supplied, 1261 // then this class cannot be instantiated from Python. 1262 // (Structs always use the default ctor.) 1263 static if (is(typeof(new U))) { 1264 static if (is(U == class)) { 1265 type.tp_init = &wrapped_init!(Shim).init; 1266 } else { 1267 type.tp_init = &wrapped_struct_init!(U).init; 1268 } 1269 } 1270 } 1271 } 1272 } 1273 1274 template IsDef(string pyname) { 1275 template IsDef(Params...) { 1276 static if(Params[0].stringof.startsWith("Def!") && 1277 __traits(hasMember,Params[0], "funcname")) { 1278 enum bool IsDef = (Params[0].funcname == pyname); 1279 }else{ 1280 enum bool IsDef = false; 1281 } 1282 } 1283 } 1284 struct Iterator(Params...) { 1285 alias Filter!(IsDef!"__iter__", Params) Iters; 1286 alias Filter!(IsDef!"next", Params) Nexts; 1287 enum bool needs_shim = false; 1288 static void call(T)() { 1289 alias PydTypeObject!T type; 1290 import std.range; 1291 static if(Iters.length == 1 && (Nexts.length == 1 || isInputRange!(ReturnType!(Iters[0].func)))) { 1292 version(Python_3_0_Or_Later) { 1293 }else{ 1294 type.tp_flags |= Py_TPFLAGS_HAVE_ITER; 1295 } 1296 type.tp_iter = &opiter_wrap!(T, Iters[0].func).func; 1297 static if(Nexts.length == 1) 1298 type.tp_iternext = &opiter_wrap!(T, Nexts[0].func).func; 1299 } 1300 } 1301 } 1302 1303 template IsOpIndex(P...) { 1304 enum bool IsOpIndex = P[0].stringof.startsWith("OpIndex!"); 1305 } 1306 template IsOpIndexAssign(P...) { 1307 enum bool IsOpIndexAssign = P[0].stringof.startsWith("OpIndexAssign!"); 1308 } 1309 template IsOpSlice(P...) { 1310 enum bool IsOpSlice = P[0].stringof.startsWith("OpSlice!"); 1311 } 1312 template IsOpSliceAssign(P...) { 1313 enum bool IsOpSliceAssign = P[0].stringof.startsWith("OpSliceAssign!"); 1314 } 1315 template IsLen(P...) { 1316 enum bool IsLen = P[0].stringof.startsWith("Len!"); 1317 } 1318 /* 1319 Extended slice syntax goes through mp_subscript, mp_ass_subscript, 1320 not sq_slice, sq_ass_slice. 1321 1322 TODO: Python's extended slicing is more powerful than D's. We should expose 1323 this. 1324 */ 1325 struct IndexSliceMerge(Params...) { 1326 alias Filter!(IsOpIndex, Params) OpIndexs; 1327 alias Filter!(IsOpIndexAssign, Params) OpIndexAssigns; 1328 alias Filter!(IsOpSlice, Params) OpSlices; 1329 alias Filter!(IsOpSliceAssign, Params) OpSliceAssigns; 1330 alias Filter!(IsLen, Params) Lens; 1331 1332 static assert(OpIndexs.length <= 1); 1333 static assert(OpIndexAssigns.length <= 1); 1334 static assert(OpSlices.length <= 1); 1335 static assert(OpSliceAssigns.length <= 1); 1336 1337 static void call(T)() { 1338 alias PydTypeObject!T type; 1339 static if(OpIndexs.length + OpSlices.length) { 1340 { 1341 enum slot = "type.tp_as_mapping.mp_subscript"; 1342 mixin(autoInitializeMethods()); 1343 mixin(slot ~ " = &op_func!(T);"); 1344 } 1345 } 1346 static if(OpIndexAssigns.length + OpSliceAssigns.length) { 1347 { 1348 enum slot = "type.tp_as_mapping.mp_ass_subscript"; 1349 mixin(autoInitializeMethods()); 1350 mixin(slot ~ " = &ass_func!(T);"); 1351 } 1352 } 1353 } 1354 1355 1356 static extern(C) PyObject* op_func(T)(PyObject* self, PyObject* key) { 1357 static if(OpIndexs.length) { 1358 version(Python_2_5_Or_Later) { 1359 Py_ssize_t i; 1360 if(!PyIndex_Check(key)) goto slice; 1361 i = PyNumber_AsSsize_t(key, PyExc_IndexError); 1362 }else{ 1363 C_long i; 1364 if(!PyInt_Check(key)) goto slice; 1365 i = PyLong_AsLong(key); 1366 } 1367 if(i == -1 && PyErr_Occurred()) { 1368 return null; 1369 } 1370 alias OpIndexs[0] OpIndex0; 1371 return opindex_wrap!(T, OpIndex0.Inner!T.FN).func(self, key); 1372 } 1373 slice: 1374 static if(OpSlices.length) { 1375 if(PySlice_Check(key)) { 1376 Py_ssize_t len = PyObject_Length(self); 1377 Py_ssize_t start, stop, step, slicelength; 1378 if(PySlice_GetIndicesEx(key, len, 1379 &start, &stop, &step, &slicelength) < 0) { 1380 return null; 1381 } 1382 if(step != 1) { 1383 PyErr_SetString(PyExc_TypeError, 1384 "slice steps not supported in D"); 1385 return null; 1386 } 1387 alias OpSlices[0] OpSlice0; 1388 return opslice_wrap!(T, OpSlice0.Inner!T.FN).func( 1389 self, start, stop); 1390 } 1391 } 1392 PyErr_SetString(PyExc_TypeError, format( 1393 "index type '%s' not supported\0", to!string(key.ob_type.tp_name)).ptr); 1394 return null; 1395 } 1396 1397 static extern(C) int ass_func(T)(PyObject* self, PyObject* key, 1398 PyObject* val) { 1399 static if(OpIndexAssigns.length) { 1400 version(Python_2_5_Or_Later) { 1401 Py_ssize_t i; 1402 if(!PyIndex_Check(key)) goto slice; 1403 i = PyNumber_AsSsize_t(key, PyExc_IndexError); 1404 }else{ 1405 C_long i; 1406 if(!PyInt_Check(key)) goto slice; 1407 i = PyLong_AsLong(key); 1408 } 1409 if(i == -1 && PyErr_Occurred()) { 1410 return -1; 1411 } 1412 alias OpIndexAssigns[0] OpIndexAssign0; 1413 return opindexassign_wrap!(T, OpIndexAssign0.Inner!T.FN).func( 1414 self, key, val); 1415 } 1416 slice: 1417 static if(OpSliceAssigns.length) { 1418 if(PySlice_Check(key)) { 1419 Py_ssize_t len = PyObject_Length(self); 1420 Py_ssize_t start, stop, step, slicelength; 1421 if(PySlice_GetIndicesEx(key, len, 1422 &start, &stop, &step, &slicelength) < 0) { 1423 return -1; 1424 } 1425 if(step != 1) { 1426 PyErr_SetString(PyExc_TypeError, 1427 "slice steps not supported in D"); 1428 return -1; 1429 } 1430 alias OpSliceAssigns[0] OpSliceAssign0; 1431 return opsliceassign_wrap!(T, OpSliceAssign0.Inner!T.FN).func( 1432 self, start, stop, val); 1433 } 1434 } 1435 PyErr_SetString(PyExc_TypeError, format( 1436 "assign index type '%s' not supported\0", to!string(key.ob_type.tp_name)).ptr); 1437 return -1; 1438 } 1439 } 1440 1441 /* 1442 Params: each param is a Type which supports the interface 1443 1444 Param.needs_shim == false => Param.call!(pyclassname, T) 1445 or 1446 Param.needs_shim == true => Param.call!(pyclassname,T, Shim) 1447 1448 performs appropriate mutations to the PyTypeObject 1449 1450 Param.shim!(i,T) for i : Params[i] == Param 1451 1452 generates a string to be mixed in to Shim type 1453 1454 where T is the type being wrapped, Shim is the wrapped type 1455 1456 */ 1457 1458 /** 1459 Wrap a class. 1460 1461 Params: 1462 T = The class being wrapped. 1463 Params = Mixture of definitions of members of T to be wrapped and 1464 optional arguments. 1465 Concerning optional arguments, accepts PyName!(pyname), ModuleName!(modulename), and Docstring!(docstring). 1466 pyname = The name of the class as it will appear in Python. Defaults to 1467 T's name in D 1468 modulename = The name of the python module in which the wrapped class 1469 resides. Defaults to "". 1470 docstring = The class's docstring. Defaults to "". 1471 */ 1472 void wrap_class(T, Params...)() { 1473 alias Args!("","", __traits(identifier,T), "",Params) args; 1474 _wrap_class!(T, args.pyname, args.docstring, args.modulename, args.rem).wrap_class(); 1475 } 1476 template _wrap_class(_T, string name, string docstring, string modulename, Params...) { 1477 import std.conv; 1478 import util.typelist; 1479 static if (is(_T == class)) { 1480 //pragma(msg, "wrap_class: " ~ name); 1481 alias pyd.make_wrapper.make_wrapper!(_T, Params).wrapper shim_class; 1482 //alias W.wrapper shim_class; 1483 alias _T T; 1484 } else { 1485 //pragma(msg, "wrap_struct: '" ~ name ~ "'"); 1486 alias void shim_class; 1487 alias _T* T; 1488 } 1489 void wrap_class() { 1490 if(!Pyd_Module_p(modulename)) { 1491 if(should_defer_class_wrap(modulename, name)) { 1492 defer_class_wrap(modulename, name, toDelegate(&wrap_class)); 1493 return; 1494 } 1495 } 1496 alias PydTypeObject!(T) type; 1497 init_PyTypeObject!T(type); 1498 1499 foreach (param; Params) { 1500 static if (param.needs_shim) { 1501 param.call!(name, T, shim_class)(); 1502 } else { 1503 param.call!(name,T)(); 1504 } 1505 } 1506 1507 assert(Pyd_Module_p(modulename) !is null, "Must initialize module '" ~ modulename ~ "' before wrapping classes."); 1508 string module_name = to!string(PyModule_GetName(Pyd_Module_p(modulename))); 1509 1510 ////////////////// 1511 // Basic values // 1512 ////////////////// 1513 Py_SET_TYPE(&type, &PyType_Type); 1514 type.tp_basicsize = PyObject.sizeof; 1515 type.tp_doc = (docstring ~ "\0").ptr; 1516 version(Python_3_0_Or_Later) { 1517 type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; 1518 }else{ 1519 type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES; 1520 } 1521 //type.tp_repr = &wrapped_repr!(T).repr; 1522 type.tp_methods = wrapped_method_list!(T).ptr; 1523 type.tp_name = (module_name ~ "." ~ name ~ "\0").ptr; 1524 1525 ///////////////// 1526 // Inheritance // 1527 ///////////////// 1528 // Inherit classes from their wrapped superclass. 1529 static if (is(T B == super)) { 1530 foreach (C; B) { 1531 static if (is(C == class) && !is(C == Object)) { 1532 if (is_wrapped!(C)) { 1533 type.tp_base = &PydTypeObject!(C); 1534 } 1535 } 1536 } 1537 } 1538 1539 //////////////////////// 1540 // Operator overloads // 1541 //////////////////////// 1542 1543 Operators!(Filter!(IsOp, Params)).call!T(); 1544 // its just that simple. 1545 1546 IndexSliceMerge!(Params).call!T(); 1547 // indexing and slicing aren't exactly simple. 1548 1549 ////////////////////////// 1550 // Constructor wrapping // 1551 ////////////////////////// 1552 Constructors!(name, Filter!(IsInit, Params)).call!(T, shim_class)(); 1553 1554 ////////////////////////// 1555 // Iterator wrapping // 1556 ////////////////////////// 1557 Iterator!(Params).call!(T)(); 1558 1559 1560 ////////////////// 1561 // Finalization // 1562 ////////////////// 1563 if (PyType_Ready(&type) < 0) { 1564 throw new Exception("Couldn't ready wrapped type!"); 1565 } 1566 Py_INCREF(cast(PyObject*)&type); 1567 PyModule_AddObject(Pyd_Module_p(modulename), (name~"\0").ptr, cast(PyObject*)&type); 1568 1569 is_wrapped!(T) = true; 1570 static if (is(T == class)) { 1571 is_wrapped!(shim_class) = true; 1572 wrapped_classes[T.classinfo] = &type; 1573 wrapped_classes[shim_class.classinfo] = &type; 1574 } 1575 } 1576 } 1577