diff --git a/html_doc/basics.html b/html_doc/basics.html index 670d7c5..99abffb 100644 --- a/html_doc/basics.html +++ b/html_doc/basics.html @@ -50,7 +50,7 @@

Module basics

It does little more than call Py_InitModule and return the new module object. This object is also available via the Pyd_Module_p property once you've called module_init.

-

Due to the way in which Pyd implements function and class wrapping, any calls to def must occur before the call to module_init, and any calls to finalize_class must occur after the call. I know this seems like a rather arbitrary rule, but it is important. Calls to def in the wrong place will simply be ignored, and calls to finalize_class in the wrong place will throw an assert.

+

Due to the way in which Pyd implements function and class wrapping, any calls to def must occur before the call to module_init, and any calls to wrap_class must occur after the call. I know this seems like a rather arbitrary rule, but it is important. Calls to def in the wrong place will simply be ignored, and calls to wrap_class in the wrong place will throw an assert.

PydMain will catch any D exception that is thrown from inside it, and safely pass that exception to Python.

diff --git a/html_doc/celerid.html b/html_doc/celerid.html index 94a784f..47571ac 100644 --- a/html_doc/celerid.html +++ b/html_doc/celerid.html @@ -57,7 +57,7 @@

CeleriD

debug_flags
Similar to version_flags, the strings in this list will be passed to D as debug flags.
raw_only
This flag defaults to False. When True, it supresses the compilation and linkage of Pyd, StackThreads, and meta. This is useful if you only want to write a raw Python/C extension without the overhead of Pyd and its auxiliary packages. This is equivalent to specifying False to the next four flags.
with_pyd
This flag defaults to True. When False, it supresses the compilation and linkage of Pyd. This is useful if you want to write a raw Python/C extension and don't want the overhead of compiling Pyd.
-
with_st
This flag defaults to True. When False, it supresses the compilation and linkage of StackThreads. Pyd uses StackThreads for its iteration wrapping support. By setting this to False, opApply wrapping, wrapped_class.iter, and wrapped_class.alt_iter will be unavailable. If with_pyd and this are True, then the Pyd_with_StackThreads version flag will be defined (which is used internally by Pyd). Important note: StackThreads does not currently work with GDC! CeleriD will always set this flag to False when using GDC! This means that opApply wrapping is not available on Linux at this time.
+
with_st
This flag defaults to True. When False, it supresses the compilation and linkage of StackThreads. Pyd uses StackThreads for its iteration wrapping support. By setting this to False, opApply wrapping, Iter, and AltIter will be unavailable. If with_pyd and this are True, then the Pyd_with_StackThreads version flag will be defined (which is used internally by Pyd). Important note: StackThreads does not currently work with GDC! CeleriD will always set this flag to False when using GDC! This means that opApply wrapping is not available on Linux at this time.
with_meta
This flag defaults to True. When False, it supresses the compilation and linkage of meta (Pyd's metaprogramming package). Because Pyd depends on meta, an exception will be raised if with_pyd is True and this is not.
with_main
This flag defaults to True. When False, it supresses the use of the "magic" PydMain function. (Instead, users must manually declare a C-style init function.) Do not use this unless you know what you are doing. If with_pyd is False, this will silently be set to False as well. PydMain can only be used if Pyd itself is in use.
diff --git a/html_doc/class_wrapping.html b/html_doc/class_wrapping.html index 2ea2387..a3ab172 100644 --- a/html_doc/class_wrapping.html +++ b/html_doc/class_wrapping.html @@ -28,56 +28,46 @@

Class wrapping

-

Exposing D classes to Python is easy! The heart of Pyd's class wrapping features is the wrapped_class template struct:

+

Exposing D classes to Python is easy! The heart of Pyd's class wrapping features is the wrap_class function template:

-

struct wrapped_class(T, char[] classname = symbolnameof!(T));

+

void wrap_class(T, char[] classname = symbolnameof!(T), Params...) ();

-

To expose the constructors, methods, and properties of the class, wrapped_class provides a series of template member functions.

+

Calls to wrap_class must occur after calling module_init.

+ +

To expose the constructors, methods, and properties of the class, you must pass wrap_class instantiations of these struct templates.

-
static void def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn)) (char[] docstring="");
-
This wraps a method of the class. It functions exactly like the def function used to wrap regular functions, 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.)
+
struct Def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn));
+
This wraps a method of the class. It functions very much like the def function used to wrap regular functions, 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.)
-
static void static_def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn), uint MIN_ARGS = minArgs!(fn)) (char[] docstring="");
+
struct StaticDef(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn), uint MIN_ARGS = minArgs!(fn));
This wraps a static member function of the class. It also functions exactly like the def function used to wrap regular functions, and even includes support for default arguments.
-
static void prop(alias fn, char[] name = symbolnameof!(fn), bool RO = false) (char[] docstring="");
+
struct Property(alias fn, char[] name = symbolnameof!(fn), bool RO = false);
This wraps a property. See the examples below for more details.
-
static void init(C ...) ();
-
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 C 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 init function.
- -
static void parent(Parent) ();
-
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 inheritance), 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.
- -
static void hide();
-
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 inheritance.
+
struct Init(C ...);
+
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 C 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 the init function.
-
static void iter(iter_t) ();
+
struct Iter(iter_t);
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 iter_t argument should be the type of the delegate that forms the argument to opApply. This might be e.g. int delegate(inout int). Don't forget the inout modifiers! (This is not available in Linux; see the note below on opApply wrapping.)
-
static void alt_iter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail) (char[] docstring="");
+
struct AltIter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail);
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 iter_t 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.)
-

