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