root/trunk/infrastructure/pyd/class_wrap.d

Revision 122, 29.8 kB (checked in by KirkMcDonald, 4 years ago)

* Un-broke Init. (Oops.)
* Reverted some symbol-length-shortening code, which caused the above problem.

Line 
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 module pyd.class_wrap;
23
24 import python;
25
26 import pyd.ctor_wrap;
27 import pyd.def;
28 import pyd.dg_convert;
29 import pyd.exception;
30 import pyd.func_wrap;
31 version (Pyd_with_StackThreads) {
32     import pyd.iteration;
33 }
34 import pyd.make_object;
35 import pyd.make_wrapper;
36 import pyd.op_wrap;
37 import pyd.make_wrapper;
38 import pyd.lib_abstract :
39     symbolnameof,
40     prettytypeof,
41     toString,
42     ParameterTypeTuple,
43     ReturnType,
44     minArgs,
45     objToStr,
46     ToString
47 ;
48
49 //import meta.Default;
50
51 PyTypeObject*[ClassInfo] wrapped_classes;
52 template shim_class(T) {
53     PyTypeObject* shim_class;
54 }
55
56 // This is split out in case I ever want to make a subtype of a wrapped class.
57 template PydWrapObject_HEAD(T) {
58     mixin PyObject_HEAD;
59     T d_obj;
60 }
61
62 /// The class object, a subtype of PyObject
63 template wrapped_class_object(T) {
64     extern(C)
65     struct wrapped_class_object {
66         mixin PydWrapObject_HEAD!(T);
67     }
68 }
69
70 ///
71 template wrapped_class_type(T) {
72 /// The type object, an instance of PyType_Type
73     static PyTypeObject wrapped_class_type = {
74         1,                            /*ob_refcnt*/
75         null,                         /*ob_type*/
76         0,                            /*ob_size*/
77         null,                         /*tp_name*/
78         0,                            /*tp_basicsize*/
79         0,                            /*tp_itemsize*/
80         &wrapped_methods!(T).wrapped_dealloc, /*tp_dealloc*/
81         null,                         /*tp_print*/
82         null,                         /*tp_getattr*/
83         null,                         /*tp_setattr*/
84         null,                         /*tp_compare*/
85         null,                         /*tp_repr*/
86         null,                         /*tp_as_number*/
87         null,                         /*tp_as_sequence*/
88         null,                         /*tp_as_mapping*/
89         null,                         /*tp_hash */
90         null,                         /*tp_call*/
91         null,                         /*tp_str*/
92         null,                         /*tp_getattro*/
93         null,                         /*tp_setattro*/
94         null,                         /*tp_as_buffer*/
95         0,                            /*tp_flags*/
96         null,                         /*tp_doc*/
97         null,                         /*tp_traverse*/
98         null,                         /*tp_clear*/
99         null,                         /*tp_richcompare*/
100         0,                            /*tp_weaklistoffset*/
101         null,                         /*tp_iter*/
102         null,                         /*tp_iternext*/
103         null,                         /*tp_methods*/
104         null,                         /*tp_members*/
105         null,                         /*tp_getset*/
106         null,                         /*tp_base*/
107         null,                         /*tp_dict*/
108         null,                         /*tp_descr_get*/
109         null,                         /*tp_descr_set*/
110         0,                            /*tp_dictoffset*/
111         null,                         /*tp_init*/
112         null,                         /*tp_alloc*/
113         &wrapped_methods!(T).wrapped_new, /*tp_new*/
114         null,                         /*tp_free*/
115         null,                         /*tp_is_gc*/
116         null,                         /*tp_bases*/
117         null,                         /*tp_mro*/
118         null,                         /*tp_cache*/
119         null,                         /*tp_subclasses*/
120         null,                         /*tp_weaklist*/
121         null,                         /*tp_del*/
122     };
123 }
124
125 // A mappnig of all class references that are being held by Python.
126 PyObject*[void*] wrapped_gc_objects;
127 // A mapping of all GC references that are being held by Python.
128 template wrapped_gc_references(dg_t) {
129     PyObject*[dg_t] wrapped_gc_references;
130 }
131
132 /**
133  * A useful check for whether a given class has been wrapped. Mainly used by
134  * the conversion functions (see make_object.d), but possibly useful elsewhere.
135  */
136 template is_wrapped(T) {
137     bool is_wrapped = false;
138 }
139
140 // The list of wrapped methods for this class.
141 template wrapped_method_list(T) {
142     PyMethodDef[] wrapped_method_list = [
143         { null, null, 0, null }
144     ];
145 }
146
147 // The list of wrapped properties for this class.
148 template wrapped_prop_list(T) {
149     static PyGetSetDef[] wrapped_prop_list = [
150         { null, null, null, null, null }
151     ];
152 }
153
154 //////////////////////
155 // STANDARD METHODS //
156 //////////////////////
157
158 //import std.stdio;
159
160 /// Various wrapped methods
161 template wrapped_methods(T) {
162     alias wrapped_class_object!(T) wrap_object;
163     /// The generic "__new__" method
164     extern(C)
165     PyObject* wrapped_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
166         return exception_catcher(delegate PyObject*() {
167             wrap_object* self;
168
169             self = cast(wrap_object*)type.tp_alloc(type, 0);
170             if (self !is null) {
171                 self.d_obj = null;
172             }
173
174             return cast(PyObject*)self;
175         });
176     }
177
178     /// The generic dealloc method.
179     extern(C)
180     void wrapped_dealloc(PyObject* self) {
181         exception_catcher(delegate void() {
182             //writefln("wrapped_dealloc: T is %s", typeid(T));
183             WrapPyObject_SetObj!(T)(self, cast(T)null);
184             self.ob_type.tp_free(self);
185         });
186     }
187 }
188
189 template wrapped_repr(T, alias fn) {
190     alias wrapped_class_object!(T) wrap_object;
191     /// The default repr method calls the class's toString.
192     extern(C)
193     PyObject* repr(PyObject* self) {
194         return exception_catcher(delegate PyObject*() {
195             return method_wrap!(T, fn, char[] function()).func(self, null);
196         });
197     }
198 }
199
200 // This template gets an alias to a property and derives the types of the
201 // getter form and the setter form. It requires that the getter form return the
202 // same type that the setter form accepts.
203 template property_parts(alias p) {
204     // This may be either the getter or the setter
205     alias typeof(&p) p_t;
206     alias ParameterTypeTuple!(p_t) Info;
207     // This means it's the getter
208     static if (Info.length == 0) {
209         alias p_t getter_type;
210         // The setter may return void, or it may return the newly set attribute.
211         alias typeof(p(ReturnType!(p_t).init)) function(ReturnType!(p_t)) setter_type;
212     // This means it's the setter
213     } else {
214         alias p_t setter_type;
215         alias Info[0] function() getter_type;
216     }
217 }
218
219 ///
220 template wrapped_get(T, alias Fn) {
221     /// A generic wrapper around a "getter" property.
222     extern(C)
223     PyObject* func(PyObject* self, void* closure) {
224         // method_wrap already catches exceptions
225         return method_wrap!(T, Fn, property_parts!(Fn).getter_type).func(self, null);
226     }
227 }
228
229 ///
230 template wrapped_set(T, alias Fn) {
231     /// A generic wrapper around a "setter" property.
232     extern(C)
233     int func(PyObject* self, PyObject* value, void* closure) {
234         PyObject* temp_tuple = PyTuple_New(1);
235         if (temp_tuple is null) return -1;
236         scope(exit) Py_DECREF(temp_tuple);
237         Py_INCREF(value);
238         PyTuple_SetItem(temp_tuple, 0, value);
239         PyObject* res = method_wrap!(T, Fn, property_parts!(Fn).setter_type).func(self, temp_tuple);
240         // If we get something back, we need to DECREF it.
241         if (res) Py_DECREF(res);
242         // If we don't, propagate the exception
243         else return -1;
244         // Otherwise, all is well.
245         return 0;
246     }
247 }
248
249 //////////////////////////////
250 // CLASS WRAPPING INTERFACE //
251 //////////////////////////////
252
253 /+
254 /**
255  * This struct wraps a D class. Its member functions are the primary way of
256  * wrapping the specific parts of the class.
257  */
258 struct wrapped_class(T, char[] classname = symbolnameof!(T)) {
259     static if (is(T == class)) pragma(msg, "wrapped_class: " ~ classname);
260     static const char[] _name = classname;
261     static bool _private = false;
262     alias T wrapped_type;
263 +/
264
265 //enum ParamType { Def, StaticDef, Property, Init, Parent, Hide, Iter, AltIter }
266 struct DoNothing {
267     static void call(T) () {}
268 }
269 /**
270 Wraps a member function of the class.
271
272 Params:
273 fn = The member function to wrap.
274 name = The name of the function as it will appear in Python.
275 fn_t = The type of the function. It is only useful to specify this
276        if more than one function has the same name as this one.
277 */
278 struct Def(alias fn) {
279     mixin _Def!(fn, symbolnameof!(fn), typeof(&fn), "");
280 }
281 struct Def(alias fn, string docstring) {
282     mixin _Def!(fn, /*symbolnameof!(fn),*/ symbolnameof!(fn), typeof(&fn)/+, minArgs!(fn)+/, docstring);
283 }
284 struct Def(alias fn, string name, string docstring) {
285     mixin _Def!(fn, /*symbolnameof!(fn),*/ name, typeof(&fn)/+, minArgs!(fn)+/, docstring);
286 }
287 struct Def(alias fn, string name, fn_t) {
288     mixin _Def!(fn, /*symbolnameof!(fn),*/ name, fn_t/+, minArgs!(fn)+/, "");
289 }
290 struct Def(alias fn, fn_t) {
291     mixin _Def!(fn, /*symbolnameof!(fn),*/ symbolnameof!(fn), fn_t/+, minArgs!(fn)+/, "");
292 }
293 struct Def(alias fn, fn_t, string docstring) {
294     mixin _Def!(fn, /*symbolnameof!(fn),*/ symbolnameof!(fn), fn_t/+, minArgs!(fn)+/, docstring);
295 }
296 struct Def(alias fn, string name, fn_t, string docstring) {
297     mixin _Def!(fn, /*symbolnameof!(fn),*/ name, fn_t/+, minArgs!(fn)+/, docstring);
298 }
299 /+
300 template Def(alias fn, string name, fn_t, uint MIN_ARGS=minArgs!(fn)/+, string docstring=""+/) {
301     alias Def!(fn, /*symbolnameof!(fn),*/ name, fn_t, MIN_ARGS/+, docstring+/) Def;
302 }
303 +/
304 template _Def(alias fn, /*string _realname,*/ string name, fn_t/+, uint MIN_ARGS=minArgs!(fn)+/, string docstring) {
305     //static const type = ParamType.Def;
306     alias fn func;
307     alias fn_t func_t;
308     static const char[] realname = symbolnameof!(fn);//_realname;
309     static const char[] funcname = name;
310     static const uint min_args = minArgs!(fn);
311     static const bool needs_shim = false;
312
313     static void call(T) () {
314         pragma(msg, "class.def: " ~ name);
315         static PyMethodDef empty = { null, null, 0, null };
316         alias wrapped_method_list!(T) list;
317         list[length-1].ml_name = (name ~ \0).ptr;
318         list[length-1].ml_meth = &method_wrap!(T, fn, fn_t).func;
319         list[length-1].ml_flags = METH_VARARGS;
320         list[length-1].ml_doc = (docstring~\0).ptr;
321         list ~= empty;
322         // It's possible that appending the empty item invalidated the
323         // pointer in the type struct, so we renew it here.
324         wrapped_class_type!(T).tp_methods = list.ptr;
325     }
326     template shim(uint i) {
327         const char[] shim =
328             "    alias Params["~ToString!(i)~"] __pyd_p"~ToString!(i)~";\n"
329             "    ReturnType!(__pyd_p"~ToString!(i)~".func_t) "~realname~"(ParameterTypeTuple!(__pyd_p"~ToString!(i)~".func_t) t) {\n"
330             "        return __pyd_get_overload!(\""~realname~"\", __pyd_p"~ToString!(i)~".func_t).func(\""~name~"\", t);\n"
331             "    }\n";
332     }
333 }
334
335 /**
336 Wraps a static member function of the class. Identical to pyd.def.def
337 */
338 struct StaticDef(alias fn) {
339     mixin _StaticDef!(fn,/+ symbolnameof!(fn),+/ symbolnameof!(fn), typeof(&fn), minArgs!(fn), "");
340 }
341 struct StaticDef(alias fn, string docstring) {
342     mixin _StaticDef!(fn,/+ symbolnameof!(fn),+/ symbolnameof!(fn), typeof(&fn), minArgs!(fn), docstring);
343 }
344 struct StaticDef(alias _fn, string name, string docstring) {
345     mixin _StaticDef!(fn,/+ symbolnameof!(fn),+/ name, typeof(&fn), minArgs!(fn), docstring);
346 }
347 struct StaticDef(alias _fn, string name, fn_t, string docstring) {
348     mixin _StaticDef!(fn,/+ symbolnameof!(fn),+/ name, fn_t, minArgs!(fn), docstring);
349 }
350 struct StaticDef(alias _fn, fn_t) {
351     mixin _StaticDef!(fn,/+ symbolnameof!(fn),+/ symbolnameof!(fn), fn_t, minArgs!(fn), "");
352 }
353 struct StaticDef(alias _fn, fn_t, string docstring) {
354     mixin _StaticDef!(fn,/+ symbolnameof!(fn),+/ symbolnameof!(fn), fn_t, minArgs!(fn), docstring);
355 }
356 struct StaticDef(alias _fn, string name, fn_t) {
357     mixin _StaticDef!(fn,/+ symbolnameof!(fn),+/ name, fn_t, minArgs!(fn), "");
358 }
359 struct StaticDef(alias _fn, string name, fn_t, uint MIN_ARGS) {
360     mixin _StaticDef!(fn,/+ symbolnameof!(fn),+/ name, fn_t, MIN_ARGS, "");
361 }
362 struct StaticDef(alias _fn, string name, fn_t, uint MIN_ARGS, string docstring) {
363     mixin _StaticDef!(fn,/+ symbolnameof!(fn),+/ name, fn_t, MIN_ARGS, docstring);
364 }
365 template _StaticDef(alias fn,/+ string _realname,+/ string name, fn_t, uint MIN_ARGS, string docstring) {
366     //static const type = ParamType.StaticDef;
367     alias fn func;
368     alias fn_t func_t;
369     static const char[] funcname = name;
370     static const uint min_args = MIN_ARGS;
371     static const bool needs_shim = false;
372     static void call(T) () {
373         pragma(msg, "class.static_def: " ~ name);
374         static PyMethodDef empty = { null, null, 0, null };
375         alias wrapped_method_list!(T) list;
376         list[length-1].ml_name = (name ~ \0).ptr;
377         list[length-1].ml_meth = &function_wrap!(fn, MIN_ARGS, fn_t).func;
378         list[length-1].ml_flags = METH_VARARGS | METH_STATIC;
379         list[length-1].ml_doc = (docstring~\0).ptr;
380         list ~= empty;
381         wrapped_class_type!(T).tp_methods = list;
382     }
383     template shim(uint i) {
384         const char[] shim = "";
385     }
386 }
387
388 /**
389 Wraps a property of the class.
390
391 Params:
392 fn = The property to wrap.
393 name = The name of the property as it will appear in Python.
394 RO = Whether this is a read-only property.
395 */
396 //template Property(alias fn, char[] name = symbolnameof!(fn), bool RO=false, char[] docstring = "") {
397 //    alias Property!(fn, symbolnameof!(fn), name, RO, docstring) Property;
398 //}
399 struct Property(alias fn) {
400     mixin _Property!(fn, symbolnameof!(fn), symbolnameof!(fn), false, "");
401 }
402 struct Property(alias fn, string docstring) {
403     mixin _Property!(fn, symbolnameof!(fn), symbolnameof!(fn), false, docstring);
404 }
405 struct Property(alias fn, string name, string docstring) {
406     mixin _Property!(fn, symbolnameof!(fn), name, false, docstring);
407 }
408 struct Property(alias fn, string name, bool RO) {
409     mixin _Property!(fn, symbolnameof!(fn), name, RO, "");
410 }
411 struct Property(alias fn, string name, bool RO, string docstring) {
412     mixin _Property!(fn, symbolnameof!(fn), name, RO, docstring);
413 }
414 struct Property(alias fn, bool RO) {
415     mixin _Property!(fn, symbolnameof!(fn), symbolnameof!(fn), RO, "");
416 }
417 struct Property(alias fn, bool RO, string docstring) {
418     mixin _Property!(fn, symbolnameof!(fn), symbolnameof!(fn), RO, docstring);
419 }
420 template _Property(alias fn, string _realname, string name, bool RO, string docstring) {
421     alias property_parts!(fn).getter_type get_t;
422     alias property_parts!(fn).setter_type set_t;
423     static const char[] realname = _realname;
424     static const char[] funcname = name;
425     static const bool readonly = RO;
426     static const bool needs_shim = false;
427     static void call(T) () {
428         pragma(msg, "class.prop: " ~ name);
429         static PyGetSetDef empty = { null, null, null, null, null };
430         wrapped_prop_list!(T)[length-1].name = (name ~ \0).ptr;
431         wrapped_prop_list!(T)[length-1].get =
432             &wrapped_get!(T, fn).func;
433         static if (!RO) {
434             wrapped_prop_list!(T)[length-1].set =
435                 &wrapped_set!(T, fn).func;
436         }
437         wrapped_prop_list!(T)[length-1].doc = (docstring~\0).ptr;
438         wrapped_prop_list!(T)[length-1].closure = null;
439         wrapped_prop_list!(T) ~= empty;
440         // It's possible that appending the empty item invalidated the
441         // pointer in the type struct, so we renew it here.
442         wrapped_class_type!(T).tp_getset =
443             wrapped_prop_list!(T).ptr;
444     }
445     template shim_setter(uint i) {
446         static if (RO) {
447             const char[] shim_setter = "";
448         } else {
449             const char[] shim_setter =
450             "    ReturnType!(__pyd_p"~ToString!(i)~".set_t) "~_realname~"(ParameterTypeTuple!(__pyd_p"~ToString!(i)~".set_t) t) {\n"
451             "        return __pyd_get_overload!(\""~_realname~"\", __pyd_p"~ToString!(i)~".set_t).func(\""~name~"\", t);\n"
452             "    }\n";
453         }
454     }
455     template shim(uint i) {
456         const char[] shim =
457             "    alias Params["~ToString!(i)~"] __pyd_p"~ToString!(i)~";\n"
458             "    ReturnType!(__pyd_p"~ToString!(i)~".get_t) "~_realname~"() {\n"
459             "        return __pyd_get_overload!(\""~_realname~"\", __pyd_p"~ToString!(i)~".get_t).func(\""~name~"\");\n"
460             "    }\n" ~
461             shim_setter!(i);
462     }
463 }
464
465 /**
466 Wraps a method as the class's __repr__ in Python.
467 */
468 struct Repr(alias fn) {
469     static const bool needs_shim = false;
470     static void call(T)() {
471         alias wrapped_class_type!(T) type;
472         type.tp_repr = &wrapped_repr!(T, fn).repr;
473     }
474     template shim(uint i) {
475         const char[] shim = "";
476     }
477 }
478
479 /**
480 Wraps the constructors of the class.
481
482 This template takes a series of specializations of the ctor template
483 (see ctor_wrap.d), each of which describes a different constructor
484 that the class supports. The default constructor need not be
485 specified, and will always be available if the class supports it.
486
487 Bugs:
488 This currently does not support having multiple constructors with
489 the same number of arguments.
490 */
491 struct Init(C ...) {
492     alias C ctors;
493     static const bool needs_shim = true;
494     template call(T, shim) {
495         //mixin wrapped_ctors!(param.ctors) Ctors;
496         static void call() {
497             wrapped_class_type!(T).tp_init =
498                 //&Ctors.init_func;
499                 &wrapped_ctors!(shim, C).init_func;
500         }
501     }
502     template shim_impl(uint i, uint c=0) {
503         static if (c < ctors.length) {
504             const char[] shim_impl =
505                 "    this(ParameterTypeTuple!(__pyd_c"~ToString!(i)~"["~ToString!(c)~"]) t) {\n"
506                 "        super(t);\n"
507                 "    }\n" ~ shim_impl!(i, c+1);
508         } else {
509             const char[] shim_impl =
510                 "    static if (is(typeof(new T))) {\n"
511                 "        this() { super(); }\n"
512                 "    }\n";
513         }
514     }
515     template shim(uint i) {
516         const char[] shim =
517             "    alias Params["~ToString!(i)~"] __pyd_p"~ToString!(i)~";\n"
518             "    alias __pyd_p"~ToString!(i)~".ctors __pyd_c"~ToString!(i)~";\n"~
519             shim_impl!(i);
520     }
521 }
522
523 // Iteration wrapping support requires StackThreads
524 version(Pyd_with_StackThreads) {
525
526 /**
527 Allows selection of alternate opApply overloads. iter_t should be
528 the type of the delegate in the opApply function that the user wants
529 to be the default.
530 */
531 struct Iter(iter_t) {
532     static const bool needs_shim = false;
533     alias iter_t iterator_t;
534     static void call(T) () {
535         PydStackContext_Ready();
536         // This strange bit of hackery is needed since we operate on pointer-
537         // to-struct types, rather than just struct types.
538         static if (is(T S : S*) && is(S == struct)) {
539             wrapped_class_type!(T).tp_iter = &wrapped_iter!(T, S.opApply, int function(iter_t)).iter;
540         } else {
541             wrapped_class_type!(T).tp_iter = &wrapped_iter!(T, T.opApply, int function(iter_t)).iter;
542         }
543     }
544 }
545
546 /**
547 Exposes alternate iteration methods, originally intended for use with
548 D's delegate-as-iterator features, as methods returning a Python
549 iterator.
550 */
551 struct AltIter(alias fn, string name = symbolnameof!(fn), iter_t = ParameterTypeTuple!(fn)[0]) {
552     static const bool needs_shim = false;
553     static void call(T) () {
554         static PyMethodDef empty = { null, null, 0, null };
555         alias wrapped_method_list!(T) list;
556         PydStackContext_Ready();
557         list[length-1].ml_name = name ~ \0;
558         list[length-1].ml_meth = cast(PyCFunction)&wrapped_iter!(T, fn, int function(iter_t)).iter;
559         list[length-1].ml_flags = METH_VARARGS;
560         list[length-1].ml_doc = "";//(docstring ~ \0).ptr;
561         list ~= empty;
562         // It's possible that appending the empty item invalidated the
563         // pointer in the type struct, so we renew it here.
564         wrapped_class_type!(T).tp_methods = list;
565     }
566 }
567
568 } /*Pyd_with_StackThreads*/
569
570 void wrap_class(T, Params...) (string docstring="", string modulename="") {
571     _wrap_class!(T, symbolnameof!(T), Params).wrap_class(docstring, modulename);
572 }
573 /+
574 template _wrap_class(T, Params...) {
575     mixin _wrap_class!(T, symbolnameof!(T), Params);
576 }
577 +/
578 //import std.stdio;
579 template _wrap_class(_T, string name, Params...) {
580     static if (is(_T == class)) {
581         pragma(msg, "wrap_class: " ~ name);
582         alias pyd.make_wrapper.make_wrapper!(_T, Params).wrapper shim_class;
583         //alias W.wrapper shim_class;
584         alias _T T;
585 //    } else static if (is(_T == interface)) {
586 //        pragma(msg, "wrap_interface: " ~ name);
587 //        alias make_wrapper!(_T, Params).wrapper shim_class;
588 //        alias _T T;
589     } else {
590         pragma(msg, "wrap_struct: '" ~ name ~ "'");
591         alias void shim_class;
592         alias _T* T;
593     }
594 void wrap_class(string docstring="", string modulename="") {
595     pragma(msg, "shim.mangleof: " ~ shim_class.mangleof);
596     alias wrapped_class_type!(T) type;
597     //writefln("entering wrap_class for %s", typeid(T));
598     //pragma(msg, "wrap_class, T is " ~ prettytypeof!(T));
599
600     //Params params;
601     //writefln("before params: tp_init is %s", type.tp_init);
602     foreach (param; Params) {
603         static if (param.needs_shim) {
604             //mixin param.call!(T) PCall;
605             param.call!(T, shim_class)();
606         } else {
607             param.call!(T)();
608         }
609     }
610     //writefln("after params: tp_init is %s", type.tp_init);
611
612     assert(Pyd_Module_p(modulename) !is null, "Must initialize module before wrapping classes.");
613     string module_name = toString(python.PyModule_GetName(Pyd_Module_p(modulename)));
614
615     //////////////////
616     // Basic values //
617     //////////////////
618     type.ob_type      = python.PyType_Type_p();
619     type.tp_basicsize = (wrapped_class_object!(T)).sizeof;
620     type.tp_doc       = (docstring ~ \0).ptr;
621     type.tp_flags     = python.Py_TPFLAGS_DEFAULT | python.Py_TPFLAGS_BASETYPE;
622     //type.tp_repr      = &wrapped_repr!(T).repr;
623     type.tp_methods   = wrapped_method_list!(T).ptr;
624     type.tp_name      = (module_name ~ "." ~ name ~ \0).ptr;
625
626     /////////////////
627     // Inheritance //
628     /////////////////
629     // Inherit classes from their wrapped superclass.
630     static if (is(T B == super)) {
631         foreach (C; B) {
632             static if (is(C == class) && !is(C == Object)) {
633                 if (is_wrapped!(C)) {
634                     type.tp_base = &wrapped_class_type!(C);
635                 }
636             }
637         }
638     }
639
640     ////////////////////////
641     // Operator overloads //
642     ////////////////////////
643     // Numerical operator overloads
644     if (pyd.op_wrap.wrapped_class_as_number!(T) != python.PyNumberMethods.init) {
645         type.tp_as_number = &pyd.op_wrap.wrapped_class_as_number!(T);
646     }
647     // Sequence operator overloads
648     if (pyd.op_wrap.wrapped_class_as_sequence!(T) != python.PySequenceMethods.init) {
649         type.tp_as_sequence = &pyd.op_wrap.wrapped_class_as_sequence!(T);
650     }
651     // Mapping operator overloads
652     if (pyd.op_wrap.wrapped_class_as_mapping!(T) != python.PyMappingMethods.init) {
653         type.tp_as_mapping = &pyd.op_wrap.wrapped_class_as_mapping!(T);
654     }
655
656     // Standard operator overloads
657     // opApply
658     version(Pyd_with_StackThreads) {
659         static if (is(typeof(&T.opApply))) {
660             if (type.tp_iter is null) {
661                 PydStackContext_Ready();
662                 type.tp_iter = &wrapped_iter!(T, T.opApply).iter;
663             }
664         }
665     }
666     // opCmp
667     static if (is(typeof(&T.opCmp))) {
668         type.tp_compare = &pyd.op_wrap.opcmp_wrap!(T).func;
669     }
670     // opCall
671     static if (is(typeof(&T.opCall))) {
672         type.tp_call = cast(ternaryfunc)&method_wrap!(T, T.opCall, typeof(&T.opCall)).func;
673     }
674
675     //////////////////////////
676     // Constructor wrapping //
677     //////////////////////////
678     // If a ctor wasn't supplied, try the default.
679     // If the default ctor isn't available, and no ctors were supplied,
680     // then this class cannot be instantiated from Python.
681     // (Structs always use the default ctor.)
682     static if (is(typeof(new T))) {
683         if (type.tp_init is null) {
684             static if (is(T == class)) {
685                 type.tp_init = &wrapped_init!(shim_class).init;
686             } else {
687                 type.tp_init = &wrapped_struct_init!(T).init;
688             }
689         }
690     }
691     //writefln("after default check: tp_init is %s", type.tp_init);
692
693     //////////////////
694     // Finalization //
695     //////////////////
696     if (PyType_Ready(&type) < 0) {
697         throw new Exception("Couldn't ready wrapped type!");
698     }
699     //writefln("after Ready: tp_init is %s", type.tp_init);
700     python.Py_INCREF(cast(PyObject*)&type);
701     python.PyModule_AddObject(Pyd_Module_p(modulename), (name~\0).ptr, cast(PyObject*)&type);
702
703     is_wrapped!(T) = true;
704     static if (is(T == class)) {
705         is_wrapped!(shim_class) = true;
706         wrapped_classes[T.classinfo] = &type;
707         wrapped_classes[shim_class.classinfo] = &type;
708     }
709     //writefln("leaving wrap_class for %s", typeid(T));
710 }
711 }
712 ////////////////
713 // DOCSTRINGS //
714 ////////////////
715
716 struct Docstring {
717     char[] name, doc;
718 }
719
720 void docstrings(T=void)(Docstring[] docs...) {
721     static if (is(T == void)) {
722        
723     }
724 }
725
726 ///////////////////////
727 // PYD API FUNCTIONS //
728 ///////////////////////
729
730 // If the passed D reference has an existing Python object, return a borrowed
731 // reference to it. Otherwise, return null.
732 PyObject* get_existing_reference(T) (T t) {
733     static if (is(T == class)) {
734         PyObject** obj_p = cast(void*)t in wrapped_gc_objects;
735         if (obj_p) return *obj_p;
736         else return null;
737     } else {
738         PyObject** obj_p = t in wrapped_gc_references!(T);
739         if (obj_p) return *obj_p;
740         else return null;
741     }
742 }
743
744 // Drop the passed D reference from the pool of held references.
745 void drop_reference(T) (T t) {
746     static if (is(T == class)) {
747         wrapped_gc_objects.remove(cast(void*)t);
748     } else {
749         wrapped_gc_references!(T).remove(t);
750     }
751 }
752
753 // Add the passed D reference to the pool of held references.
754 void add_reference(T) (T t, PyObject* o) {
755     static if (is(T == class)) {
756         wrapped_gc_objects[cast(void*)t] = o;
757     } else {
758         wrapped_gc_references!(T)[t] = o;
759     }
760 }
761
762 PyObject* WrapPyObject_FromObject(T) (T t) {
763     return WrapPyObject_FromTypeAndObject(&wrapped_class_type!(T), t);
764 }
765
766 /**
767  * Returns a new Python object of a wrapped type.
768  */
769 PyObject* WrapPyObject_FromTypeAndObject(T) (PyTypeObject* type, T t) {
770     //alias wrapped_class_object!(T) wrapped_object;
771     //alias wrapped_class_type!(T) type;
772     if (is_wrapped!(T)) {
773         // If this object is already wrapped, get the existing object.
774         PyObject* obj_p = get_existing_reference(t);
775         if (obj_p) {
776             Py_INCREF(obj_p);
777             return obj_p;
778         }
779         // Otherwise, allocate a new object
780         PyObject* obj = type.tp_new(type, null, null);
781         // Set the contained instance
782         WrapPyObject_SetObj(obj, t);
783         return obj;
784     } else {
785         PyErr_SetString(PyExc_RuntimeError, ("Type " ~ objToStr(typeid(T)) ~ " is not wrapped by Pyd.").ptr);
786         return null;
787     }
788 }
789
790 /**
791  * Returns the object contained in a Python wrapped type.
792  */
793 T WrapPyObject_AsObject(T) (PyObject* _self) {
794     alias wrapped_class_object!(T) wrapped_object;
795     alias wrapped_class_type!(T) type;
796     wrapped_object* self = cast(wrapped_object*)_self;
797     if (!is_wrapped!(T)) {
798         throw new Exception("Error extracting D object: Type " ~ objToStr(typeid(T)) ~ " is not wrapped.");
799     }
800     if (self is null) {
801         throw new Exception("Error extracting D object: 'self' was null!");
802     }
803     static if (is(T == class)) {
804         if (cast(Object)(self.d_obj) is null) {
805             throw new Exception("Error extracting D object: Reference was not castable to Object!");
806         }
807         if (cast(T)cast(Object)(self.d_obj) is null) {
808             throw new Exception("Error extracting D object: Object was not castable to type "~objToStr(typeid(T))~".");
809         }
810     }
811     return self.d_obj;
812 }
813
814 /**
815  * Sets the contained object in self to t.
816  */
817 void WrapPyObject_SetObj(T) (PyObject* _self, T t) {
818     alias wrapped_class_object!(T) obj;
819     obj* self = cast(obj*)_self;
820     if (t is self.d_obj) return;
821     // Clean up the old object, if there is one
822     if (self.d_obj !is null) {
823         drop_reference(self.d_obj);
824     }
825     self.d_obj = t;
826     // Handle the new one, if there is one
827     if (t !is null) add_reference(self.d_obj, _self);
828 }
Note: See TracBrowser for help on using the browser.