root/trunk/infrastructure/pyd/class_wrap.d

Revision 122, 29.8 kB (checked in by KirkMcDonald, 9 months 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]) {