Changeset 91

Show
Ignore:
Timestamp:
02/01/07 18:56:35 (5 years ago)
Author:
KirkMcDonald
Message:

Improved inheritance support. Fixed ctor-related bug.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/examples/inherit/inherit.d

    r68 r91  
    55 
    66class Base { 
     7    this(int i) { writefln("Base.this(): ", i); } 
    78    void foo() { 
    89        writefln("Base.foo"); 
     
    1415 
    1516class Derived : Base { 
     17    this(int i) { super(i); writefln("Derived.this(): ", i); } 
    1618    void foo() { 
    1719        writefln("Derived.foo"); 
     
    1921} 
    2022 
    21 class WrapDerive : Derived
     23class BaseWrap : Base
    2224    mixin OverloadShim; 
     25    this(int i) { super(i); } 
     26    void foo() { 
     27        get_overload(&super.foo, "foo"); 
     28    } 
     29    void bar() { 
     30        get_overload(&super.bar, "bar"); 
     31    } 
     32} 
     33 
     34class DeriveWrap : Derived { 
     35    mixin OverloadShim; 
     36    this(int i) { super(i); } 
    2337    void foo() { 
    2438        get_overload(&super.foo, "foo"); 
     
    3448 
    3549Base return_poly_base() { 
    36     if (b1 is null) b1 = new Base
     50    if (b1 is null) b1 = new Base(1)
    3751    return b1; 
    3852} 
    3953 
    4054Base return_poly_derived() { 
    41     if (b2 is null) b2 = new Derived
     55    if (b2 is null) b2 = new Derived(2)
    4256    return b2; 
    4357} 
    4458 
    4559Base return_poly_wrap() { 
    46     if (b3 is null) b3 = new WrapDerive
     60    if (b3 is null) b3 = new DeriveWrap(3)
    4761    return b3; 
    4862} 
     
    5771 
    5872    wrapped_class!(Base) b; 
     73    b.hide(); 
    5974    b.def!(Base.foo); 
    6075    b.def!(Base.bar); 
     
    6277 
    6378    wrapped_class!(Derived) d; 
     79    d.hide(); 
    6480    d.def!(Derived.foo); 
    6581    finalize_class(d); 
    6682 
    67     wrapped_class!(WrapDerive) w; 
    68     w.def!(WrapDerive.foo); 
    69     finalize_class(w); 
     83    wrapped_class!(BaseWrap, "Base") bw; 
     84    bw.init!(void function(int)); 
     85    bw.def!(BaseWrap.foo); 
     86    bw.def!(BaseWrap.bar); 
     87    finalize_class(bw); 
     88 
     89    wrapped_class!(DeriveWrap, "Derived") dw; 
     90    dw.init!(void function(int)); 
     91    dw.parent!(BaseWrap); 
     92    dw.def!(DeriveWrap.foo); 
     93    finalize_class(dw); 
    7094} 
     95 
  • trunk/examples/inherit/test.py

    r68 r91  
    1212import inherit 
    1313 
    14 b = inherit.Base(
    15 d = inherit.Derived(
     14b = inherit.Base(1
     15d = inherit.Derived(2
    1616 
    1717b.foo() 
     
    2626inherit.call_poly(d) 
    2727 
    28 w = inherit.WrapDerive() 
    29 inherit.call_poly(w) 
     28#w = inherit.WrapDerive() 
     29#inherit.call_poly(w) 
    3030 
    31 class PyClass(inherit.WrapDerive): 
     31class PyClass(inherit.Derived): 
    3232    def foo(self): 
    3333        print 'PyClass.foo' 
    3434 
    35 p = PyClass(
     35p = PyClass(3
    3636#print "The basic inheritance support breaks down here:" 
    3737inherit.call_poly(p) 
     
    4141b1 = inherit.return_poly_base() 
    4242print "inherit.return_poly_base returned instance of Base" 
    43 assert type(b1) == inherit.Base 
     43b1.foo() 
     44b1.bar() 
     45#assert type(b1) == inherit.Base 
    4446b2 = inherit.return_poly_derived() 
    4547b2a = inherit.return_poly_derived() 
    4648print "inherit.return_poly_derived returned instance of Derived" 
    47 assert type(b2) == inherit.Derived 
     49#assert type(b2) == inherit.Derived 
    4850print "inherit.return_poly_derived returned the same object twice" 
    4951assert b2 is b2a 
    5052b3 = inherit.return_poly_wrap() 
    51 print "inherit.return_poly_wrap returned instance of WrapDerive
    52 assert type(b3) == inherit.WrapDerive 
     53print "inherit.return_poly_wrap returned instance of DeriveWrap
     54assert type(b3) == inherit.Derived 
    5355 
    5456print 
  • trunk/html_doc/class_wrapping.html

    r86 r91  
    5858<dt><code>static void init(<span class="t_arg">C</span> ...) ();</code></dt> 
    5959<dd>This allows you to expose the class's constructors to Python. If the class provides a zero-argument constructor, there is no need to specify it; it is always available. Each element of <span class="t_arg">C</span> should be a function type. Each function type should correspond to a constructor. (That is, the arguments to the function should be the same as arguments to the class constructor. The return type is ignored.) There is an additional limitation at this time: No two constructors may have the same number of arguments. Pyd will always attempt to call the first constructor with the right number of arguments. If you wish to support a constructor with default arguments, you must specify each possible constructor call as a different template argument to this function. The examples show a few uses of the <code>init</code> function.</dd> 
     60 
     61<dt><code>static void parent(<span class="t_arg">Parent</span>) ();</code></dt> 
     62<dd>This allows the user to manually specify a class as this class's parent. This is intended for a very specific purpose (related to how Pyd handles <a href="inherit.html">inheritance</a>), and should not be used heedlessly. If a class's parent was previously wrapped, then Pyd will detect this and set up a parent-child relationship automatically, in which case it is not neccessary to specify this.</dd> 
     63 
     64<dt><code>static void hide();</code></dt> 
     65<dd>Causes this class to be wrapped, but not actually directly exposed to Python. This can be useful if you want to return instances of a class without allowing Python code to instantiate them. This is mainly used when handling <a href="inherit.html">inheritance</a>.</dd> 
    6066 
    6167<dt><code>static void iter(<span class="t_arg">iter_t</span>) ();</code></dt> 
  • trunk/html_doc/inherit.html

    r86 r91  
    122122PyClass.foo</pre> 
    123123 
     124<p>However, BaseWrap has no particular relationship to Derived. You may remember that Derived overloads <tt>bar</tt> but not <tt>foo</tt>. When we wrapped Derived in <tt>PydMain</tt>, we specified the <tt>foo</tt> overload but not the <tt>bar</tt> overload. Because Derived's parent class is no longer wrapped, Pyd no longer has any way to know about the <tt>bar</tt> method of the Derived class.</tt></p> 
     125 
     126<p>The solution is to explicitly tell Pyd that Derived's parent is BaseWrap. Furthermore, it is probably best to go the extra mile, by wrapping an <tt>OverloadShim</tt> subclass of Derived (call it DerivedWrap), and telling Pyd that BaseWrap is <em>its</em> parent. Additionally, the original <tt>Base</tt> and <tt>Derived</tt> classes should still be wrapped, in the event that functions return instances of them to Python, but should not actually be exposed to Python. The complete solution ends up looking like this:</p> 
     127 
     128<pre class="code"><span class="keyword">import</span> pyd.pyd; 
     129<span class="keyword">import</span> std.stdio; 
     130 
     131<span class="keyword">class</span> Base { 
     132    <span class="keyword">void</span> foo() { writefln(<span class="string">"Base.foo"</span>); } 
     133    <span class="keyword">void</span> bar() { writefln(<span class="string">"Base.bar"</span>); } 
     134} 
     135 
     136<span class="keyword">class</span> Derived : Base { 
     137    <span class="keyword">void</span> foo() { writefln(<span class="string">"Derived.foo"</span>); } 
     138} 
     139 
     140<span class="keyword">class</span> BaseWrap : Base { 
     141    <span class="keyword">mixin</span> OverloadShim; 
     142    <span class="keyword">void</span> foo() { 
     143        get_overload(&amp;<span class="keyword">super</span>.foo, <span class="string">"foo"</span>); 
     144    } 
     145    <span class="keyword">void</span> bar() { 
     146        get_overload(&amp;<span class="keyword">super</span>.bar, <span class="string">"bar"</span>); 
     147    } 
     148} 
     149 
     150<span class="keyword">class</span> DerivedWrap : Derived { 
     151    <span class="keyword">mixin</span> OverloadShim; 
     152    <span class="keyword">void</span> foo() { 
     153        get_overload(&amp;<span class="keyword">super</span>.foo, <span class="string">"foo"</span>); 
     154    } 
     155} 
     156 
     157<span class="keyword">extern</span> (C) <span class="keyword">void</span> PydMain() { 
     158    module_init(); 
     159 
     160    wrapped_class!(Base) b; 
     161    w.hide(); 
     162    w.def!(Base.foo); 
     163    w.def!(Base.bar); 
     164    finalize_class(w); 
     165 
     166    wrapped_class!(Derived) d; 
     167    d.hide(); 
     168    d.def!(Derived.foo); 
     169    finalize_class(d); 
     170 
     171    wrapped_class!(BaseWrap, <span class="string">"Base"</span>) bw; 
     172    bw.def!(BaseWrap.foo); 
     173    bw.def!(BaseWrap.bar); 
     174    finalize_class(bw); 
     175 
     176    wrapped_class!(DerivedWrap, <span class="string">"Derived"</span>) dw; 
     177    dw.parent!(BaseWrap); 
     178    dw.def!(DerivedWrap.foo); 
     179    finalize_class(dw); 
     180}</pre> 
     181 
     182<p><i>(I recognize that this is astoundingly ugly. However, it is the best solution I can come up with without resorting to code generation.)</i></p> 
     183 
     184<p>The <a href="http://dsource.org/projects/pyd/browser/trunk/examples/inherit/inherit.d"><tt>inherit</tt> example</a> in the Pyd distribution provides a more complete version of this example, including how wrapper code should handle constructors.</p> 
     185 
     186<p><i>(TODO: Add support for interfaces and abstract classes.)</i></p> 
     187 
    124188</div> 
    125189 
  • trunk/infrastructure/pyd/class_wrap.d

    r69 r91  
    8686        null,                         /*tp_setattro*/ 
    8787        null,                         /*tp_as_buffer*/ 
    88         0, /*tp_flags*/ 
     88        0,                            /*tp_flags*/ 
    8989        null,                         /*tp_doc*/ 
    9090        null,                         /*tp_traverse*/ 
     
    250250    static if (is(T == class)) pragma(msg, "wrapped_class: " ~ classname); 
    251251    static const char[] _name = classname; 
     252    static bool _private = false; 
    252253    alias T wrapped_type; 
    253254    /** 
     
    333334    } 
    334335 
     336    static void parent(Parent) () { 
     337        wrapped_class_type!(T).tp_base = &wrapped_class_type!(Parent); 
     338    } 
     339 
     340    static void hide() { 
     341        _private = true; 
     342    } 
     343 
    335344    // Iteration wrapping support requires StackThreads 
    336345    version(Pyd_with_StackThreads) { 
     
    394403    type.tp_name      = (module_name ~ "." ~ name ~ \0).ptr; 
    395404 
    396     // Check for wrapped parent classes 
    397     static if (is(T B == super)) { 
    398         foreach (C; B) { 
    399             static if (is(C == class) && !is(C == Object)) { 
    400                 if (is_wrapped!(C)) { 
    401                     type.tp_base = &wrapped_class_type!(C); 
     405    // Check for wrapped parent classes, if one was not explicitly supplied. 
     406    if (type.tp_base is null) { 
     407        static if (is(T B == super)) { 
     408            foreach (C; B) { 
     409                static if (is(C == class) && !is(C == Object)) { 
     410                    if (is_wrapped!(C)) { 
     411                        type.tp_base = &wrapped_class_type!(C); 
     412                    } 
    402413                } 
    403414            } 
     
    437448    } 
    438449 
    439     // If a ctor wasn't supplied, try the default. 
    440     if (type.tp_init is null) { 
    441         static if (is(T == class)) { 
    442             type.tp_init = &wrapped_init!(T).init; 
    443         } else { 
    444             type.tp_init = &wrapped_struct_init!(T).init; 
     450    if (CLS._private) { 
     451        type.tp_init = null; 
     452    } else { 
     453        // If a ctor wasn't supplied, try the default. 
     454        static if (is(typeof(new T))) { 
     455            if (type.tp_init is null) { 
     456                static if (is(T == class)) { 
     457                    type.tp_init = &wrapped_init!(T).init; 
     458                } else { 
     459                    type.tp_init = &wrapped_struct_init!(T).init; 
     460                } 
     461            } 
    445462        } 
    446463    } 
    447464    if (PyType_Ready(&type) < 0) { 
    448         // XXX: This will probably crash the interpreter, as it isn't normally 
    449         // caught and translated. 
    450465        throw new Exception("Couldn't ready wrapped type!"); 
    451466    } 
    452467    Py_INCREF(cast(PyObject*)&type); 
    453     PyModule_AddObject(Pyd_Module_p(modulename), name.ptr, cast(PyObject*)&type); 
     468    if (!CLS._private) { 
     469        PyModule_AddObject(Pyd_Module_p(modulename), name.ptr, cast(PyObject*)&type); 
     470    } 
    454471    is_wrapped!(T) = true; 
    455472    static if (is(T == class)) { 
     
    460477template OverloadShim() { 
    461478    std.traits.ReturnType!(dg_t) get_overload(dg_t, T ...) (dg_t dg, char[] name, T t) { 
    462         python.PyObject* _pyobj = wrapped_gc_objects[cast(void*)this]; 
    463         if (_pyobj.ob_type != &wrapped_class_type!(typeof(this))) { 
     479        python.PyObject** _pyobj = cast(void*)this in wrapped_gc_objects; 
     480        python.PyTypeObject** _pytype = this.classinfo in wrapped_classes; 
     481        if (_pyobj is null || _pytype is null || (*_pyobj).ob_type != *_pytype) { 
    464482            // If this object's type is not the wrapped class's type (that is, 
    465483            // if this object is actually a Python subclass of the wrapped 
    466484            // class), then call the Python object. 
    467             python.PyObject* method = python.PyObject_GetAttrString(_pyobj, (name ~ \0).ptr); 
     485            python.PyObject* method = python.PyObject_GetAttrString(*_pyobj, (name ~ \0).ptr); 
    468486            if (method is null) handle_exception(); 
    469487            dg_t pydg = PydCallable_AsDelegate!(dg_t)(method); 
  • trunk/raw_html/class_wrapping.html

    r86 r91  
    4242<dt><code>static void init(<span class="t_arg">C</span> ...) ();</code></dt> 
    4343<dd>This allows you to expose the class's constructors to Python. If the class provides a zero-argument constructor, there is no need to specify it; it is always available. Each element of <span class="t_arg">C</span> should be a function type. Each function type should correspond to a constructor. (That is, the arguments to the function should be the same as arguments to the class constructor. The return type is ignored.) There is an additional limitation at this time: No two constructors may have the same number of arguments. Pyd will always attempt to call the first constructor with the right number of arguments. If you wish to support a constructor with default arguments, you must specify each possible constructor call as a different template argument to this function. The examples show a few uses of the <code>init</code> function.</dd> 
     44 
     45<dt><code>static void parent(<span class="t_arg">Parent</span>) ();</code></dt> 
     46<dd>This allows the user to manually specify a class as this class's parent. This is intended for a very specific purpose (related to how Pyd handles <a href="inherit.html">inheritance</a>), and should not be used heedlessly. If a class's parent was previously wrapped, then Pyd will detect this and set up a parent-child relationship automatically, in which case it is not neccessary to specify this.</dd> 
     47 
     48<dt><code>static void hide();</code></dt> 
     49<dd>Causes this class to be wrapped, but not actually directly exposed to Python. This can be useful if you want to return instances of a class without allowing Python code to instantiate them. This is mainly used when handling <a href="inherit.html">inheritance</a>.</dd> 
    4450 
    4551<dt><code>static void iter(<span class="t_arg">iter_t</span>) ();</code></dt> 
  • trunk/raw_html/inherit.html

    r86 r91  
    106106PyClass.foo</pre> 
    107107 
     108<p>However, BaseWrap has no particular relationship to Derived. You may remember that Derived overloads <tt>bar</tt> but not <tt>foo</tt>. When we wrapped Derived in <tt>PydMain</tt>, we specified the <tt>foo</tt> overload but not the <tt>bar</tt> overload. Because Derived's parent class is no longer wrapped, Pyd no longer has any way to know about the <tt>bar</tt> method of the Derived class.</tt></p> 
     109 
     110<p>The solution is to explicitly tell Pyd that Derived's parent is BaseWrap. Furthermore, it is probably best to go the extra mile, by wrapping an <tt>OverloadShim</tt> subclass of Derived (call it DerivedWrap), and telling Pyd that BaseWrap is <em>its</em> parent. Additionally, the original <tt>Base</tt> and <tt>Derived</tt> classes should still be wrapped, in the event that functions return instances of them to Python, but should not actually be exposed to Python. The complete solution ends up looking like this:</p> 
     111 
     112<pre class="code"><span class="keyword">import</span> pyd.pyd; 
     113<span class="keyword">import</span> std.stdio; 
     114 
     115<span class="keyword">class</span> Base { 
     116    <span class="keyword">void</span> foo() { writefln(<span class="string">"Base.foo"</span>); } 
     117    <span class="keyword">void</span> bar() { writefln(<span class="string">"Base.bar"</span>); } 
     118} 
     119 
     120<span class="keyword">class</span> Derived : Base { 
     121    <span class="keyword">void</span> foo() { writefln(<span class="string">"Derived.foo"</span>); } 
     122} 
     123 
     124<span class="keyword">class</span> BaseWrap : Base { 
     125    <span class="keyword">mixin</span> OverloadShim; 
     126    <span class="keyword">void</span> foo() { 
     127        get_overload(&amp;<span class="keyword">super</span>.foo, <span class="string">"foo"</span>); 
     128    } 
     129    <span class="keyword">void</span> bar() { 
     130        get_overload(&amp;<span class="keyword">super</span>.bar, <span class="string">"bar"</span>); 
     131    } 
     132} 
     133 
     134<span class="keyword">class</span> DerivedWrap : Derived { 
     135    <span class="keyword">mixin</span> OverloadShim; 
     136    <span class="keyword">void</span> foo() { 
     137        get_overload(&amp;<span class="keyword">super</span>.foo, <span class="string">"foo"</span>); 
     138    } 
     139} 
     140 
     141<span class="keyword">extern</span> (C) <span class="keyword">void</span> PydMain() { 
     142    module_init(); 
     143 
     144    wrapped_class!(Base) b; 
     145    w.hide(); 
     146    w.def!(Base.foo); 
     147    w.def!(Base.bar); 
     148    finalize_class(w); 
     149 
     150    wrapped_class!(Derived) d; 
     151    d.hide(); 
     152    d.def!(Derived.foo); 
     153    finalize_class(d); 
     154 
     155    wrapped_class!(BaseWrap, <span class="string">"Base"</span>) bw; 
     156    bw.def!(BaseWrap.foo); 
     157    bw.def!(BaseWrap.bar); 
     158    finalize_class(bw); 
     159 
     160    wrapped_class!(DerivedWrap, <span class="string">"Derived"</span>) dw; 
     161    dw.parent!(BaseWrap); 
     162    dw.def!(DerivedWrap.foo); 
     163    finalize_class(dw); 
     164}</pre> 
     165 
     166<p><i>(I recognize that this is astoundingly ugly. However, it is the best solution I can come up with without resorting to code generation.)</i></p> 
     167 
     168<p>The <a href="http://dsource.org/projects/pyd/browser/trunk/examples/inherit/inherit.d"><tt>inherit</tt> example</a> in the Pyd distribution provides a more complete version of this example, including how wrapper code should handle constructors.</p> 
     169 
     170<p><i>(TODO: Add support for interfaces and abstract classes.)</i></p> 
     171 
    108172</div> 
    109173