Once you have called all of the member functions of wrapped_class that you wish to, you must issue a call to finalize_class.

- -

void finalize_class(CLS) (CLS cls, char[] docstring="");

- -

This does some final initialization of the class and then registers it with Python. Unlike calls to def, calls to finalize_class must occur after calling module_init. The cls function argument should be an instance of wrapped_class.

-

If you ever wish to check whether a given class has been wrapped, Pyd helpfully registers all wrapped classes with the is_wrapped template, which is just a templated bool:

template is_wrapped(T);

@@ -95,13 +85,13 @@

Automatic operator overloading

At the moment, only the following operator overloads are supported:

-

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

+

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

-

Missing from this list are opUShr and opUShrAssign. 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 wrapped_class.def, of course.) Also missing from the list is opApplyReverse. This must be wrapped explicitly with wrapped_class.alt_iter.

+

Missing from this list are opUShr and opUShrAssign. 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 Def, of course.) Also missing from the list is opApplyReverse. This must be wrapped explicitly with AltIter.

Also missing from the list is opAssign. Python has strict reference semantics for its objects, so overloading the assignment operator is not possible. You must explicitly wrap opAssign with a regular method.

-

Additionally, if a class provides a length property, Pyd will automatically make it available via Python's built-in function len and the special __len__ method. You may still wrap it with prop or def if you wish it to be available as a normal property or method.

+

Additionally, if a class provides a length property, Pyd will automatically make it available via Python's built-in function len and the special __len__ method. You may still wrap it with Property or Def if you wish it to be available as a normal property or method.

Notes on wrapped operators

@@ -139,24 +129,25 @@

Examples

We would expose this class to Python by putting this code in PydMain after the call to module_init:

-
// Make an instance of wrapped_class
-wrapped_class!(Foo) f;
-// Wrap the "foo" method
-f.def!(Foo.foo);
-// Wrap the "i" property
-f.prop!(Foo.i);
-// Wrap the constructors.
-f.init!(void function(int), void function(int, int));
-finalize_class(f);
+
// Call wrap_class
+wrap_class!(
+    Foo,
+    // Wrap the "foo" method
+    Def!(Foo.foo),
+    // Wrap the "i" property
+    Property!(Foo.i),
+    // Wrap the constructors.
+    Init!(void function(int), void function(int, int))
+);

Now we can use this type from within Python like any other type.

-
>>> from testmodule import Foo
+
>>> from testmodule import Foo
 >>> f = Foo()
 >>> f.i
 0
 >>> f.i = 20
->>> f.foo("Hello! i is ")
+>>> f.foo("Hello! i is ")
 Hello! i is 20
 >>> f = Foo(10, 10)
 >>> f.i
@@ -167,10 +158,10 @@ 

Examples

>>> e = f + g >>> e.i 50 ->>> # We can even subclass our D type ->>> class MyFoo(Foo): -... def bar(self): -... print "Hey, i+3 is", self.i + 3 +>>> # We can even subclass our D type +>>> class MyFoo(Foo): +... def bar(self): +... print "Hey, i+3 is", self.i + 3 ... >>> h = MyFoo(3) >>> h.bar() diff --git a/html_doc/inherit.html b/html_doc/inherit.html index f8e297c..87aaf2b 100644 --- a/html_doc/inherit.html +++ b/html_doc/inherit.html @@ -43,14 +43,16 @@

Inheritance

These would be exposed to Python by putting this code in PydMain after the call to module_init:

