Changeset 41
- Timestamp:
- 10/26/06 18:10:53 (2 years ago)
- Files:
-
- trunk/examples/testdll/testdll.d (modified) (2 diffs)
- trunk/html_doc/class_wrapping.html (modified) (6 diffs)
- trunk/html_doc/func_wrapping.html (modified) (2 diffs)
- trunk/infrastructure/pyd/class_wrap.d (modified) (6 diffs)
- trunk/infrastructure/pyd/op_wrap.d (modified) (4 diffs)
- trunk/infrastructure/pyd/pyd.d (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/examples/testdll/testdll.d
r40 r41 106 106 extern (C) 107 107 export void inittestdll() { 108 def!(foo /*, "foo"*/);108 def!(foo); 109 109 // Python does not support function overloading. This allows us to wrap 110 110 // an overloading function under a different name. Note that if the … … 112 112 // must be specified. 113 113 def!(foo, "foo2", void function(int), 1); 114 def!(bar , "bar");114 def!(bar); 115 115 // Default argument support - Now implicit! 116 def!(baz , "baz");117 def!(spam , "spam");118 def!(iter_test , "iter_test");119 def!(func_test , "func_test");120 def!(dg_test , "dg_test");116 def!(baz); 117 def!(spam); 118 def!(iter_test); 119 def!(func_test); 120 def!(dg_test); 121 121 122 122 module_init("testdll"); 123 123 124 auto t = func_range!(foo, 0)(); 125 alias typeof(t) Tu; 126 writefln(typeid(TypeNo!(Tu, 1))); 127 writefln(MIN_ARGS!(bar)); 128 129 wrapped_class!(Foo, "Foo") f; 124 wrapped_class!(Foo) f; 130 125 // Constructor wrapping 131 126 f.init!(void function(int), void function(int, int)); 132 127 // Member function wrapping 133 f.def!(Foo.foo , "foo");128 f.def!(Foo.foo); 134 129 // Property wrapping 135 f.prop!(Foo.i , "i");130 f.prop!(Foo.i); 136 131 finalize_class(f); 137 132 } trunk/html_doc/class_wrapping.html
r38 r41 26 26 <p>Exposing D classes to Python is easy! The heart of Pyd's class wrapping features is the <code>wrapped_class</code> template struct:</p> 27 27 28 <p><code>struct wrapped_class(<span class="t_arg">T</span>, char[] <span class="t_arg">classname</span> );</code></p>28 <p><code>struct wrapped_class(<span class="t_arg">T</span>, char[] <span class="t_arg">classname</span> = symbolnameof!(T));</code></p> 29 29 <ul> 30 30 <li><span class="t_arg">T</span> is the class being wrapped.</li> … … 35 35 36 36 <dl> 37 <dt><code>static void def(alias <span class="t_arg">fn</span>, char[] <span class="t_arg">name</span> , <span class="t_arg">fn_t</span> = typeof(&fn)) ();</code></dt>37 <dt><code>static void def(alias <span class="t_arg">fn</span>, char[] <span class="t_arg">name</span> = symbolnameof!(fn), <span class="t_arg">fn_t</span> = typeof(&fn)) ();</code></dt> 38 38 <dd>This wraps a method of the class. It functions exactly like the <code>def</code> function used to <a href="func_wrapping.html">wrap regular functions</a>, with one very important difference: There is no support for default arguments. (This is a side-effect of the fact that you cannot call an alias of a method in D, and delegates do not understand default arguments.)</dd> 39 39 … … 42 42 <ul> 43 43 <li><span class="t_arg">fn</span> is the name of the property. <code>prop</code> will automatically attempt to wrap both the "get" and "set" forms of the property, unless <span class="t_arg">RO</span> is specified.</li> 44 <li><span class="t_arg">name</span> is the name of the property as it will appear in Python. </li>44 <li><span class="t_arg">name</span> is the name of the property as it will appear in Python. As with <code>def</code>, <code>prop</code> will attempt to derive this automatically.</li> 45 45 <li><span class="t_arg">RO</span> specifies whether this is a <i>read-only</i> property. If true, it will only wrap the "get" form of the property. If false, it will wrap both the "get" and "set" forms. (This is a little hackish, and I will probably try to make this detection more automatic in the future. It also means it cannot support a property that only has a "set" form.)</li> 46 46 </ul> … … 48 48 49 49 <dt><code>static void init(<span class="t_arg">C1</span>, <span class="t_arg">C2</span>, <span class="t_arg">C3</span>, ..., <span class="t_arg">C<i>n</i></span>) ();</code></dt> 50 <dd>This allows you to expose anywhere from zero to 10 of 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 of <span class="t_arg">C<i>n</i></span> should be an instance of the <code>pyd.tuples.tuple</code> template (not an instance of the tuple struct <em>itself</em>, but an instance of the <em>template</em>), which in turn supports up to 10 arguments. Each tuple should correspond to a constructor. 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> 50 <dd>This allows you to expose anywhere from zero to 10 of 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 of <span class="t_arg">C<i>n</i></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> 51 52 <dt><code>static void iter(<span class="t_arg">iter_t</span>) ();</code></dt> 53 <dd>This allows the user to specify a different overload of opApply than the default. (The default is always the one that is lexically first.) The <span class="t_arg">iter_t</span> argument should be the type of the delegate that forms the argument to opApply. This might be e.g. <code>int delegate(inout int)</code>. Don't forget the <code>inout</code> modifiers!</dd> 54 55 <dt><code>static void alt_iter(alias <span class="t_arg">fn</span>, char[] <span class="t_arg">name</span> = symbolnameof!(fn), iter_t = <i>implementationDetail</i>) ();</code></dt> 56 <dd>This wraps alternate iterator methods as Python methods that return iterator objects. The wrapped methods should have a signature like that of opApply. (In other words, they should be methods intended to be used with D's ability to iterate over delgates.) The <span class="t_arg">iter_t</span> argument should be the type of the delegate argument to the method. This will usually be derived automatically. 57 </dd> 51 58 </dl> 52 59 … … 74 81 <p>At the moment, only the following operator overloads are supported:</p> 75 82 76 <p><code>opAdd, opSub, opMul, opDiv, opMod, opAnd, opOr, opXor, opShl, opShr, opCat, opAddAssign, opSubAssign, opMulAssign, opDivAssign, opModAssign, opAndAssign, opOrAssign, opXorAssign, opShlAssign, opShrAssign, opCatAssign, opIn_r, op Apply</code></p>83 <p><code>opAdd, opSub, opMul, opDiv, opMod, opAnd, opOr, opXor, opShl, opShr, opCat, opAddAssign, opSubAssign, opMulAssign, opDivAssign, opModAssign, opAndAssign, opOrAssign, opXorAssign, opShlAssign, opShrAssign, opCatAssign, opIn_r, opCmp, opCall, opApply</code></p> 77 84 78 <p>Most notably missing from this list are <code>opIndex, opIndexAssign, opSlice, opSliceAssign</code>, and <code>opCall</code>. Support for these operators is forthcoming. Also missing from the 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.)</p> 85 <p>Most notably missing from this list are <code>opIndex, opIndexAssign, opSlice</code>, and <code>opSliceAssign</code>. Support for these operators is forthcoming. Also missing from the 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>alt_iter</code>.</p> 86 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> if you wish it to be available as a normal property.</p> 89 --> 79 90 80 91 <p>Notably <em>included</em> in the list is <code>opApply</code>. Pyd wraps D's iteration protocol with the help of Mikola Lysenko's StackThreads package.</p> … … 108 119 109 120 <pre class="code"><span class="comment">// Make an instance of wrapped_class</span> 110 wrapped_class!(Foo , <span class="string">"Foo"</span>) f;121 wrapped_class!(Foo) f; 111 122 <span class="comment">// Wrap the "foo" method</span> 112 f.def!(Foo.foo , <span class="string">"foo"</span>);123 f.def!(Foo.foo); 113 124 <span class="comment">// Wrap the "i" property</span> 114 f.prop!(Foo.i , <span class="string">"i"</span>);125 f.prop!(Foo.i); 115 126 <span class="comment">// Wrap the constructors.</span> 116 f.init!( tuple!(<span class="keyword">int</span>), tuple!(<span class="keyword">int</span>, <span class="keyword">int</span>));127 f.init!(<span class="keyword">void function</span>(<span class="keyword">int</span>), <span class="keyword">void function</span>(<span class="keyword">int</span>, <span class="keyword">int</span>)); 117 128 finalize_class(f);</pre> 118 129 trunk/html_doc/func_wrapping.html
r35 r41 26 26 <p>Exposing D functions to Python is easy! The heart of Pyd's function wrapping features is the <code>def</code> template function:</p> 27 27 28 <p><code>void def(alias <span class="t_arg">fn</span>, char[] <span class="t_arg">name</span> , <span class="t_arg">fn_t</span> = typeof(&fn), uint <span class="t_arg">MIN_ARGS</span> = MIN_ARGS!(fn)) ();</code></p>28 <p><code>void def(alias <span class="t_arg">fn</span>, char[] <span class="t_arg">name</span> = symbolnameof!(fn), <span class="t_arg">fn_t</span> = typeof(&fn), uint <span class="t_arg">MIN_ARGS</span> = minArgs!(fn)) ();</code></p> 29 29 <ul> 30 30 <li><span class="t_arg">fn</span> is the function to wrap. Note that this is an alias parameter. <code>def</code> is only capable of wrapping full-fledged functions, not function pointers, function literals, or delegates.</li> 31 <li><span class="t_arg">name</span> is the name the function will have inside of Python. </li>31 <li><span class="t_arg">name</span> is the name the function will have inside of Python. Usually, you don't have to specify this. (Special thanks to Don Clugston's Nameof module.)</li> 32 32 <li><span class="t_arg">fn_t</span> is the function type of the function. Typically, you won't have to specify this. It is used to wrap overloaded functions. (See below.)</li> 33 <li><span class="t_arg">MIN_ARGS</span> is the minimum number of arguments this function can accept. It is used when a function has default arguments. The <code> MIN_ARGS</code> template can derive the correct number automatically, so you typically won't specify this manually.</li>33 <li><span class="t_arg">MIN_ARGS</span> is the minimum number of arguments this function can accept. It is used when a function has default arguments. The <code>minArgs</code> template can derive the correct number automatically, so you typically won't specify this manually.</li> 34 34 </ul> 35 35 36 <p>All calls to <code>def</code> must occur <em>before</em> calling <code>module_init</code>. Any function whose return type and arguments are <a href="conversion.html">convertable</a> can be wrapped by <code>def</code>. <code>def</code> also provides support for wrapping overloaded functions as well as functions with default arguments. Here are some examples:</p>36 <p>All calls to <code>def</code> must occur <em>before</em> calling <code>module_init</code>. Any function whose return type and arguments are <a href="conversion.html">convertable</a> can be wrapped by <code>def</code>. <code>def</code> can only wrap functions with <code>in</code> arguments (not <code>out</code> or <code>inout</code>). <code>def</code> also provides support for wrapping overloaded functions as well as functions with default arguments. Here are some examples:</p> 37 37 38 38 <pre class="code"><span class="keyword">import</span> pyd.pyd; … … 58 58 <span class="keyword">export void</span> inittestmodule() { 59 59 <span class="comment">// Plain old function</span> 60 def!(foo , <span class="string">"foo"</span>);61 <span class="comment">// Wraps the lexically first function </span>60 def!(foo); 61 <span class="comment">// Wraps the lexically first function under the given name</span> 62 62 def!(bar, <span class="string">"bar1"</span>); 63 63 <span class="comment">// Wraps the function of the specified type</span> 64 64 def!(bar, <span class="string">"bar2"</span>, <span class="keyword">void function</span>(<span class="keyword">char</span>[])); 65 65 <span class="comment">// Wraps the function with default arguments</span> 66 def!(baz , <span class="string">"baz"</span>);66 def!(baz); 67 67 68 68 module_init(<span class="string">"testmodule"</span>); trunk/infrastructure/pyd/class_wrap.d
r40 r41 36 36 import meta.FuncMeta; 37 37 import meta.Tuple; 38 import meta.Nameof; 38 39 39 40 import std.string; … … 83 84 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ 84 85 null, /*tp_doc*/ 85 null, /* tp_traverse */86 null, /* tp_clear */87 null, /* tp_richcompare */88 0, /* tp_weaklistoffset */89 null, /* tp_iter */90 null, /* tp_iternext */86 null, /* tp_traverse */ 87 null, /* tp_clear */ 88 null, /* tp_richcompare */ 89 0, /* tp_weaklistoffset */ 90 null, /* tp_iter */ 91 null, /* tp_iternext */ 91 92 null, /* tp_methods */ 92 93 null, /* tp_members */ … … 248 249 * wrapping the specific parts of the class. 249 250 */ 250 template wrapped_class(T, char[] classname ) {251 template wrapped_class(T, char[] classname = symbolnameof!(T)) { 251 252 pragma(msg, "wrapped_class: " ~ classname); 252 253 struct wrapped_class { … … 287 288 * RO = Whether this is a read-only property. 288 289 */ 289 template prop(alias fn, char[] name , bool RO=false) {290 template prop(alias fn, char[] name = symbolnameof!(fn), bool RO=false) { 290 291 pragma(msg, "class.prop: " ~ name); 291 292 static void prop() { … … 376 377 type.tp_methods = wrapped_method_list!(T); 377 378 type.tp_name = module_name ~ "." ~ name ~ \0; 379 380 // Numerical operator overloads 378 381 if (wrapped_class_as_number!(T) != PyNumberMethods.init) { 379 382 type.tp_as_number = &wrapped_class_as_number!(T); 380 383 } 381 384 // Sequence operator overloads 385 if (wrapped_class_as_sequence!(T) != PySequenceMethods.init) { 386 type.tp_as_sequence = &wrapped_class_as_sequence!(T); 387 } 388 // Mapping operator overloads 389 if (wrapped_class_as_mapping!(T) != PyMappingMethods.init) { 390 type.tp_as_mapping = &wrapped_class_as_mapping!(T); 391 } 392 393 // Standard operator overloads 394 // opApply 382 395 static if (is(typeof(&T.opApply))) { 383 396 if (type.tp_iter is null) { … … 386 399 } 387 400 } 388 401 // opCmp 402 static if (is(typeof(&T.opCmp))) { 403 type.tp_compare = &opcmp_wrap!(T).func; 404 } 405 // opCall 406 static if (is(typeof(&T.opCall))) { 407 type.tp_call = cast(ternaryfunc)&func_wrap!(T.opCall, minArgs!(T.opCall), T).func; 408 } 409 389 410 // If a ctor wasn't supplied, try the default. 390 411 if (type.tp_init is null) { trunk/infrastructure/pyd/op_wrap.d
r37 r41 26 26 private import pyd.class_wrap; 27 27 private import pyd.func_wrap; 28 private import pyd.exception; 29 private import pyd.make_object; 30 31 private import meta.FuncMeta; 28 32 29 33 template wrapped_class_as_number(T) { … … 73 77 static PySequenceMethods wrapped_class_as_sequence = { 74 78 null, /*sq_length*/ 75 null,/*sq_concat*/79 opCat_wrap!(T), /*sq_concat*/ 76 80 null, /*sq_repeat*/ 77 81 null, /*sq_item*/ … … 80 84 null, /*sq_ass_slice*/ 81 85 null, /*sq_contains*/ 82 null,/*sq_inplace_concat*/86 opCatAssign_wrap!(T), /*sq_inplace_concat*/ 83 87 null, /*sq_inplace_repeat*/ 84 88 }; … … 115 119 } 116 120 121 template opindex_sequence_pyfunc(T) { 122 alias wrapped_class_object!(T) wrap_object; 123 124 extern(C) 125 PyObject* func(PyObject* self, int i) { 126 return exception_catcher(delegate PyObject*() { 127 return _py((cast(wrap_object*)self).d_obj.opIndex(i)); 128 }); 129 } 130 } 131 132 template opindexassign_sequence_pyfunc(T) { 133 alias wrapped_class_object!(T) wrap_object; 134 135 extern(C) 136 int func(PyObject* self, int i, PyObject* o) { 137 138 } 139 } 140 141 template opcmp_wrap(T) { 142 alias wrapped_class_object!(T) wrap_object; 143 alias funcDelegInfoT!(typeof(&T.opCmp)) Info; 144 alias Info.Meta.ArgType!(0) OtherT; 145 extern(C) 146 int func(PyObject* self, PyObject* other) { 147 return exception_catcher(delegate int() { 148 int result = (cast(wrap_object*)self).d_obj.opCmp(d_type!(OtherT)(other)); 149 // The Python API reference specifies that tp_compare must return 150 // -1, 0, or 1. The D spec says opCmp may return any integer value, 151 // and just compares it with zero. 152 if (result < 0) return -1; 153 if (result == 0) return 0; 154 if (result > 0) return 1; 155 }); 156 } 157 } 158 159 // The rest of the file is composed of these short stubs 160 template opIndex_sequence_wrap(T) { 161 static if (is(typeof(&T.opIndex)) && 162 funcDelegInfoT!(typeof(&T.opIndex)).numArgs == 1 && 163 is(funcDelegInfoT!(typeof(&T.opIndex)).Meta.ArgType!(0) : int) 164 ) { 165 const intargfunc opIndex_sequence_wrap = &opindex_sequence_pyfunc!(T).func; 166 } else { 167 const intargfunc opIndex_sequence_wrap = null; 168 } 169 } 170 171 template opIndexAssign_sequence_wrap(T) { 172 static if (true) { 173 const intobjargproc opIndexAssign_sequence_wrap = null; 174 } else { 175 const intobjargproc opIndexAssign_sequence_wrap = null; 176 } 177 } 178 179 template opIndex_mapping_wrap(T) { 180 181 } 182 183 template opIndexAssign_mapping_wrap(T) { 184 185 } 186 187 template opSlice_wrap(T) { 188 189 } 190 191 template opSliceAssign_wrap(T) { 192 193 } 194 117 195 template opAdd_wrap(T) { 118 196 static if (is(typeof(&T.opAdd))) { trunk/infrastructure/pyd/pyd.d
r29 r41 32 32 public import pyd.dpyobject; 33 33 public import pyd.exception; 34 public import pyd.ftype;35 34 public import pyd.func_wrap; 36 35 public import pyd.make_object; 37 public import pyd.tuples;38 36
