Changeset 44

Show
Ignore:
Timestamp:
11/03/06 04:32:46 (2 years ago)
Author:
KirkMcDonald
Message:

Improvements to opIndex and friends. Some function wrapping exception changes.

Files:

Legend:

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

    r41 r44  
    4848    void foo() { 
    4949        writefln("Foo.foo(): i = %s", m_i); 
     50    } 
     51    int length() { return 10; } 
     52    int opSlice(int i1, int i2) { 
     53        writefln(i1, " ", i2); 
     54        return 12; 
     55    } 
     56    int opIndex(int x, int y) { 
     57        writefln(x, " ", y); 
     58        return x+y; 
    5059    } 
    5160    Foo opAdd(Foo f) { return new Foo(m_i + f.m_i); } 
  • trunk/html_doc/class_wrapping.html

    r43 r44  
    8585<p>Missing from this list are <code>opUShr</code> and <code>opUShrAssign</code>. Python does not have an unsigned right-shift operator, so these operator overloads are not supported. (You may still wrap them with a normal method using <code>wrapped_class.def</code>, of course.) Also missing from the list is <code>opApplyReverse</code>. This must be wrapped explicitly with <code>wrapped_class.alt_iter</code>.</p> 
    8686 
    87 <!-- This is not yet implemented. 
    88 <p>Additionally, if a class provides a <code>length</code> property, Pyd will automatically make it available via Python's built-in function <code>len</code>. You may still wrap it with <code>prop</code> or <code>def</code> if you wish it to be available as a normal property or method.</p> 
    89 --> 
     87<p>Additionally, if a class provides a <code>length</code> property, Pyd will automatically make it available via Python's built-in function <code>len</code> and the special <code>__len__</code> method. You may still wrap it with <code>prop</code> or <code>def</code> if you wish it to be available as a normal property or method.</p> 
    9088 
    9189<p><b>Notes on wrapped operators</b></p> 
     
    9391<dl> 
    9492<dt><code>opApply</code></dt> <dd>Pyd wraps D's iteration protocol with the help of Mikola Lysenko's StackThreads package.</dd> 
    95 <dt><code>opIndex, opIndexAssign</code></dt> <dd>Pyd only supports these overloads if they have precisely one index argument. This is a limitation of the Python/C API.</dd> 
    96 <dt><code>opSlice, opSliceAssign</code></dt> <dd>Pyd only supports these overloads if both of their two indexes are implicitly convertable to type <code>int</code>. This is a limitation of the Python/C API.</dd> 
     93<dt><code>opSlice, opSliceAssign</code></dt> <dd>Pyd only supports these overloads if both of their two indexes are implicitly convertable to type <code>int</code>. This is a limitation of the Python/C API. Note that this means the zero-argument form of opSlice (for allowing the "empty slice," e.g. <code>foo[]</code>) cannot be wrapped. <i>(I may work around this in the future.)</i> Because Pyd can only automatically wrap the lexically-first method in a class, it will fail to wrap opSlice and opSliceAssign if you define an empty form first.</dd> 
    9794<dt><code>opCat, opCatAssign</code></dt> <dd>Python does not have a dedicated array concatenation operator. The plus sign (<code>+</code>) is reused for this purpose. Therefore, odd behavior may result with classes that define both <code>opAdd/opAddAssign</code> and one or both of these operators. (Consider yourself warned.) However, the Python/C API considers addition and concatenation distinct operations, and so both of these sets of operator overloads are supported.</dd> 
    9895<dt><code>opIn_r</code></dt> <dd>Python expects the <code>in</code> operator to return a boolean value (it is a containment test). D convention is for <code>in</code> to search for the value in the container, and to return a pointer to the found item, or <code>null</code> if the item is not found. That said, D does not enforce any particular signature on the <code>in</code> overload, while the Python/C API does. Pyd will check the boolean result of a call to <code>opIn_r</code>, and return that value to Python.</dd> 
  • trunk/html_doc/index.html

    r42 r44  
    2323 
    2424<h1>Pyd</h1> 
    25 <p>Pyd is a library for D that wraps the raw <a href="http://docs.python.org/api/api.html">Python/C API</a> with a cleaner, simpler interface. It makes exposing raw D functions and classes to Python almost trivially simple. It bears certain similarities to <a href="http://www.boost.org/libs/python/doc/">Boost.Python</a>, and indeed Boost.Python is a major influence on Pyd.</p> 
     25<p>Pyd is a library for the D programming language that wraps the raw <a href="http://docs.python.org/api/api.html">Python/C API</a> with a cleaner, simpler interface. It makes exposing raw D functions and classes to Python almost trivially simple. It bears certain similarities to <a href="http://www.boost.org/libs/python/doc/">Boost.Python</a>, and indeed Boost.Python is a major influence on Pyd.</p> 
    2626 
    2727<p>Pyd also comes with its own extension to Python's <a href="http://docs.python.org/dist/dist.html">distutils</a>, called <a href="celerid.html">CeleriD</a>, making the building of extension modules quite easy.</p> 
  • trunk/infrastructure/pyd/func_wrap.d

    r40 r44  
    6969} 
    7070 
    71 private void setWrongArgsError(int gotArgs, uint minArgs, uint maxArgs) { 
    72     char[] str = "Wrong number of arguments. Got " ~ 
    73         toString(gotArgs) ~ 
    74         ", expected "; 
    75     if (minArgs == maxArgs) str ~= toString(minArgs) ~ "."; 
    76     else str ~= "between " ~ toString(minArgs) ~ " and " ~ toString(maxArgs) ~ "."; 
     71void setWrongArgsError(int gotArgs, uint minArgs, uint maxArgs, char[] funcName="") { 
     72    char[] str; 
     73    if (funcName == "") { 
     74        str ~= "function takes "; 
     75    } else { 
     76        str ~= funcName ~ "() takes "; 
     77    } 
     78 
     79    char[] argStr(int args) { 
     80        char[] temp = toString(args) ~ " argument"; 
     81        if (args > 1) { 
     82            temp ~= "s"; 
     83        } 
     84        return temp; 
     85    } 
     86 
     87    if (minArgs == maxArgs) { 
     88        if (minArgs == 0) { 
     89            str ~= "no arguments"; 
     90        } else { 
     91            str ~= "exactly " ~ argStr(minArgs); 
     92        } 
     93    } 
     94    else if (gotArgs < minArgs) { 
     95        str ~= "at least " ~ argStr(minArgs); 
     96    } else { 
     97        str ~= "at most " ~ argStr(maxArgs); 
     98    } 
     99    str ~= " (" ~ toString(gotArgs) ~ " given)"; 
     100 
    77101    PyErr_SetString(PyExc_TypeError, str ~ \0); 
    78102} 
  • trunk/infrastructure/pyd/make_object.d

    r37 r44  
    196196    // 
    197197    // This also means that: 
    198     //  (1) Conversion to Object will construct an object and return that. 
     198    //  (1) Conversion to DPyObject will construct an object and return that. 
    199199    //  (2) Any integral type smaller than a C_long (which is usually just 
    200200    //      an int, meaning short and byte) will use the bool conversion. 
     
    203203        return o; 
    204204    } else static if (is(DPyObject : T)) { 
    205         return new DPyObject(o); 
     205        return new DPyObject(o, true); 
    206206    } else static if (is(T == void)) { 
    207207        if (o != Py_None) could_not_convert!(T)(o); 
  • trunk/infrastructure/pyd/op_wrap.d

    r43 r44  
    3131 
    3232private import meta.FuncMeta; 
     33private import meta.Nameof; 
    3334 
    3435template wrapped_class_as_number(T) { 
     
    7778template wrapped_class_as_sequence(T) { 
    7879    static PySequenceMethods wrapped_class_as_sequence = { 
    79         null,                            /*sq_length*/ 
     80        length_wrap!(T),                 /*sq_length*/ 
    8081        opCat_wrap!(T),                  /*sq_concat*/ 
    8182        null,                            /*sq_repeat*/ 
     
    8485        opIndexAssign_sequence_wrap!(T), /*sq_ass_item*/ 
    8586        opSliceAssign_wrap!(T),          /*sq_ass_slice*/ 
    86         null,                            /*sq_contains*/ 
     87        opIn_wrap!(T),                   /*sq_contains*/ 
    8788        opCatAssign_wrap!(T),            /*sq_inplace_concat*/ 
    8889        null,                            /*sq_inplace_repeat*/ 
     
    9899} 
    99100 
     101//----------------// 
     102// Implementation // 
     103//----------------// 
    100104template opfunc_binary_wrap(T, alias opfn) { 
    101105    alias wrapped_class_object!(T) wrap_object; 
     
    105109        return exception_catcher(delegate PyObject*() { 
    106110            auto dg = dg_wrapper((cast(wrap_object*)self).d_obj, &opfn); 
    107             return _py(dg(d_type!(Info.Meta.ArgType!(0))(o))); 
     111            pragma(msg, prettytypeof!(typeof(dg))); 
     112            pragma(msg, symbolnameof!(opfn)); 
     113            return _py( 
     114                dg( 
     115                    d_type!(Info.Meta.ArgType!(0))(o) 
     116                ) 
     117            ); 
    108118        }); 
    109119    } 
     
    143153} 
    144154 
     155template opindex_mapping_pyfunc(T) { 
     156    alias wrapped_class_object!(T) wrap_object; 
     157    alias funcDelegInfoT!(typeof(&T.opIndex)) Info; 
     158    const uint ARGS = Info.numArgs; 
     159 
     160    // Multiple arguments are converted into tuples, and thus become a standard 
     161    // wrapped member function call. A single argument is passed directly. 
     162    static if (ARGS == 1) { 
     163        alias Info.Meta.ArgType!(0) KeyT; 
     164        extern(C) 
     165        PyObject* func(PyObject* self, PyObject* key) { 
     166            return exception_catcher(delegate PyObject*() { 
     167                return _py((cast(wrap_object*)self).d_obj.opIndex(d_type!(KeyT)(key))); 
     168            }); 
     169        } 
     170    } else { 
     171        extern(C) 
     172        PyObject* func(PyObject* self, PyObject* key) { 
     173            int args; 
     174            if (!PyTuple_CheckExact(key)) { 
     175                args = 1; 
     176            } else { 
     177                args = PySequence_Length(key); 
     178            } 
     179            if (ARGS != args) { 
     180                setWrongArgsError(args, ARGS, ARGS); 
     181                return null; 
     182            } 
     183            return func_wrap!(T.opIndex, ARGS, T).func(self, key); 
     184        } 
     185    } 
     186} 
     187 
    145188template opindexassign_mapping_pyfunc(T) { 
    146189    alias wrapped_class_object!(T) wrap_object; 
    147190    alias funcDelegInfoT!(typeof(&T.opIndexAssign)) Info; 
    148     alias Info.Meta.ArgType!(0) ValT; 
    149     alias Info.Meta.ArgType!(1) KeyT; 
    150  
    151     extern(C) 
    152     int func(PyObject* self, PyObject* key, PyObject* val) { 
    153         return exception_catcher(delegate int() { 
    154             (cast(wrap_object*)self).d_obj.opIndexAssign(d_type!(ValT)(val), d_type!(KeyT)(key)); 
     191    const uint ARGS = Info.numArgs; 
     192 
     193    static if (ARGS > 2) { 
     194        extern(C) 
     195        int func(PyObject* self, PyObject* key, PyObject* val) { 
     196            int args; 
     197            if (!PyTuple_CheckExact(key)) { 
     198                args = 2; 
     199            } else { 
     200                args = PySequence_Length(key) + 1; 
     201            } 
     202            if (ARGS != args) { 
     203                setWrongArgsError(args, ARGS, ARGS); 
     204                return -1; 
     205            } 
     206            // Build a new tuple with the value at the front. 
     207            PyObject* temp = PyTuple_New(ARGS); 
     208            if (temp is null) return -1; 
     209            scope(exit) Py_DECREF(temp); 
     210            PyTuple_SetItem(temp, 0, val); 
     211            for (int i=1; i<ARGS; ++i) { 
     212                Py_INCREF(PyTuple_GetItem(key, i-1)); 
     213                PyTuple_SetItem(temp, i, PyTuple_GetItem(key, i-1)); 
     214            } 
     215            func_wrap!(T.opIndexAssign, ARGS, T).func(self, temp); 
    155216            return 0; 
    156         }); 
     217        } 
     218    } else { 
     219        alias Info.Meta.ArgType!(0) ValT; 
     220        alias Info.Meta.ArgType!(1) KeyT; 
     221 
     222        extern(C) 
     223        int func(PyObject* self, PyObject* key, PyObject* val) { 
     224            return exception_catcher(delegate int() { 
     225                (cast(wrap_object*)self).d_obj.opIndexAssign(d_type!(ValT)(val), d_type!(KeyT)(key)); 
     226                return 0; 
     227            }); 
     228        } 
    157229    } 
    158230} 
     
    217289} 
    218290 
    219 // The rest of the file is composed of these short stubs 
     291template length_pyfunc(T) { 
     292    alias wrapped_class_object!(T) wrap_object; 
     293 
     294    extern(C) 
     295    int func(PyObject* self) { 
     296        return exception_catcher(delegate int() { 
     297            return (cast(wrap_object*)self).d_obj.length(); 
     298        }); 
     299    } 
     300
     301 
     302//----------// 
     303// Dispatch // 
     304//----------// 
     305template length_wrap(T) { 
     306    static if ( 
     307        is(typeof(&T.length)) && 
     308        is(typeof(&T.length()) : int) 
     309    ) { 
     310        const inquiry length_wrap = &length_pyfunc!(T).func; 
     311    } else { 
     312        const inquiry length_wrap = null; 
     313    } 
     314
     315 
    220316template opIndex_sequence_wrap(T) { 
    221317    static if ( 
     
    245341    static if ( 
    246342        is(typeof(&T.opIndex)) && 
    247         funcDelegInfoT!(typeof(&T.opIndex)).numArgs == 1 && 
    248         !is(funcDelegInfoT!(typeof(&T.opIndex)).Meta.ArgType!(0) : int) 
     343        (funcDelegInfoT!(typeof(&T.opIndex)).numArgs > 1 || 
     344        !is(funcDelegInfoT!(typeof(&T.opIndex)).Meta.ArgType!(0) : int)) 
    249345    ) { 
    250         const binaryfunc opIndex_mapping_wrap = &opfunc_binary_wrap!(T, T.opIndex).func; 
     346        const binaryfunc opIndex_mapping_wrap = &opindex_mapping_pyfunc!(T).func; 
    251347    } else { 
    252348        const binaryfunc opIndex_mapping_wrap = null; 
     
    257353    static if ( 
    258354        is(typeof(&T.opIndexAssign)) && 
    259         funcDelegInfoT!(typeof(&T.opIndexAssign)).numArgs == 2 && 
    260         !is(funcDelegInfoT!(typeof(&T.opIndexAssign)).Meta.ArgType!(1) : int) 
     355        (funcDelegInfoT!(typeof(&T.opIndexAssign)).numArgs > 2 || 
     356        !is(funcDelegInfoT!(typeof(&T.opIndexAssign)).Meta.ArgType!(1) : int)) 
    261357    ) { 
    262358        const objobjargproc opIndexAssign_mapping_wrap = &opindexassign_mapping_pyfunc!(T).func; 
     
    273369        is(funcDelegInfoT!(typeof(&T.opSlice)).Meta.ArgType!(1) : int) 
    274370    ) { 
    275         const intintargfunc opSlice_wrap = opslice_pyfunc!(T).func; 
     371        const intintargfunc opSlice_wrap = &opslice_pyfunc!(T).func; 
    276372    } else { 
    277373        const intintargfunc opSlice_wrap = null; 
     
    286382        is(funcDelegInfoT!(typeof(&T.opSlice)).Meta.ArgType!(2) : int) 
    287383    ) { 
    288         const intintobjargproc opSliceAssign_wrap = opsliceassign_pyfunc!(T).func; 
     384        const intintobjargproc opSliceAssign_wrap = &opsliceassign_pyfunc!(T).func; 
    289385    } else { 
    290386        const intintobjargproc opSliceAssign_wrap = null; 
     
    509605template opIn_wrap(T) { 
    510606    static if (is(typeof(&T.opIn_r))) { 
    511         const binaryfunc opIn_wrap = &opin_wrap.func; 
    512     } else { 
    513         const binaryfunc opIn_wrap = null; 
     607        const objobjproc opIn_wrap = &opin_wrap.func; 
     608    } else { 
     609        const objobjproc opIn_wrap = null; 
    514610    } 
    515611}