-
wrapped_class!(Base) b;
-b.def!(Base.foo);
-b.def!(Base.bar);
-finalize_class(b);
+
wrap_class!(
+    Base,
+    Def!(Base.foo),
+    Def!(Base.bar),
+);
 
-wrapped_class!(Derived) d;
-d.def!(Derived.foo);
-finalize_class(d);
+wrap_class!( + Derived, + Def!(Derived.foo), +);

When used in Python, we get the expected behavior:

@@ -67,7 +69,7 @@

Inheritance

>>> d.bar() Base.bar
-

There is one weakness in the default behavior. Take a function like the following:

+

Polymorphic behavior is also automatically taken care of. Take a function like the following:

void polymorphic_call(Base b) {
     b.foo();
@@ -75,114 +77,14 @@ 

Inheritance

And in Python:

-
class PyClass(Base):
-    def foo(self):
-        print "PyClass.foo"
-
+
>>> class PyClass(Base):
+... 	def foo(self):
+... 		print "PyClass.foo"
+... 
 >>> p = PyClass()
 >>> polymorphic_call(p)
-Base.foo
- -

Optimally, we would want polymorphic_call to call PyClass.foo. This requires some additional work on the D side of things. To get this behavior, then rather than expose Base directly, we must expose a wrapper class:

- -
class BaseWrap : Base {
-    mixin OverloadShim;
-    void foo() {
-        get_overload(&super.foo, "foo");
-    }
-    void bar() {
-        get_overload(&super.bar, "bar");
-    }
-}
- -

The OverloadShim template has but a single member, the get_overload function.

- -
-
ReturnType!(dg_t) get_overload(dg_t, T ...) (dg_t dg, char[] name, T t);
-
    -
  • dg should be a delegate to the parent class's method.
  • -
  • name should be the name of the method as Python understands it to be. (There's no efficient way to derive this automatically based on only the delegate.)
  • -
  • t is a tuple argument. These arguments will be passed on to the actual function call, be it the parent class's implementation or a Python subclass's implementation.
  • -
-
- -

get_overload returns whatever the method does.

- -

Now, we must replace the old wrapping of Base with this:

- -
wrapped_class!(BaseWrap, "Base") w;
-w.def!(BaseWrap.foo);
-w.def!(BaseWrap.bar);
-finalize_class(w);
- -

Now our subclass will perform just like we expect:

- -
>>> p = PyClass()
->>> polymorphic_call(p)
 PyClass.foo
-

However, BaseWrap has no particular relationship to Derived. You may remember that Derived overloads bar but not foo. When we wrapped Derived in PydMain, we specified the foo overload but not the bar overload. Because Derived's parent class is no longer wrapped, Pyd no longer has any way to know about the bar method of the Derived class.

- -

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 OverloadShim subclass of Derived (call it DerivedWrap), and telling Pyd that BaseWrap is its parent. Additionally, the original Base and Derived 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:

- -
import pyd.pyd;
-import std.stdio;
-
-class Base {
-    void foo() { writefln("Base.foo"); }
-    void bar() { writefln("Base.bar"); }
-}
-
-class Derived : Base {
-    void foo() { writefln("Derived.foo"); }
-}
-
-class BaseWrap : Base {
-    mixin OverloadShim;
-    void foo() {
-        get_overload(&super.foo, "foo");
-    }
-    void bar() {
-        get_overload(&super.bar, "bar");
-    }
-}
-
-class DerivedWrap : Derived {
-    mixin OverloadShim;
-    void foo() {
-        get_overload(&super.foo, "foo");
-    }
-}
-
-extern (C) void PydMain() {
-    module_init();
-
-    wrapped_class!(Base) b;
-    w.hide();
-    w.def!(Base.foo);
-    w.def!(Base.bar);
-    finalize_class(w);
-
-    wrapped_class!(Derived) d;
-    d.hide();
-    d.def!(Derived.foo);
-    finalize_class(d);
-
-    wrapped_class!(BaseWrap, "Base") bw;
-    bw.def!(BaseWrap.foo);
-    bw.def!(BaseWrap.bar);
-    finalize_class(bw);
-
-    wrapped_class!(DerivedWrap, "Derived") dw;
-    dw.parent!(BaseWrap);
-    dw.def!(DerivedWrap.foo);
-    finalize_class(dw);
-}
- -

(I recognize that this is astoundingly ugly. However, it is the best solution I can come up with without resorting to code generation.)

- -

The inherit example in the Pyd distribution provides a more complete version of this example, including how wrapper code should handle constructors.

-

(TODO: Add support for interfaces and abstract classes.)

diff --git a/html_doc/struct_wrapping.html b/html_doc/struct_wrapping.html index 23d6c56..b85855c 100644 --- a/html_doc/struct_wrapping.html +++ b/html_doc/struct_wrapping.html @@ -30,41 +30,38 @@

Struct wrapping

Wrapping D's structs is similar to wrapping classes. In fact, many of the operations are identical.

-

struct wrapped_struct(T, char[] structname = symbolnameof!(T));

+

void wrap_struct(T, char[] structname = symbolnameof!(T), Params...) ();

  • T is the struct being wrapped.
  • structname is the name of the struct as it will appear in Python.
  • +
  • Params is a series of struct types (defined below), which define the various members of the struct.
-

To expose the data members, member functions, and properties of the class, wrapped_struct provides a series of template member functions.

+

As with calls to wrap_class, calls to wrap_struct must occur after calling module_init.

+ +

To expose the data members, member functions, and properties of the struct, you must pass a series of struct template instantiations to wrap_struct.

-
static void member(M, size_t offset, char[] name) (char[] docstring="");
-
This exposes a data member of the struct to Python. M is the type of the member, and must be a convertible type. offset is the offset (in bytes) of the member in the struct. name is the name of the data member as it will be used in Python. (Optimally, one would simply be able to pass an alias to the member, or at worst an alias and a name, but DMD currently has some issues with this.)
+
struct Member(char[] realname, char[] name=realname);
+
This exposes a data member of the struct to Python. The member must be a convertible type. The realname is the member's actual name. name is the name of the data member as it will be used in Python. This defaults to realname.
-
static void def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn)) (char[] docstring="");
-
This wraps a member function of the struct. It functions exactly like the def function used to wrap class methods, including the lack of support for default arguments.
+
struct Def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn));
+
This wraps a member function of the struct. It is in fact exactly the same Def struct template used to wrap class methods, including the lack of support for default arguments.
-
static void static_def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn), uint MIN_ARGS = minArgs!(fn)) (char[] docstring="");
-
This wraps a static member function of the struct. It functions exactly like the static_def function used to wrap static class member functions, and also includes support for default arguments.
+
struct StaticDef(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn), uint MIN_ARGS = minArgs!(fn));
+
This wraps a static member function of the struct. It is the same StaticDef struct template used to wrap static class member functions, and also includes support for default arguments.
-
static void prop(alias fn, char[] name = symbolnameof!(fn), bool RO = false) (char[] docstring="");
-
This wraps a property. It is identical to the prop function used to wrap class properties.
+
struct Property(alias fn, char[] name = symbolnameof!(fn), bool RO = false);
+
This wraps a property. It is the same Property struct template used to wrap class properties.
-
static void iter(iter_t) ();
-
This allows the user to specify a different overload of opApply than the default. (The default is always the one that is lexically first.) It is identical to the iter function used in class wrapping.
+
struct Iter(iter_t);
+
This allows the user to specify a different overload of opApply than the default. (The default is always the one that is lexically first.) It is the same Iter struct template used in class wrapping.
-
static void alt_iter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail) (char[] docstring="");
-
This wraps alternate iterator methods as Python methods that return iterator objects. It is is identical to the alt_iter function used in class wrapping.
+
struct AltIter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail);
+
This wraps alternate iterator methods as Python methods that return iterator objects. It is the same AltIter struct template used in class wrapping.
-

