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 util.typelist; 35 import 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 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 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 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 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 bool IsPyBinary(string op) { 600 foreach(_op, slot; binaryslots) { 601 if (op[$-1] != '=' && op == _op) return true; 602 } 603 return false; 604 } 605 bool IsPyAsg(string op0) { 606 auto op = op0~"="; 607 foreach(_op, slot; binaryslots) { 608 if (op == _op) return true; 609 } 610 return false; 611 } 612 613 enum unaryslots = [ 614 "+": "type.tp_as_number.nb_positive", 615 "-": "type.tp_as_number.nb_negative", 616 "~": "type.tp_as_number.nb_invert", 617 ]; 618 619 bool IsPyUnary(string op) { 620 foreach(_op, slot; unaryslots) { 621 if(op == _op) return true; 622 } 623 return false; 624 } 625 626 // string mixin to initialize tp_as_number or tp_as_sequence or tp_as_mapping 627 // if necessary. Scope mixed in must have these variables: 628 // slot: a value from binaryslots or unaryslots 629 // type: a PyObjectType. 630 string autoInitializeMethods() { 631 return q{ 632 import std.algorithm: countUntil; 633 static if(countUntil(slot, "tp_as_number") != -1) { 634 if(type.tp_as_number is null) 635 type.tp_as_number = new PyNumberMethods; 636 }else static if(countUntil(slot, "tp_as_sequence") != -1) { 637 if(type.tp_as_sequence is null) 638 type.tp_as_sequence = new PySequenceMethods; 639 }else static if(countUntil(slot, "tp_as_mapping") != -1) { 640 if(type.tp_as_mapping is null) 641 type.tp_as_mapping = new PyMappingMethods; 642 } 643 }; 644 } 645 646 private struct Guess{} 647 648 struct BinaryOperatorX(string _op, bool isR, rhs_t) { 649 enum op = _op; 650 enum isRight = isR; 651 652 static if(isR) enum nom = "opBinaryRight"; 653 else enum nom = "opBinary"; 654 655 enum bool needs_shim = false; 656 657 template Inner(C) { 658 import std..string: format; 659 660 enum fn_str1 = "Alias!(C."~nom~"!(op))"; 661 enum fn_str2 = "C."~nom~"!(op,rhs_t)"; 662 enum string OP = op; 663 static if(!__traits(hasMember, C, nom)) { 664 static assert(0, C.stringof ~ " has no "~(isR ?"reflected ":"")~ 665 "binary operator overloads"); 666 } 667 template Alias(alias fn) { 668 alias fn Alias; 669 } 670 static if(is(typeof(mixin(fn_str1)) == function)) { 671 alias ParameterTypeTuple!(typeof(mixin(fn_str1)))[0] RHS_T; 672 alias ReturnType!(typeof(mixin(fn_str1))) RET_T; 673 mixin("alias " ~ fn_str1 ~ " FN;"); 674 static if(!is(rhs_t == Guess)) 675 static assert(is(RHS_T == rhs_t), 676 format("expected typeof(rhs) = %s, found %s", 677 rhs.stringof, RHS_T.stringof)); 678 }else static if(is(rhs_t == Guess)) { 679 static assert(false, 680 format("Operator %s: Cannot determine type of rhs", op)); 681 } else static if(is(typeof(mixin(fn_str2)) == function)) { 682 alias rhs_t RHS_T; 683 alias ReturnType!(typeof(mixin(fn_str2))) RET_T; 684 mixin("alias "~fn_str2~" FN;"); 685 } else static assert(false, "Cannot get operator overload"); 686 } 687 688 static void call(string classname, T)() { 689 // can't handle __op__ __rop__ pairs here 690 } 691 692 template shim(size_t i, T) { 693 // bah 694 enum shim = ""; 695 } 696 } 697 698 /** 699 Wrap a binary operator overload. 700 701 Example: 702 --- 703 class Foo{ 704 int _j; 705 int opBinary(string op)(int i) if(op == "+"){ 706 return i+_j; 707 } 708 int opBinaryRight(string op)(int i) if(op == "+"){ 709 return i+_j; 710 } 711 } 712 713 class_wrap!(Foo, 714 OpBinary!("+"), 715 OpBinaryRight!("+")); 716 --- 717 718 Params: 719 op = Operator to wrap 720 rhs_t = (optional) Type of opBinary's parameter for disambiguation if 721 there are multiple overloads. 722 Bugs: 723 Issue 8602 prevents disambiguation for case X opBinary(string op, T)(T t); 724 */ 725 template OpBinary(string op, rhs_t = Guess) if(IsPyBinary(op) && op != "in"){ 726 alias BinaryOperatorX!(op, false, rhs_t) OpBinary; 727 } 728 729 /// ditto 730 template OpBinaryRight(string op, lhs_t = Guess) if(IsPyBinary(op)) { 731 alias BinaryOperatorX!(op, true, lhs_t) OpBinaryRight; 732 } 733 734 /** 735 Wrap a unary operator overload. 736 */ 737 struct OpUnary(string _op) if(IsPyUnary(_op)) { 738 enum op = _op; 739 enum bool needs_shim = false; 740 741 template Inner(C) { 742 enum string OP = op; 743 static if(!__traits(hasMember, C, "opUnary")) { 744 static assert(0, C.stringof ~ " has no unary operator overloads"); 745 } 746 static if(is(typeof(C.init.opUnary!(op)) == function)) { 747 alias ReturnType!(C.opUnary!(op)) RET_T; 748 alias C.opUnary!(op) FN; 749 } else static assert(false, "Cannot get operator overload"); 750 } 751 static void call(string classname, T)() { 752 alias PydTypeObject!T type; 753 enum slot = unaryslots[op]; 754 mixin(autoInitializeMethods()); 755 mixin(slot ~ " = &opfunc_unary_wrap!(T, Inner!T .FN).func;"); 756 } 757 template shim(size_t i,T) { 758 // bah 759 enum shim = ""; 760 } 761 } 762 763 /** 764 Wrap an operator assignment overload. 765 766 Example: 767 --- 768 class Foo{ 769 int _j; 770 void opOpAssign(string op)(int i) if(op == "+"){ 771 _j = i; 772 } 773 } 774 775 class_wrap!(Foo, 776 OpAssign!("+")); 777 --- 778 Params: 779 op = Base operator to wrap 780 rhs_t = (optional) Type of opOpAssign's parameter for disambiguation if 781 there are multiple overloads. 782 */ 783 struct OpAssign(string _op, rhs_t = Guess) if(IsPyAsg(_op)) { 784 enum op = _op~"="; 785 786 enum bool needs_shim = false; 787 788 template Inner(C) { 789 enum string OP = op; 790 static if(!__traits(hasMember, C, "opOpAssign")) { 791 static assert(0, C.stringof ~ " has no operator assignment overloads"); 792 } 793 static if(is(typeof(C.init.opOpAssign!(_op)) == function)) { 794 alias ParameterTypeTuple!(typeof(C.opOpAssign!(_op)))[0] RHS_T; 795 alias ReturnType!(typeof(C.opOpAssign!(_op))) RET_T; 796 alias C.opOpAssign!(_op) FN; 797 static if(!is(rhs_t == Guess)) 798 static assert(is(RHS_T == rhs_t), 799 format("expected typeof(rhs) = %s, found %s", 800 rhs.stringof, RHS_T.stringof)); 801 }else static if(is(rhs_t == Guess)) { 802 static assert(false, "Cannot determine type of rhs"); 803 } else static if(is(typeof(C.opOpAssign!(_op,rhs_t)) == function)) { 804 alias rhs_t RHS_T; 805 alias ReturnType!(typeof(C.opOpAssign!(_op,rhs_t))) RET_T; 806 alias C.opOpAssign!(_op,rhs_t) FN; 807 } else static assert(false, "Cannot get operator assignment overload"); 808 } 809 static void call(string classname, T)() { 810 alias PydTypeObject!T type; 811 enum slot = binaryslots[op]; 812 mixin(autoInitializeMethods()); 813 alias CW!(TypeTuple!(OpAssign)) OpAsg; 814 alias CW!(TypeTuple!()) Nop; 815 static if(op == "^^=") 816 mixin(slot ~ " = &powopasg_wrap!(T, Inner!T.FN).func;"); 817 else 818 mixin(slot ~ " = &binopasg_wrap!(T, Inner!T.FN).func;"); 819 } 820 821 template shim(size_t i,T) { 822 // bah 823 enum shim = ""; 824 } 825 } 826 827 // struct types could probably take any parameter type 828 // class types must take Object 829 /** 830 Wrap opCmp. 831 832 Params: 833 rhs_t = (optional) Type of opCmp's parameter for disambiguation if there 834 are multiple overloads (for classes it will always be Object). 835 */ 836 struct OpCompare(_rhs_t = Guess) { 837 enum bool needs_shim = false; 838 839 840 template Inner(C) { 841 import std..string: format; 842 static if(is(_rhs_t == Guess) && is(C == class)) { 843 alias Object rhs_t; 844 }else { 845 alias _rhs_t rhs_t; 846 } 847 static if(!__traits(hasMember, C, "opCmp")) { 848 static assert(0, C.stringof ~ " has no comparison operator overloads"); 849 } 850 static if(!is(typeof(C.init.opCmp) == function)) { 851 static assert(0, format("why is %s.opCmp not a function?",C)); 852 } 853 alias TypeTuple!(__traits(getOverloads, C, "opCmp")) Overloads; 854 static if(is(rhs_t == Guess) && Overloads.length > 1) { 855 static assert(0, format("Cannot choose between %s", Overloads)); 856 }else static if(Overloads.length == 1) { 857 static if(!is(rhs_t == Guess) && 858 !is(ParameterTypeTuple!(Overloads[0])[0] == rhs_t)) { 859 static assert(0, format("%s.opCmp: expected param %s, got %s", 860 C, rhs_t, ParameterTypeTuple!(Overloads[0]))); 861 }else{ 862 alias Overloads[0] FN; 863 } 864 }else{ 865 template IsDesiredOverload(alias fn) { 866 enum bool IsDesiredOverload = is(ParameterTypeTuple!(fn)[0] == rhs_t); 867 } 868 alias Filter!(IsDesiredOverload, Overloads) Overloads1; 869 static assert(Overloads1.length == 1, 870 format("Cannot choose between %s", Overloads1)); 871 alias Overloads1[0] FN; 872 } 873 } 874 static void call(string classname, T)() { 875 alias PydTypeObject!T type; 876 alias ApplyConstness!(T, constness!(typeof(Inner!T.FN))) cT; 877 type.tp_richcompare = &rich_opcmp_wrap!(cT, Inner!T.FN).func; 878 } 879 template shim(size_t i,T) { 880 // bah 881 enum shim = ""; 882 } 883 } 884 885 /** 886 Wrap opIndex, opIndexAssign. 887 888 Params: 889 index_t = (optional) Types of opIndex's parameters for disambiguation if 890 there are multiple overloads. 891 */ 892 struct OpIndex(index_t...) { 893 enum bool needs_shim = false; 894 template Inner(C) { 895 import std..string: format; 896 897 static if(!__traits(hasMember, C, "opIndex")) { 898 static assert(0, C.stringof ~ " has no index operator overloads"); 899 } 900 static if(is(typeof(C.init.opIndex) == function)) { 901 alias TypeTuple!(__traits(getOverloads, C, "opIndex")) Overloads; 902 static if(index_t.length == 0 && Overloads.length > 1) { 903 static assert(0, 904 format("%s.opIndex: Cannot choose between %s", 905 C.stringof,Overloads.stringof)); 906 }else static if(index_t.length == 0) { 907 alias Overloads[0] FN; 908 }else{ 909 template IsDesiredOverload(alias fn) { 910 enum bool IsDesiredOverload = is(ParameterTypeTuple!fn == index_t); 911 } 912 alias Filter!(IsDesiredOverload, Overloads) Overloads1; 913 static assert(Overloads1.length == 1, 914 format("%s.opIndex: Cannot choose between %s", 915 C.stringof,Overloads1.stringof)); 916 alias Overloads1[0] FN; 917 } 918 }else static if(is(typeof(C.init.opIndex!(index_t)) == function)) { 919 alias C.opIndex!(index_t) FN; 920 }else{ 921 static assert(0, 922 format("cannot get a handle on %s.opIndex", C.stringof)); 923 } 924 } 925 static void call(string classname, T)() { 926 /* 927 alias PydTypeObject!T type; 928 enum slot = "type.tp_as_mapping.mp_subscript"; 929 mixin(autoInitializeMethods()); 930 mixin(slot ~ " = &opindex_wrap!(T, Inner!T.FN).func;"); 931 */ 932 } 933 template shim(size_t i,T) { 934 // bah 935 enum shim = ""; 936 } 937 } 938 939 /// ditto 940 struct OpIndexAssign(index_t...) { 941 static assert(index_t.length != 1, 942 "opIndexAssign must have at least 2 parameters"); 943 enum bool needs_shim = false; 944 template Inner(C) { 945 import std..string: format; 946 947 static if(!__traits(hasMember, C, "opIndexAssign")) { 948 static assert(0, C.stringof ~ " has no index operator overloads"); 949 } 950 static if(is(typeof(C.init.opIndex) == function)) { 951 alias TypeTuple!(__traits(getOverloads, C, "opIndexAssign")) Overloads; 952 template IsValidOverload(alias fn) { 953 enum bool IsValidOverload = ParameterTypeTuple!fn.length >= 2; 954 } 955 alias Filter!(IsValidOverload, Overloads) VOverloads; 956 static if(VOverloads.length == 0 && Overloads.length != 0) 957 static assert(0, 958 "opIndexAssign must have at least 2 parameters"); 959 static if(index_t.length == 0 && VOverloads.length > 1) { 960 static assert(0, 961 format("%s.opIndexAssign: Cannot choose between %s", 962 C.stringof,VOverloads.stringof)); 963 }else static if(index_t.length == 0) { 964 alias VOverloads[0] FN; 965 }else{ 966 template IsDesiredOverload(alias fn) { 967 enum bool IsDesiredOverload = is(ParameterTypeTuple!fn == index_t); 968 } 969 alias Filter!(IsDesiredOverload, VOverloads) Overloads1; 970 static assert(Overloads1.length == 1, 971 format("%s.opIndex: Cannot choose between %s", 972 C.stringof,Overloads1.stringof)); 973 alias Overloads1[0] FN; 974 } 975 }else static if(is(typeof(C.init.opIndexAssign!(index_t)) == function)) { 976 alias C.opIndexAssign!(index_t) FN; 977 }else{ 978 static assert(0, 979 format("cannot get a handle on %s.opIndexAssign", C.stringof)); 980 } 981 } 982 static void call(string classname, T)() { 983 /* 984 alias PydTypeObject!T type; 985 enum slot = "type.tp_as_mapping.mp_ass_subscript"; 986 mixin(autoInitializeMethods()); 987 mixin(slot ~ " = &opindexassign_wrap!(T, Inner!T.FN).func;"); 988 */ 989 } 990 template shim(size_t i,T) { 991 // bah 992 enum shim = ""; 993 } 994 } 995 996 /** 997 Wrap opSlice. 998 999 Requires signature 1000 --- 1001 Foo.opSlice(Py_ssize_t, Py_ssize_t); 1002 --- 1003 This is a limitation of the C/Python API. 1004 */ 1005 struct OpSlice() { 1006 enum bool needs_shim = false; 1007 template Inner(C) { 1008 import std..string: format; 1009 1010 static if(!__traits(hasMember, C, "opSlice")) { 1011 static assert(0, C.stringof ~ " has no slice operator overloads"); 1012 } 1013 static if(is(typeof(C.init.opSlice) == function)) { 1014 alias TypeTuple!(__traits(getOverloads, C, "opSlice")) Overloads; 1015 template IsDesiredOverload(alias fn) { 1016 enum bool IsDesiredOverload = is(ParameterTypeTuple!fn == 1017 TypeTuple!(Py_ssize_t,Py_ssize_t)); 1018 } 1019 alias Filter!(IsDesiredOverload, Overloads) Overloads1; 1020 static assert(Overloads1.length != 0, 1021 format("%s.opSlice: must have overload %s", 1022 C.stringof,TypeTuple!(Py_ssize_t,Py_ssize_t).stringof)); 1023 static assert(Overloads1.length == 1, 1024 format("%s.opSlice: cannot choose between %s", 1025 C.stringof,Overloads1.stringof)); 1026 alias Overloads1[0] FN; 1027 }else{ 1028 static assert(0, format("cannot get a handle on %s.opSlice", 1029 C.stringof)); 1030 } 1031 } 1032 static void call(string classname, T)() { 1033 /* 1034 alias PydTypeObject!T type; 1035 enum slot = "type.tp_as_sequence.sq_slice"; 1036 mixin(autoInitializeMethods()); 1037 mixin(slot ~ " = &opslice_wrap!(T, Inner!T.FN).func;"); 1038 */ 1039 } 1040 template shim(size_t i,T) { 1041 // bah 1042 enum shim = ""; 1043 } 1044 } 1045 1046 /** 1047 Wrap opSliceAssign. 1048 1049 Requires signature 1050 --- 1051 Foo.opSliceAssign(Value,Py_ssize_t, Py_ssize_t); 1052 --- 1053 This is a limitation of the C/Python API. 1054 */ 1055 struct OpSliceAssign(rhs_t = Guess) { 1056 enum bool needs_shim = false; 1057 template Inner(C) { 1058 static if(!__traits(hasMember, C, "opSliceAssign")) { 1059 static assert(0, C.stringof ~ " has no slice assignment operator overloads"); 1060 } 1061 static if(is(typeof(C.init.opSliceAssign) == function)) { 1062 alias TypeTuple!(__traits(getOverloads, C, "opSliceAssign")) Overloads; 1063 template IsDesiredOverload(alias fn) { 1064 alias ParameterTypeTuple!fn ps; 1065 enum bool IsDesiredOverload = 1066 is(ps[1..3] == TypeTuple!(Py_ssize_t,Py_ssize_t)); 1067 } 1068 alias Filter!(IsDesiredOverload, Overloads) Overloads1; 1069 static assert(Overloads1.length != 0, 1070 format("%s.opSliceAssign: must have overload %s", 1071 C.stringof,TypeTuple!(Guess,Py_ssize_t,Py_ssize_t).stringof)); 1072 static if(is(rhs_t == Guess)) { 1073 static assert(Overloads1.length == 1, 1074 format("%s.opSliceAssign: cannot choose between %s", 1075 C.stringof,Overloads1.stringof)); 1076 alias Overloads1[0] FN; 1077 }else{ 1078 template IsDesiredOverload2(alias fn) { 1079 alias ParameterTypeTuple!fn ps; 1080 enum bool IsDesiredOverload2 = is(ps[0] == rhs_t); 1081 } 1082 alias Filter!(IsDesiredOverload2, Overloads1) Overloads2; 1083 static assert(Overloads2.length == 1, 1084 format("%s.opSliceAssign: cannot choose between %s", 1085 C.stringof,Overloads2.stringof)); 1086 alias Overloads2[0] FN; 1087 } 1088 }else{ 1089 static assert(0, format("cannot get a handle on %s.opSlice", 1090 C.stringof)); 1091 } 1092 } 1093 static void call(string classname, T)() { 1094 /* 1095 alias PydTypeObject!T type; 1096 enum slot = "type.tp_as_sequence.sq_ass_slice"; 1097 mixin(autoInitializeMethods()); 1098 mixin(slot ~ " = &opsliceassign_wrap!(T, Inner!T.FN).func;"); 1099 */ 1100 } 1101 template shim(size_t i,T) { 1102 // bah 1103 enum shim = ""; 1104 } 1105 } 1106 1107 /** 1108 wrap opCall. The parameter types of opCall must be specified. 1109 */ 1110 struct OpCall(Args_t...) { 1111 enum bool needs_shim = false; 1112 1113 template Inner(T) { 1114 import std..string: format; 1115 1116 alias TypeTuple!(__traits(getOverloads, T, "opCall")) Overloads; 1117 template IsDesiredOverload(alias fn) { 1118 alias ParameterTypeTuple!fn ps; 1119 enum bool IsDesiredOverload = is(ps == Args_t); 1120 } 1121 alias Filter!(IsDesiredOverload, Overloads) VOverloads; 1122 static if(VOverloads.length == 0) { 1123 static assert(0, 1124 format("%s.opCall: cannot find signature %s", T.stringof, 1125 Args_t.stringof)); 1126 }else static if(VOverloads.length == 1){ 1127 alias VOverloads[0] FN; 1128 }else static assert(0, 1129 format("%s.%s: cannot choose between %s", T.stringof, nom, 1130 VOverloads.stringof)); 1131 } 1132 static void call(string classname, T)() { 1133 alias PydTypeObject!T type; 1134 alias Inner!T.FN fn; 1135 alias ApplyConstness!(T, constness!(typeof(fn))) cT; 1136 type.tp_call = &opcall_wrap!(cT, fn).func; 1137 } 1138 template shim(size_t i,T) { 1139 // bah 1140 enum shim = ""; 1141 } 1142 } 1143 1144 /** 1145 Wraps Foo.length or another function as python's ___len__ function. 1146 1147 Requires signature 1148 --- 1149 Py_ssize_t length(); 1150 --- 1151 This is a limitation of the C/Python API. 1152 */ 1153 template Len() { 1154 alias _Len!() Len; 1155 } 1156 1157 /// ditto 1158 template Len(alias fn) { 1159 alias _Len!(fn) Len; 1160 } 1161 1162 struct _Len(fnt...) { 1163 enum bool needs_shim = false; 1164 template Inner(T) { 1165 import std..string: format; 1166 1167 static if(fnt.length == 0) { 1168 enum nom = "length"; 1169 }else{ 1170 enum nom = __traits(identifier, fnt[0]); 1171 } 1172 alias TypeTuple!(__traits(getOverloads, T, nom)) Overloads; 1173 template IsDesiredOverload(alias fn) { 1174 alias ParameterTypeTuple!fn ps; 1175 alias ReturnType!fn rt; 1176 enum bool IsDesiredOverload = isImplicitlyConvertible!(rt,Py_ssize_t) && ps.length == 0; 1177 } 1178 alias Filter!(IsDesiredOverload, Overloads) VOverloads; 1179 static if(VOverloads.length == 0 && Overloads.length != 0) { 1180 static assert(0, 1181 format("%s.%s must have signature %s", T.stringof, nom, 1182 (Py_ssize_t function()).stringof)); 1183 }else static if(VOverloads.length == 1){ 1184 alias VOverloads[0] FN; 1185 }else static assert(0, 1186 format("%s.%s: cannot choose between %s", T.stringof, nom, 1187 VOverloads.stringof)); 1188 } 1189 static void call(string classname, T)() { 1190 alias PydTypeObject!T type; 1191 enum slot = "type.tp_as_sequence.sq_length"; 1192 mixin(autoInitializeMethods()); 1193 mixin(slot ~ " = &length_wrap!(T, Inner!T.FN).func;"); 1194 } 1195 template shim(size_t i,T) { 1196 // bah 1197 enum shim = ""; 1198 } 1199 } 1200 1201 1202 template param1(C) { 1203 template param1(T) {alias ParameterTypeTuple!(T.Inner!C .FN)[0] param1; } 1204 } 1205 1206 enum IsOp(A) = __traits(hasMember, A, "op"); 1207 1208 template IsUn(A) { 1209 import std.algorithm: startsWith; 1210 enum IsUn = A.stringof.startsWith("OpUnary!"); 1211 } 1212 1213 template IsBin(T...) { 1214 import std.algorithm: startsWith; 1215 static if(T[0].stringof.startsWith("BinaryOperatorX!")) 1216 enum bool IsBin = !T[0].isRight; 1217 else 1218 enum bool IsBin = false; 1219 } 1220 template IsBinR(T...) { 1221 import std.algorithm: startsWith; 1222 static if(T[0].stringof.startsWith("BinaryOperatorX!")) 1223 enum IsBinR = T[0].isRight; 1224 else 1225 enum IsBinR = false; 1226 } 1227 1228 // handle all operator overloads. Ops must only contain operator overloads. 1229 struct Operators(Ops...) { 1230 import util.replace: Replace; 1231 enum bool needs_shim = false; 1232 1233 template BinOp(string op, T) { 1234 enum IsThisOp(A) = A.op == op; 1235 alias Filter!(IsThisOp, Ops) Ops0; 1236 alias Filter!(IsBin, Ops0) OpsL; 1237 alias staticMap!(param1!T, OpsL) OpsLparams; 1238 static assert(OpsL.length <= 1, 1239 Replace!("Cannot overload $T1 $OP x with types $T2", 1240 "$OP", op, "$T1", T.stringof, "$T2", OpsLparams.stringof)); 1241 alias Filter!(IsBinR, Ops0) OpsR; 1242 alias staticMap!(param1, OpsR) OpsRparams; 1243 static assert(OpsR.length <= 1, 1244 Replace!("Cannot overload x $OP $T1 with types $T2", 1245 "$OP", op, "$T1", T.stringof, "$T2", OpsRparams.stringof)); 1246 static assert(op[$-1] != '=' || OpsR.length == 0, 1247 "Cannot reflect assignment operator"); 1248 1249 static void call() { 1250 static if(OpsL.length + OpsR.length != 0) { 1251 alias PydTypeObject!T type; 1252 enum slot = binaryslots[op]; 1253 mixin(autoInitializeMethods()); 1254 static if(op == "in") { 1255 mixin(slot ~ " = &inop_wrap!(T, CW!OpsL, CW!OpsR).func;"); 1256 }else static if(op == "^^" || op == "^^=") { 1257 mixin(slot ~ " = &powop_wrap!(T, CW!OpsL, CW!OpsR).func;"); 1258 }else { 1259 mixin(slot ~ " = &binop_wrap!(T, CW!OpsL, CW!OpsR).func;"); 1260 } 1261 } 1262 } 1263 1264 } 1265 struct UnOp(string op, T) { 1266 import util.replace: Replace; 1267 enum IsThisOp(A) = A.op == op; 1268 alias Filter!(IsUn, Filter!(IsThisOp, Ops)) Ops1; 1269 static assert(Ops1.length <= 1, 1270 Replace!("Cannot have overloads of $OP$T1", 1271 "$OP", op, "$T1", T.stringof)); 1272 static void call() { 1273 static if(Ops1.length != 0) { 1274 alias PydTypeObject!T type; 1275 alias Ops1[0] Ops1_0; 1276 alias Ops1_0.Inner!T .FN fn; 1277 enum slot = unaryslots[op]; 1278 mixin(autoInitializeMethods()); 1279 mixin(slot ~ " = &opfunc_unary_wrap!(T, fn).func;"); 1280 } 1281 } 1282 } 1283 1284 static void call(T)() { 1285 enum GetOp(A) = A.op; 1286 alias NoDuplicates!(staticMap!(GetOp, Ops)) str_op_tuple; 1287 enum binops = binaryslots.keys(); 1288 foreach(_op; str_op_tuple) { 1289 BinOp!(_op, T).call(); // noop if op is unary 1290 UnOp!(_op, T).call(); // noop if op is binary 1291 } 1292 } 1293 } 1294 1295 struct Constructors(string classname, Ctors...) { 1296 enum bool needs_shim = true; 1297 1298 static void call(T, Shim)() { 1299 alias PydTypeObject!T type; 1300 alias NewParamT!T U; 1301 static if(Ctors.length) { 1302 type.tp_init = &wrapped_ctors!(classname, T, Shim, Ctors).func; 1303 }else { 1304 // If a ctor wasn't supplied, try the default. 1305 // If the default ctor isn't available, and no ctors were supplied, 1306 // then this class cannot be instantiated from Python. 1307 // (Structs always use the default ctor.) 1308 static if (is(typeof(new U))) { 1309 static if (is(U == class)) { 1310 type.tp_init = &wrapped_init!(Shim).init; 1311 } else { 1312 type.tp_init = &wrapped_struct_init!(U).init; 1313 } 1314 } 1315 } 1316 } 1317 } 1318 1319 template IsDef(string pyname) { 1320 template IsDef(Params...) { 1321 import std.algorithm: startsWith; 1322 static if(Params[0].stringof.startsWith("Def!") && 1323 __traits(hasMember,Params[0], "funcname")) { 1324 enum bool IsDef = (Params[0].funcname == pyname); 1325 }else{ 1326 enum bool IsDef = false; 1327 } 1328 } 1329 } 1330 struct Iterator(Params...) { 1331 alias Filter!(IsDef!"__iter__", Params) Iters; 1332 alias Filter!(IsDef!"next", Params) Nexts; 1333 enum bool needs_shim = false; 1334 static void call(T)() { 1335 alias PydTypeObject!T type; 1336 import std.range; 1337 static if(Iters.length == 1 && (Nexts.length == 1 || isInputRange!(ReturnType!(Iters[0].func)))) { 1338 version(Python_3_0_Or_Later) { 1339 }else{ 1340 type.tp_flags |= Py_TPFLAGS_HAVE_ITER; 1341 } 1342 type.tp_iter = &opiter_wrap!(T, Iters[0].func).func; 1343 static if(Nexts.length == 1) 1344 type.tp_iternext = &opiter_wrap!(T, Nexts[0].func).func; 1345 } 1346 } 1347 } 1348 1349 template IsOpIndex(P...) { 1350 import std.algorithm: startsWith; 1351 enum bool IsOpIndex = P[0].stringof.startsWith("OpIndex!"); 1352 } 1353 template IsOpIndexAssign(P...) { 1354 import std.algorithm: startsWith; 1355 enum bool IsOpIndexAssign = P[0].stringof.startsWith("OpIndexAssign!"); 1356 } 1357 template IsOpSlice(P...) { 1358 import std.algorithm: startsWith; 1359 enum bool IsOpSlice = P[0].stringof.startsWith("OpSlice!"); 1360 } 1361 template IsOpSliceAssign(P...) { 1362 import std.algorithm: startsWith; 1363 enum bool IsOpSliceAssign = P[0].stringof.startsWith("OpSliceAssign!"); 1364 } 1365 template IsLen(P...) { 1366 import std.algorithm: startsWith; 1367 enum bool IsLen = P[0].stringof.startsWith("Len!"); 1368 } 1369 /* 1370 Extended slice syntax goes through mp_subscript, mp_ass_subscript, 1371 not sq_slice, sq_ass_slice. 1372 1373 TODO: Python's extended slicing is more powerful than D's. We should expose 1374 this. 1375 */ 1376 struct IndexSliceMerge(Params...) { 1377 alias Filter!(IsOpIndex, Params) OpIndexs; 1378 alias Filter!(IsOpIndexAssign, Params) OpIndexAssigns; 1379 alias Filter!(IsOpSlice, Params) OpSlices; 1380 alias Filter!(IsOpSliceAssign, Params) OpSliceAssigns; 1381 alias Filter!(IsLen, Params) Lens; 1382 1383 static assert(OpIndexs.length <= 1); 1384 static assert(OpIndexAssigns.length <= 1); 1385 static assert(OpSlices.length <= 1); 1386 static assert(OpSliceAssigns.length <= 1); 1387 1388 static void call(T)() { 1389 alias PydTypeObject!T type; 1390 static if(OpIndexs.length + OpSlices.length) { 1391 { 1392 enum slot = "type.tp_as_mapping.mp_subscript"; 1393 mixin(autoInitializeMethods()); 1394 mixin(slot ~ " = &op_func!(T);"); 1395 } 1396 } 1397 static if(OpIndexAssigns.length + OpSliceAssigns.length) { 1398 { 1399 enum slot = "type.tp_as_mapping.mp_ass_subscript"; 1400 mixin(autoInitializeMethods()); 1401 mixin(slot ~ " = &ass_func!(T);"); 1402 } 1403 } 1404 } 1405 1406 1407 static extern(C) PyObject* op_func(T)(PyObject* self, PyObject* key) { 1408 import std..string: format; 1409 1410 static if(OpIndexs.length) { 1411 version(Python_2_5_Or_Later) { 1412 Py_ssize_t i; 1413 if(!PyIndex_Check(key)) goto slice; 1414 i = PyNumber_AsSsize_t(key, PyExc_IndexError); 1415 }else{ 1416 C_long i; 1417 if(!PyInt_Check(key)) goto slice; 1418 i = PyLong_AsLong(key); 1419 } 1420 if(i == -1 && PyErr_Occurred()) { 1421 return null; 1422 } 1423 alias OpIndexs[0] OpIndex0; 1424 return opindex_wrap!(T, OpIndex0.Inner!T.FN).func(self, key); 1425 } 1426 slice: 1427 static if(OpSlices.length) { 1428 if(PySlice_Check(key)) { 1429 Py_ssize_t len = PyObject_Length(self); 1430 Py_ssize_t start, stop, step, slicelength; 1431 if(PySlice_GetIndicesEx(key, len, 1432 &start, &stop, &step, &slicelength) < 0) { 1433 return null; 1434 } 1435 if(step != 1) { 1436 PyErr_SetString(PyExc_TypeError, 1437 "slice steps not supported in D"); 1438 return null; 1439 } 1440 alias OpSlices[0] OpSlice0; 1441 return opslice_wrap!(T, OpSlice0.Inner!T.FN).func( 1442 self, start, stop); 1443 } 1444 } 1445 PyErr_SetString(PyExc_TypeError, format( 1446 "index type '%s' not supported\0", to!string(key.ob_type.tp_name)).ptr); 1447 return null; 1448 } 1449 1450 static extern(C) int ass_func(T)(PyObject* self, PyObject* key, 1451 PyObject* val) { 1452 import std..string: format; 1453 1454 static if(OpIndexAssigns.length) { 1455 version(Python_2_5_Or_Later) { 1456 Py_ssize_t i; 1457 if(!PyIndex_Check(key)) goto slice; 1458 i = PyNumber_AsSsize_t(key, PyExc_IndexError); 1459 }else{ 1460 C_long i; 1461 if(!PyInt_Check(key)) goto slice; 1462 i = PyLong_AsLong(key); 1463 } 1464 if(i == -1 && PyErr_Occurred()) { 1465 return -1; 1466 } 1467 alias OpIndexAssigns[0] OpIndexAssign0; 1468 return opindexassign_wrap!(T, OpIndexAssign0.Inner!T.FN).func( 1469 self, key, val); 1470 } 1471 slice: 1472 static if(OpSliceAssigns.length) { 1473 if(PySlice_Check(key)) { 1474 Py_ssize_t len = PyObject_Length(self); 1475 Py_ssize_t start, stop, step, slicelength; 1476 if(PySlice_GetIndicesEx(key, len, 1477 &start, &stop, &step, &slicelength) < 0) { 1478 return -1; 1479 } 1480 if(step != 1) { 1481 PyErr_SetString(PyExc_TypeError, 1482 "slice steps not supported in D"); 1483 return -1; 1484 } 1485 alias OpSliceAssigns[0] OpSliceAssign0; 1486 return opsliceassign_wrap!(T, OpSliceAssign0.Inner!T.FN).func( 1487 self, start, stop, val); 1488 } 1489 } 1490 PyErr_SetString(PyExc_TypeError, format( 1491 "assign index type '%s' not supported\0", to!string(key.ob_type.tp_name)).ptr); 1492 return -1; 1493 } 1494 } 1495 1496 /* 1497 Params: each param is a Type which supports the interface 1498 1499 Param.needs_shim == false => Param.call!(pyclassname, T) 1500 or 1501 Param.needs_shim == true => Param.call!(pyclassname,T, Shim) 1502 1503 performs appropriate mutations to the PyTypeObject 1504 1505 Param.shim!(i,T) for i : Params[i] == Param 1506 1507 generates a string to be mixed in to Shim type 1508 1509 where T is the type being wrapped, Shim is the wrapped type 1510 1511 */ 1512 1513 /** 1514 Wrap a class. 1515 1516 Params: 1517 T = The class being wrapped. 1518 Params = Mixture of definitions of members of T to be wrapped and 1519 optional arguments. 1520 Concerning optional arguments, accepts PyName!(pyname), ModuleName!(modulename), and Docstring!(docstring). 1521 pyname = The name of the class as it will appear in Python. Defaults to 1522 T's name in D 1523 modulename = The name of the python module in which the wrapped class 1524 resides. Defaults to "". 1525 docstring = The class's docstring. Defaults to "". 1526 */ 1527 void wrap_class(T, Params...)() { 1528 alias Args!("","", __traits(identifier,T), "",Params) args; 1529 _wrap_class!(T, args.pyname, args.docstring, args.modulename, args.rem).wrap_class(); 1530 } 1531 template _wrap_class(_T, string name, string docstring, string modulename, Params...) { 1532 import std.conv; 1533 import util.typelist; 1534 static if (is(_T == class)) { 1535 //pragma(msg, "wrap_class: " ~ name); 1536 alias pyd.make_wrapper.make_wrapper!(_T, Params).wrapper shim_class; 1537 //alias W.wrapper shim_class; 1538 alias _T T; 1539 } else { 1540 //pragma(msg, "wrap_struct: '" ~ name ~ "'"); 1541 alias void shim_class; 1542 alias _T* T; 1543 } 1544 void wrap_class() { 1545 if(!Pyd_Module_p(modulename)) { 1546 if(should_defer_class_wrap(modulename, name)) { 1547 defer_class_wrap(modulename, name, toDelegate(&wrap_class)); 1548 return; 1549 } 1550 } 1551 alias PydTypeObject!(T) type; 1552 init_PyTypeObject!T(type); 1553 1554 foreach (param; Params) { 1555 static if (param.needs_shim) { 1556 param.call!(name, T, shim_class)(); 1557 } else { 1558 param.call!(name,T)(); 1559 } 1560 } 1561 1562 assert(Pyd_Module_p(modulename) !is null, "Must initialize module '" ~ modulename ~ "' before wrapping classes."); 1563 string module_name = to!string(PyModule_GetName(Pyd_Module_p(modulename))); 1564 1565 ////////////////// 1566 // Basic values // 1567 ////////////////// 1568 Py_SET_TYPE(&type, &PyType_Type); 1569 type.tp_basicsize = PyObject.sizeof; 1570 type.tp_doc = (docstring ~ "\0").ptr; 1571 version(Python_3_0_Or_Later) { 1572 type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; 1573 }else{ 1574 type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES; 1575 } 1576 //type.tp_repr = &wrapped_repr!(T).repr; 1577 type.tp_methods = wrapped_method_list!(T).ptr; 1578 type.tp_name = (module_name ~ "." ~ name ~ "\0").ptr; 1579 1580 ///////////////// 1581 // Inheritance // 1582 ///////////////// 1583 // Inherit classes from their wrapped superclass. 1584 static if (is(T B == super)) { 1585 foreach (C; B) { 1586 static if (is(C == class) && !is(C == Object)) { 1587 if (is_wrapped!(C)) { 1588 type.tp_base = &PydTypeObject!(C); 1589 } 1590 } 1591 } 1592 } 1593 1594 //////////////////////// 1595 // Operator overloads // 1596 //////////////////////// 1597 1598 Operators!(Filter!(IsOp, Params)).call!T(); 1599 // its just that simple. 1600 1601 IndexSliceMerge!(Params).call!T(); 1602 // indexing and slicing aren't exactly simple. 1603 1604 ////////////////////////// 1605 // Constructor wrapping // 1606 ////////////////////////// 1607 Constructors!(name, Filter!(IsInit, Params)).call!(T, shim_class)(); 1608 1609 ////////////////////////// 1610 // Iterator wrapping // 1611 ////////////////////////// 1612 Iterator!(Params).call!(T)(); 1613 1614 1615 ////////////////// 1616 // Finalization // 1617 ////////////////// 1618 if (PyType_Ready(&type) < 0) { 1619 throw new Exception("Couldn't ready wrapped type!"); 1620 } 1621 Py_INCREF(cast(PyObject*)&type); 1622 PyModule_AddObject(Pyd_Module_p(modulename), (name~"\0").ptr, cast(PyObject*)&type); 1623 1624 is_wrapped!(T) = true; 1625 static if (is(T == class)) { 1626 is_wrapped!(shim_class) = true; 1627 wrapped_classes[T.classinfo] = &type; 1628 wrapped_classes[shim_class.classinfo] = &type; 1629 } 1630 } 1631 } 1632