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 @@
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.
debug_flags
version_flags
, the strings in this list will be passed to D as debug flags.raw_only
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
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
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
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
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
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.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="");
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));
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));
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);
prop
will automatically attempt to wrap both the "get" and "set" forms of the property, unless RO is specified.def
, prop
will attempt to derive this automatically.static void init(C ...) ();
init
function.static void parent(Parent) ();
static void hide();
struct Init(C ...);
init
function.static void iter(iter_t) ();
struct Iter(iter_t);
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);
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);
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 @@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 tomodule_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.barThere 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, theget_overload
function.
ReturnType!(dg_t) get_overload(dg_t, T ...) (dg_t dg, char[] name, T t);
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 @@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...) ();
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="");
struct Member(char[] realname, char[] name=realname);
static void def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn)) (char[] docstring="");
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));
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="");
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));
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="");
prop
function used to wrap class properties.struct Property(alias fn, char[] name = symbolnameof!(fn), bool RO = false);
Property
struct template used to wrap class properties.static void iter(iter_t) ();
iter
function used in class wrapping.struct Iter(iter_t);
Iter
struct template used in class wrapping.static void alt_iter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail) (char[] docstring="");
alt_iter
function used in class wrapping.struct AltIter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail);
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.
Support for operator overloading in structs is identical to that available for classes.
+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.
+(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 @@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.
debug_flags
version_flags
, the strings in this list will be passed to D as debug flags.raw_only
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
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
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
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
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
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.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="");
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));
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));
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);
prop
will automatically attempt to wrap both the "get" and "set" forms of the property, unless RO is specified.def
, prop
will attempt to derive this automatically.static void init(C ...) ();
init
function.static void parent(Parent) ();
static void hide();
struct Init(C ...);
init
function.static void iter(iter_t) ();
struct Iter(iter_t);
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);
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);
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 @@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 tomodule_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.barThere 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, theget_overload
function.
ReturnType!(dg_t) get_overload(dg_t, T ...) (dg_t dg, char[] name, T t);
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 @@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...) ();
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="");
struct Member(char[] realname, char[] name=realname);
static void def(alias fn, char[] name = symbolnameof!(fn), fn_t = typeof(&fn)) (char[] docstring="");
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));
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="");
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));
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="");
prop
function used to wrap class properties.struct Property(alias fn, char[] name = symbolnameof!(fn), bool RO = false);
Property
struct template used to wrap class properties.static void iter(iter_t) ();
iter
function used in class wrapping.struct Iter(iter_t);
Iter
struct template used in class wrapping.static void alt_iter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail) (char[] docstring="");
alt_iter
function used in class wrapping.struct AltIter(alias fn, char[] name = symbolnameof!(fn), iter_t = implementationDetail);
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.
Support for operator overloading in structs is identical to that available for classes.
+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.
+(Todo.)