(Future enhancements: Support for struct ctors.)

- -

Once you have called all of the member functions of wrapped_struct that you wish to, you must issue a call to finalize_struct.

- -

void finalize_struct(S) (S s, char[] docstring="");

- -

This does some final initialization of the type and then registers it with Python. As with calls to finalize_class, calls to finalize_struct must occur after calling module_init. The s function argument should be an instance of wrapped_struct.

+

The is_wrapped template is available for wrapped structs, just like it is for wrapped classes.

@@ -74,6 +71,10 @@

Automatic operator overloading

Support for operator overloading in structs is identical to that available for classes.

+

Inheritance

+ +

D does not support struct inheritance. Therefore, Pyd does not provide any support for struct inheritance. However, the Python type wrapping the D struct can be subclassed from within Python. Users should not expect polymorphic behavior if they attempt to pass instances of any subclasses back to D.

+

Examples

(Todo.)

diff --git a/raw_html/basics.html b/raw_html/basics.html index 3ee19e9..7d1cfc8 100644 --- a/raw_html/basics.html +++ b/raw_html/basics.html @@ -34,7 +34,7 @@

Module basics

It does little more than call Py_InitModule and return the new module object. This object is also available via the Pyd_Module_p property once you've called module_init.

-

Due to the way in which Pyd implements function and class wrapping, any calls to def must occur before the call to module_init, and any calls to finalize_class must occur after the call. I know this seems like a rather arbitrary rule, but it is important. Calls to def in the wrong place will simply be ignored, and calls to finalize_class in the wrong place will throw an assert.

