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