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