+

Due to the way in which Pyd implements function and class wrapping, any calls to def must occur before the call to module_init, and any calls to wrap_class must occur after the call. I know this seems like a rather arbitrary rule, but it is important. Calls to def in the wrong place will simply be ignored, and calls to wrap_class in the wrong place will throw an assert.

PydMain will catch any D exception that is thrown from inside it, and safely pass that exception to Python.

diff --git a/raw_html/celerid.html b/raw_html/celerid.html index d1004dd..8b2c0bb 100644 --- a/raw_html/celerid.html +++ b/raw_html/celerid.html @@ -41,7 +41,7 @@

CeleriD

debug_flags
Similar to version_flags, the strings in this list will be passed to D as debug flags.
raw_only
This flag defaults to False. When True, it supresses the compilation and linkage of Pyd, StackThreads, and meta. This is useful if you only want to write a raw Python/C extension without the overhead of Pyd and its auxiliary packages. This is equivalent to specifying False to the next four flags.
with_pyd
This flag defaults to True. When False, it supresses the compilation and linkage of Pyd. This is useful if you want to write a raw Python/C extension and don't want the overhead of compiling Pyd.
-
with_st
This flag defaults to True. When False, it supresses the compilation and linkage of StackThreads. Pyd uses StackThreads for its iteration wrapping support. By setting this to False, opApply wrapping, wrapped_class.iter, and wrapped_class.alt_iter will be unavailable. If with_pyd and this are True, then the Pyd_with_StackThreads version flag will be defined (which is used internally by Pyd). Important note: StackThreads does not currently work with GDC! CeleriD will always set this flag to False when using GDC! This means that opApply wrapping is not available on Linux at this time.
+
with_st
This flag defaults to True. When False, it supresses the compilation and linkage of StackThreads. Pyd uses StackThreads for its iteration wrapping support. By setting this to False, opApply wrapping, Iter, and AltIter will be unavailable. If with_pyd and this are True, then the Pyd_with_StackThreads version flag will be defined (which is used internally by Pyd). Important note: StackThreads does not currently work with GDC! CeleriD will always set this flag to False when using GDC! This means that opApply wrapping is not available on Linux at this time.
with_meta
This flag defaults to True. When False, it supresses the compilation and linkage of meta (Pyd's metaprogramming package). Because Pyd depends on meta, an exception will be raised if with_pyd is True and this is not.
with_main
This flag defaults to True. When False, it supresses the use of the "magic" PydMain function. (Instead, users must manually declare a C-style init function.) Do not use this unless you know what you are doing. If with_pyd is False, this will silently be set to False as well. PydMain can only be used if Pyd itself is in use.
diff --git a/raw_html/class_wrapping.html b/raw_html/class_wrapping.html index ab97f4b..0d1227f 100644 --- a/raw_html/class_wrapping.html +++ b/raw_html/class_wrapping.html @@ -12,56 +12,46 @@

Class wrapping

-

Exposing D classes to Python is easy! The heart of Pyd's class wrapping features is the wrapped_class template struct:

+

Exposing D classes to Python is easy! The heart of Pyd's class wrapping features is the wrap_class function template:

-

struct wrapped_class(T, char[] classname = symbolnameof!(T));

+

void wrap_class(T, char[] classname = symbolnameof!(T), Params...) ();

  • T is the class being wrapped.
  • -
  • classname is the name of the class as it will appear in Python.
  • +
  • classname 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.
  • +
  • Params is a series of struct types (defined below), which define the various members of the class.
-

To expose the constructors, methods, and properties of the class, wrapped_class provides a series of template member functions.

+

Calls to wrap_class must occur after calling module_init.

+ +

To expose the constructors, methods, and properties of the class, you must pass wrap_class instantiations of these struct templates.

-
static void def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn)) (char[] docstring="");
-
This wraps a method of the class. It functions exactly like the def function used to wrap regular functions, 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.)
+
struct Def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn));
+
This wraps a method of the class. It functions very much like the def function used to wrap regular functions, 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.)
-
static void static_def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn), uint MIN_ARGS = minArgs!(fn)) (char[] docstring="");
+
struct StaticDef(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn), uint MIN_ARGS = minArgs!(fn));
This wraps a static member function of the class. It also functions exactly like the def function used to wrap regular functions, and even includes support for default arguments.
-
static void prop(alias fn, char[] name = symbolnameof!(fn), bool RO = false) (char[] docstring="");
+
struct Property(alias fn, char[] name = symbolnameof!(fn), bool RO = false);
This wraps a property. See the examples below for more details.
  • fn is the name of the property. prop will automatically attempt to wrap both the "get" and "set" forms of the property, unless RO is specified.
  • name is the name of the property as it will appear in Python. As with def, prop will attempt to derive this automatically.
  • RO specifies whether this is a read-only 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.)
  • -
  • docstring is the property's docstring. As usual, note that this is a regular function argument, and not a template argument.
