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