| 1 |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
|---|
| 2 |
<html> |
|---|
| 3 |
<head> |
|---|
| 4 |
<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1"> |
|---|
| 5 |
<link href="pyd.css" rel="stylesheet" type="text/css"> |
|---|
| 6 |
<title>Wrapping classes</title> |
|---|
| 7 |
</head> |
|---|
| 8 |
|
|---|
| 9 |
<body> |
|---|
| 10 |
%(nav)s |
|---|
| 11 |
<div id="content"> |
|---|
| 12 |
|
|---|
| 13 |
<h1>Class wrapping</h1> |
|---|
| 14 |
|
|---|
| 15 |
<p>Exposing D classes to Python is easy! The heart of Pyd's class wrapping features is the <code>wrap_class</code> function template:</p> |
|---|
| 16 |
|
|---|
| 17 |
<p><code>void wrap_class(<span class="t_arg">T</span>, char[] <span class="t_arg">classname</span> = symbolnameof!(T), <span class="t_arg">Params</span>...) ();</code></p> |
|---|
| 18 |
<ul> |
|---|
| 19 |
<li><span class="t_arg">T</span> is the class being wrapped.</li> |
|---|
| 20 |
<li><span class="t_arg">classname</span> is the name of the class as it will appear in Python. It defaults to the name of the D class. If you are wrapping an instance of a class template, you will have to provide this explicitly.</li> |
|---|
| 21 |
<li><span class="t_arg">Params</span> is a series of struct types (defined below), which define the various members of the class.</li> |
|---|
| 22 |
</ul> |
|---|
| 23 |
|
|---|
| 24 |
<p>Calls to <code>wrap_class</code> must occur <em>after</em> calling <code>module_init</code>.</p> |
|---|
| 25 |
|
|---|
| 26 |
<p>To expose the constructors, methods, and properties of the class, you must pass <code>wrap_class</code> instantiations of these struct templates.</p> |
|---|
| 27 |
|
|---|
| 28 |
<dl> |
|---|
| 29 |
<dt><code>struct 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> |
|---|
| 30 |
<dd>This wraps a method of the class. It functions very much 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> |
|---|
| 31 |
|
|---|
| 32 |
<dt><code>struct StaticDef(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></dt> |
|---|
| 33 |
<dd>This wraps a static member function of the class. It also functions exactly like the <code>def</code> function used to wrap regular functions, and even includes support for default arguments.</dd> |
|---|
| 34 |
|
|---|
| 35 |
<dt><code>struct Property(alias <span class="t_arg">fn</span>, char[] <span class="t_arg">name</span> = symbolnameof!(fn), bool <span class="t_arg">RO</span> = false);</code></dt> |
|---|
| 36 |
<dd>This wraps a property. See the examples below for more details. |
|---|
| 37 |
<ul> |
|---|
| 38 |
<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> |
|---|
| 39 |
<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> |
|---|
| 40 |
<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. <i>(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.)</i></li> |
|---|
| 41 |
</ul> |
|---|
| 42 |
</dd> |
|---|
| 43 |
|
|---|
| 44 |
<dt><code>struct Init(<span class="t_arg">C</span> ...);</code></dt> |
|---|
| 45 |
<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 type should be the same as the 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 <code>Init</code>.</dd> |
|---|
| 46 |
|
|---|
| 47 |
<dt><code>struct Repr(alias <span class="t_arg">fn</span>);</code></dt> |
|---|
| 48 |
<dd>This allows you to expose a member function of the class as the Python type's <code>__repr__</code> function. The member function must have the signature <code>char[] function()</code>.</dd> |
|---|
| 49 |
|
|---|
| 50 |
<dt><code>struct Iter(<span class="t_arg">iter_t</span>);</code></dt> |
|---|
| 51 |
<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! (This is not available in Linux; see the note below on opApply wrapping.)</dd> |
|---|
| 52 |
|
|---|
| 53 |
<dt><code>struct AltIter(alias <span class="t_arg">fn</span>, char[] <span class="t_arg">name</span> = symbolnameof!(fn), <span class="t_arg">iter_t</span> = <i>implementationDetail</i>);</code></dt> |
|---|
| 54 |
<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. (This is not available in Linux; see the note below on opApply wrapping.) |
|---|
| 55 |
</dd> |
|---|
| 56 |
</dl> |
|---|
| 57 |
|
|---|
| 58 |
<p>If you ever wish to check whether a given class has been wrapped, Pyd helpfully registers all wrapped classes with the <code>is_wrapped</code> template, which is just a templated <code>bool</code>:</p> |
|---|
| 59 |
|
|---|
| 60 |
<p><code>template is_wrapped(<span class="t_arg">T</span>);</code></p> |
|---|
| 61 |
|
|---|
| 62 |
<p>If you have a class <code>Foo</code>, you can check whether it is wrapped by simply checking whether <code>is_wrapped!(Foo)</code> is true. It is important to note that this is <em>not</em> a <code>const bool</code>, it is a <em>runtime</em> check.</p> |
|---|
| 63 |
|
|---|
| 64 |
<h3><a class="anchor" name="opwrap">Automatic operator overloading</a></h3> |
|---|
| 65 |
|
|---|
| 66 |
<p>Pyd will automatically wrap most of D's operator overload functions with appropriate Python operator overloads. There are some caveats:</p> |
|---|
| 67 |
|
|---|
| 68 |
<ul> |
|---|
| 69 |
<li>Pyd will only automatically wrap the lexically first <i>opFunc</i> defined for a given <i>opFunc</i>. <i>(In the future, I may add a mechanism allowing a user to specifiy a specific overload of an opFunc.)</i></li> |
|---|
| 70 |
<li>The usual rules for function wrapping apply: Only an <i>opFunc</i> whose return type and arguments are <a href="conversion.html">convertable</a> may be wrapped. <i>(The current implementation is pretty dumb: If the lexically first opFunc has an unconvertable return type or argument, the operator overload will still be wrapped, but won't work.)</i></li> |
|---|
| 71 |
</ul> |
|---|
| 72 |
|
|---|
| 73 |
<p>At the moment, only the following operator overloads are supported:</p> |
|---|
| 74 |
|
|---|
| 75 |
<p><code>opNeg, opPos, opCom, 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, opIndex, opIndexAssign, opSlice, opSliceAssign</code></p> |
|---|
| 76 |
|
|---|
| 77 |
<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>Def</code>, of course.) Also missing from the list is <code>opApplyReverse</code>. This must be wrapped explicitly with <code>AltIter</code>.</p> |
|---|
| 78 |
|
|---|
| 79 |
<p>Also missing from the list is <code>opAssign</code>. Python has strict reference semantics for its objects, so overloading the assignment operator is not possible. You must explicitly wrap <code>opAssign</code> with a regular method.</p> |
|---|
| 80 |
|
|---|
| 81 |
<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>Property</code> or <code>Def</code> if you wish it to be available as a normal property or method.</p> |
|---|
| 82 |
|
|---|
| 83 |
<p><b>Notes on wrapped operators</b></p> |
|---|
| 84 |
|
|---|
| 85 |
<dl> |
|---|
| 86 |
<dt><code>opApply</code></dt> <dd>Pyd wraps D's iteration protocol with the help of Mikola Lysenko's StackThreads package. This package does not work in GDC, and so opApply wrapping is not available in Linux. See also the <a href="celerid.html"><code>with_st</code></a> option offered by CeleriD.</dd> |
|---|
| 87 |
<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> |
|---|
| 88 |
<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> |
|---|
| 89 |
<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> |
|---|
| 90 |
</dl> |
|---|
| 91 |
|
|---|
| 92 |
<h3><a class="anchor" name="examples">Examples</a></h3> |
|---|
| 93 |
|
|---|
| 94 |
<p>Suppose we have the following simple class:</p> |
|---|
| 95 |
|
|---|
| 96 |
<pre class="code"><span class="keyword">import</span> std.stdio; |
|---|
| 97 |
|
|---|
| 98 |
<span class="keyword">class</span> Foo { |
|---|
| 99 |
<span class="keyword">int</span> m_i; |
|---|
| 100 |
|
|---|
| 101 |
<span class="keyword">this</span>() { m_i = <span class="number">0</span>; } |
|---|
| 102 |
<span class="keyword">this</span>(<span class="keyword">int</span> j) { m_i = j; } |
|---|
| 103 |
<span class="keyword">this</span>(<span class="keyword">int</span> j, <span class="keyword">int</span> k) { m_i = j + k; } |
|---|
| 104 |
|
|---|
| 105 |
<span class="keyword">int</span> i() { <span class="keyword">return</span> m_i; } |
|---|
| 106 |
<span class="keyword">void</span> i(<span class="keyword">int</span> j) { m_i = j; } |
|---|
| 107 |
|
|---|
| 108 |
<span class="keyword">void</span> foo(<span class="keyword">char</span>[] s) { |
|---|
| 109 |
writefln(s, m_i); |
|---|
| 110 |
} |
|---|
| 111 |
|
|---|
| 112 |
Foo opAdd(Foo rhs) { |
|---|
| 113 |
<span class="keyword">return new</span> Foo(m_i + rhs.m_i); |
|---|
| 114 |
} |
|---|
| 115 |
}</pre> |
|---|
| 116 |
|
|---|
| 117 |
<p>We would expose this class to Python by putting this code in <code>PydMain</code> after the call to <code>module_init</code>:</p> |
|---|
| 118 |
|
|---|
| 119 |
<pre class="code"><span class="comment">// Call wrap_class</span> |
|---|
| 120 |
wrap_class!( |
|---|
| 121 |
Foo, |
|---|
| 122 |
<span class="comment">// Wrap the "foo" method</span> |
|---|
| 123 |
Def!(Foo.foo), |
|---|
| 124 |
<span class="comment">// Wrap the "i" property</span> |
|---|
| 125 |
Property!(Foo.i), |
|---|
| 126 |
<span class="comment">// Wrap the constructors.</span> |
|---|
| 127 |
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>)) |
|---|
| 128 |
);</pre> |
|---|
| 129 |
|
|---|
| 130 |
<p>Now we can use this type from within Python like any other type.</p> |
|---|
| 131 |
|
|---|
| 132 |
<pre class="code">>>> <span class="keyword">from</span> testmodule <span class="keyword">import</span> Foo |
|---|
| 133 |
>>> f = Foo() |
|---|
| 134 |
>>> f.i |
|---|
| 135 |
0 |
|---|
| 136 |
>>> f.i = <span class="number">20</span> |
|---|
| 137 |
>>> f.foo(<span class="string">"Hello! i is "</span>) |
|---|
| 138 |
Hello! i is 20 |
|---|
| 139 |
>>> f = Foo(<span class="number">10</span>, <span class="number">10</span>) |
|---|
| 140 |
>>> f.i |
|---|
| 141 |
20 |
|---|
| 142 |
>>> g = Foo(<span class="number">30</span>) |
|---|
| 143 |
>>> g.i |
|---|
| 144 |
30 |
|---|
| 145 |
>>> e = f + g |
|---|
| 146 |
>>> e.i |
|---|
| 147 |
50 |
|---|
| 148 |
>>> <span class="comment"># We can even subclass our D type</span> |
|---|
| 149 |
>>> <span class="keyword">class</span> MyFoo(Foo): |
|---|
| 150 |
... <span class="keyword">def</span> bar(self): |
|---|
| 151 |
... <span class="keyword">print</span> <span class="string">"Hey, i+3 is"</span>, self.i + <span class="number">3</span> |
|---|
| 152 |
... |
|---|
| 153 |
>>> h = MyFoo(<span class="number">3</span>) |
|---|
| 154 |
>>> h.bar() |
|---|
| 155 |
Hey, i+3 is 6 |
|---|
| 156 |
>>> </pre> |
|---|
| 157 |
|
|---|
| 158 |
</div> |
|---|
| 159 |
|
|---|
| 160 |
</body> |
|---|
| 161 |
</html> |
|---|