-
static void init(C ...) ();
-
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 C 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 init function.
- -
static void parent(Parent) ();
-
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 inheritance), 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.
- -
static void hide();
-
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 inheritance.
+
struct Init(C ...);
+
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 C 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 the init function.
-
static void iter(iter_t) ();
+
struct Iter(iter_t);
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 iter_t argument should be the type of the delegate that forms the argument to opApply. This might be e.g. int delegate(inout int). Don't forget the inout modifiers! (This is not available in Linux; see the note below on opApply wrapping.)
-
static void alt_iter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail) (char[] docstring="");
+
struct AltIter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail);
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 iter_t 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.)
-

Once you have called all of the member functions of wrapped_class that you wish to, you must issue a call to finalize_class.

- -

void finalize_class(CLS) (CLS cls, char[] docstring="");

- -

This does some final initialization of the class and then registers it with Python. Unlike calls to def, calls to finalize_class must occur after calling module_init. The cls function argument should be an instance of wrapped_class.

-

If you ever wish to check whether a given class has been wrapped, Pyd helpfully registers all wrapped classes with the is_wrapped template, which is just a templated bool:

template is_wrapped(T);

@@ -79,13 +69,13 @@

Automatic operator overloading

At the moment, only the following operator overloads are supported:

-

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

+

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

-

Missing from this list are opUShr and opUShrAssign. 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 wrapped_class.def, of course.) Also missing from the list is opApplyReverse. This must be wrapped explicitly with wrapped_class.alt_iter.

+

Missing from this list are opUShr and opUShrAssign. 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 Def, of course.) Also missing from the list is opApplyReverse. This must be wrapped explicitly with AltIter.

Also missing from the list is opAssign. Python has strict reference semantics for its objects, so overloading the assignment operator is not possible. You must explicitly wrap opAssign with a regular method.

-

Additionally, if a class provides a length property, Pyd will automatically make it available via Python's built-in function len and the special __len__ method. You may still wrap it with prop or def if you wish it to be available as a normal property or method.

+

Additionally, if a class provides a length property, Pyd will automatically make it available via Python's built-in function len and the special __len__ method. You may still wrap it with Property or Def if you wish it to be available as a normal property or method.

Notes on wrapped operators

@@ -123,24 +113,25 @@

Examples

We would expose this class to Python by putting this code in PydMain after the call to module_init:

-
// Make an instance of wrapped_class
-wrapped_class!(Foo) f;
-// Wrap the "foo" method
-f.def!(Foo.foo);
-// Wrap the "i" property
-f.prop!(Foo.i);
-// Wrap the constructors.
-f.init!(void function(int), void function(int, int));
-finalize_class(f);
+
// Call wrap_class
+wrap_class!(
+    Foo,
+    // Wrap the "foo" method
+    Def!(Foo.foo),
+    // Wrap the "i" property
+    Property!(Foo.i),
+    // Wrap the constructors.
+    Init!(void function(int), void function(int, int))
+);

Now we can use this type from within Python like any other type.

-
>>> from testmodule import Foo
+
>>> from testmodule import Foo
 >>> f = Foo()
 >>> f.i
 0
 >>> f.i = 20
->>> f.foo("Hello! i is ")
+>>> f.foo("Hello! i is ")
 Hello! i is 20
 >>> f = Foo(10, 10)
 >>> f.i
@@ -151,10 +142,10 @@ 

Examples

>>> e = f + g >>> e.i 50 ->>> # We can even subclass our D type ->>> class MyFoo(Foo): -... def bar(self): -... print "Hey, i+3 is", self.i + 3 +>>> # We can even subclass our D type +>>> class MyFoo(Foo): +... def bar(self): +... print "Hey, i+3 is", self.i + 3 ... >>> h = MyFoo(3) >>> h.bar() diff --git a/raw_html/inherit.html b/raw_html/inherit.html index 459d475..79dd89b 100644 --- a/raw_html/inherit.html +++ b/raw_html/inherit.html @@ -27,14 +27,16 @@

Inheritance

These would be exposed to Python by putting this code in PydMain after the call to module_init:

-
wrapped_class!(Base) b;
-b.def!(Base.foo);
-b.def!(Base.bar);
-finalize_class(b);
+
wrap_class!(
+    Base,
+    Def!(Base.foo),
+    Def!(Base.bar),
+);
 
-wrapped_class!(Derived) d;
-d.def!(Derived.foo);
-finalize_class(d);
+wrap_class!( + Derived, + Def!(Derived.foo), +);

When used in Python, we get the expected behavior:

@@ -51,7 +53,7 @@

Inheritance

>>> d.bar() Base.bar
-

There is one weakness in the default behavior. Take a function like the following:

+

Polymorphic behavior is also automatically taken care of. Take a function like the following:

void polymorphic_call(Base b) {
     b.foo();
@@ -59,114 +61,14 @@ 

Inheritance

And in Python:

-
class PyClass(Base):
-    def foo(self):
-        print "PyClass.foo"
-
+
>>> class PyClass(Base):
+... 	def foo(self):
+... 		print "PyClass.foo"
+... 
 >>> p = PyClass()
 >>> polymorphic_call(p)
-Base.foo
- -

Optimally, we would want polymorphic_call to call PyClass.foo. This requires some additional work on the D side of things. To get this behavior, then rather than expose Base directly, we must expose a wrapper class:

- -
class BaseWrap : Base {
-    mixin OverloadShim;
-    void foo() {
-        get_overload(&super.foo, "foo");
-    }
-    void bar() {
-        get_overload(&super.bar, "bar");
-    }
-}
- -

The OverloadShim template has but a single member, the get_overload function.

- -
-
ReturnType!(dg_t) get_overload(dg_t, T ...) (dg_t dg, char[] name, T t);
-
    -
  • dg should be a delegate to the parent class's method.
  • -
  • name should be the name of the method as Python understands it to be. (There's no efficient way to derive this automatically based on only the delegate.)
  • -
  • t is a tuple argument. These arguments will be passed on to the actual function call, be it the parent class's implementation or a Python subclass's implementation.
  • -
-
- -

get_overload returns whatever the method does.

- -

Now, we must replace the old wrapping of Base with this:

- -
wrapped_class!(BaseWrap, "Base") w;
-w.def!(BaseWrap.foo);
-w.def!(BaseWrap.bar);
-finalize_class(w);
- -

Now our subclass will perform just like we expect:

- -
>>> p = PyClass()
->>> polymorphic_call(p)
 PyClass.foo
-

However, BaseWrap has no particular relationship to Derived. You may remember that Derived overloads bar but not foo. When we wrapped Derived in PydMain, we specified the foo overload but not the bar overload. Because Derived's parent class is no longer wrapped, Pyd no longer has any way to know about the bar method of the Derived class.

- -

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 OverloadShim subclass of Derived (call it DerivedWrap), and telling Pyd that BaseWrap is its parent. Additionally, the original Base and Derived 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:

- -
import pyd.pyd;
-import std.stdio;
-
-class Base {
-    void foo() { writefln("Base.foo"); }
-    void bar() { writefln("Base.bar"); }
-}
-
-class Derived : Base {
-    void foo() { writefln("Derived.foo"); }
-}
-
-class BaseWrap : Base {
-    mixin OverloadShim;
-    void foo() {
-        get_overload(&super.foo, "foo");
-    }
-    void bar() {
-        get_overload(&super.bar, "bar");
-    }
-}
-
-class DerivedWrap : Derived {
-    mixin OverloadShim;
-    void foo() {
-        get_overload(&super.foo, "foo");
-    }
-}
-
-extern (C) void PydMain() {
-    module_init();
-
-    wrapped_class!(Base) b;
-    w.hide();
-    w.def!(Base.foo);
-    w.def!(Base.bar);
-    finalize_class(w);
-
-    wrapped_class!(Derived) d;
-    d.hide();
-    d.def!(Derived.foo);
-    finalize_class(d);
-
-    wrapped_class!(BaseWrap, "Base") bw;
-    bw.def!(BaseWrap.foo);
-    bw.def!(BaseWrap.bar);
-    finalize_class(bw);
-
-    wrapped_class!(DerivedWrap, "Derived") dw;
-    dw.parent!(BaseWrap);
-    dw.def!(DerivedWrap.foo);
-    finalize_class(dw);
-}
- -

(I recognize that this is astoundingly ugly. However, it is the best solution I can come up with without resorting to code generation.)

- -

The inherit example in the Pyd distribution provides a more complete version of this example, including how wrapper code should handle constructors.

-

(TODO: Add support for interfaces and abstract classes.)

diff --git a/raw_html/struct_wrapping.html b/raw_html/struct_wrapping.html index 9f30f21..980170c 100644 --- a/raw_html/struct_wrapping.html +++ b/raw_html/struct_wrapping.html @@ -14,41 +14,38 @@

Struct wrapping

Wrapping D's structs is similar to wrapping classes. In fact, many of the operations are identical.

-

struct wrapped_struct(T, char[] structname = symbolnameof!(T));

+

void wrap_struct(T, char[] structname = symbolnameof!(T), Params...) ();

  • T is the struct being wrapped.
  • structname is the name of the struct as it will appear in Python.
  • +
  • Params is a series of struct types (defined below), which define the various members of the struct.
-

To expose the data members, member functions, and properties of the class, wrapped_struct provides a series of template member functions.

+

As with calls to wrap_class, calls to wrap_struct must occur after calling module_init.

+ +

To expose the data members, member functions, and properties of the struct, you must pass a series of struct template instantiations to wrap_struct.

-
static void member(M, size_t offset, char[] name) (char[] docstring="");
-
This exposes a data member of the struct to Python. M is the type of the member, and must be a convertible type. offset is the offset (in bytes) of the member in the struct. name is the name of the data member as it will be used in Python. (Optimally, one would simply be able to pass an alias to the member, or at worst an alias and a name, but DMD currently has some issues with this.)
+
struct Member(char[] realname, char[] name=realname);
+
This exposes a data member of the struct to Python. The member must be a convertible type. The realname is the member's actual name. name is the name of the data member as it will be used in Python. This defaults to realname.
-
static void def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn)) (char[] docstring="");
-
This wraps a member function of the struct. It functions exactly like the def function used to wrap class methods, including the lack of support for default arguments.
+
struct Def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn));
+
This wraps a member function of the struct. It is in fact exactly the same Def struct template used to wrap class methods, including the lack of support for default arguments.
-
static void static_def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn), uint MIN_ARGS = minArgs!(fn)) (char[] docstring="");
-
This wraps a static member function of the struct. It functions exactly like the static_def function used to wrap static class member functions, and also includes support for default arguments.
+
struct StaticDef(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn), uint MIN_ARGS = minArgs!(fn));
+
This wraps a static member function of the struct. It is the same StaticDef struct template used to wrap static class member functions, and also includes support for default arguments.
-
static void prop(alias fn, char[] name = symbolnameof!(fn), bool RO = false) (char[] docstring="");
-
This wraps a property. It is identical to the prop function used to wrap class properties.
+
struct Property(alias fn, char[] name = symbolnameof!(fn), bool RO = false);
+
This wraps a property. It is the same Property struct template used to wrap class properties.
-
static void iter(iter_t) ();
-
This allows the user to specify a different overload of opApply than the default. (The default is always the one that is lexically first.) It is identical to the iter function used in class wrapping.
+
struct Iter(iter_t);
+
This allows the user to specify a different overload of opApply than the default. (The default is always the one that is lexically first.) It is the same Iter struct template used in class wrapping.
-
static void alt_iter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail) (char[] docstring="");
-
This wraps alternate iterator methods as Python methods that return iterator objects. It is is identical to the alt_iter function used in class wrapping.
+
struct AltIter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail);
+
This wraps alternate iterator methods as Python methods that return iterator objects. It is the same AltIter struct template used in class wrapping.
-

(Future enhancements: Support for struct ctors.)

- -

Once you have called all of the member functions of wrapped_struct that you wish to, you must issue a call to finalize_struct.

- -

void finalize_struct(S) (S s, char[] docstring="");

- -

This does some final initialization of the type and then registers it with Python. As with calls to finalize_class, calls to finalize_struct must occur after calling module_init. The s function argument should be an instance of wrapped_struct.

+

The is_wrapped template is available for wrapped structs, just like it is for wrapped classes.

@@ -58,6 +55,10 @@

Automatic operator overloading

Support for operator overloading in structs is identical to that available for classes.

+

Inheritance

+ +

D does not support struct inheritance. Therefore, Pyd does not provide any support for struct inheritance. However, the Python type wrapping the D struct can be subclassed from within Python. Users should not expect polymorphic behavior if they attempt to pass instances of any subclasses back to D.

+

Examples

(Todo.)