From 0aa8729d500932e60afcbb50e8b2de623fba9db9 Mon Sep 17 00:00:00 2001
From: Stefan Zager
-The -builtin option gives a significant performance improvement
-in the wrapped code. More information about python built-in extensions is available
-here.
+The -builtin option provides a significant performance improvement
+in the wrapped code. To understand the difference between proxy classes
+and built-in types, let's take a look at what a wrapped object looks like
+under both circumstances.
+ When proxy classes are used, each wrapped object in python is an instance
+of a pure python class. As a reminder, here is what the __init__ method looks
+like in a proxy class:
+ When a Foo instance is created, the call to _example.new_Foo()
+creates a new C++ Foo instance; wraps that C++ instance inside an instance of
+a python built-in type called SwigPyObject; and stores the SwigPyObject
+instance in the 'this' field of the python Foo object. Did you get all that? So, the
+python Foo object is composed of three parts: When -builtin is used, the pure python layer is stripped off. Each
+wrapped class is turned into a new python built-in type which inherits from
+SwigPyObject, and SwigPyObject instances are returned directly
+from the wrapped methods. For more information about python built-in extensions,
+please refer to the python documentation: docs.python.org/extending/extending.html.
Use of the -builtin option implies a couple of limitations:
33.4.2 Built-in Classes
+class Foo(object):
+ def __init__(self):
+ self.this = _example.new_Foo()
+ self.thisown = 1
+
+
+
+
+33.4.2.1 Limitations
+
@@ -2331,7 +2372,7 @@ strings, you can define an 'operator+ (const char*)' method :
class MyString {
public:
MyString (const char *init);
- MyString operator+ (const char *other);
+ MyString operator+ (const char *other) const;
...
};
@@ -2341,21 +2382,21 @@ public:
swig will automatically create an operator overload in python that will allow this:
+
from MyModule import MyString
-mystr = MyString("Nobody expects")
+mystr = MyString("No one expects")
episode = mystr + " the Spanish Inquisition"
This works because the first operand -- the instance of MyString -- defines a way -to add a MyString and a native string. However, the following will not work: +to add a native string to itself. However, the following will not work:
-from MyModule import MyString @@ -2366,12 +2407,108 @@ episode = "Dead " + mystrThe above code fails, because the first operand -- a native python string -- -doesn't know how to add itself to an instance of MyString. +doesn't know how to add an instance of MyString to itself.
-33.4.2.2 Getting the most out of builtin-types
+33.4.2.2 Operator overloads -- use them!
+ +The entire justification for the -builtin option is improved +performance. To that end, the best way to squeeze maximum performance out +of your wrappers is to use operator overloads. +Named method dispatch is slow in python, even when compared to other scripting languages. +However, python built-in types have a large number of "slots", +analogous to C++ operator overloads, which allow you to short-circuit name method dispatch +for certain common operations. +
+ +By default, swig will translate most C++ arithmetic operator overloads into python +slot entries. For example, suppose you have this class: + +
++ ++class DeadParrot { +public: + DeadParrot operator+ (const DeadParrot& dp) const; + + // Dispatch to operator+ + DeadParrot add (const DeadParrot& dp) const + { return *this + dp; } +}; ++... then you may write python code like this:
+ +++ ++from MyModule import DeadParrot + +dp1 = DeadParrot() +dp2 = DeadParrot() +dp3 = dp1 + dp2 +dp4 = dp1.add(dp2) ++The last two lines of the python code are equivalent, +but the line that uses the '+' operator is much faster. +
+ +In-place operators (e.g., operator+=) and comparison operators +(operator==, operator<, etc.) are also converted to python +slot operators. For a complete list of C++ operators that are +automatically converted to python slot operators, refer to the file +python/pyopers.swig in the SWIG library. +
+ +There are other very useful python slots that you +may explicitly define using %feature directives. For example, +suppose you want to use instances of a wrapped class as keys in a native python +dict. That will work as long as you define a hash function for +instances of your class, and use it to define the python tp_hash +slot: +
+ +++ ++class Cheese { +public: + Cheese (const char *name); + long cheeseHashFunc () const; +}; + +%feature("python:slot", "tp_hash", functype="hashfunc") Cheese::cheeseHashFunc; ++This will allow you to write python code like this:
+ +++ ++from my MyPackage import Cheese + +inventory = { + Cheese("cheddar") : 0, + Cheese("gouda") : 0, + Cheese("camembert") : 0 +} ++Because you defined the tp_hash slot, Cheese objects may +be used as hash keys; and when the cheeseHashFunc method is invoked +by a python dict, it will not go through named method dispatch. +A more detailed discussion about %feature("python:slot") can be found +in the file python/pyopers.swig in the SWIG library. +You can read about all of the available python slots here:
+ +docs.python.org/c-api/typeobj.html
+ +You may override (almost) all of the slots defined in the PyTypeObject, +PyNumberMethods, PyMappingMethods, PySequenceMethods, and PyBufferProcs +structs. +
-TODO
33.4.3 Memory management
diff --git a/Lib/python/pyopers.swg b/Lib/python/pyopers.swg index 2dbaba41e..1ce4f0980 100644 --- a/Lib/python/pyopers.swg +++ b/Lib/python/pyopers.swg @@ -1,5 +1,61 @@ /* ------------------------------------------------------------ * Overloaded operator support + + The directives in this file apply whether or not you use the + -builtin option to swig, but operator overloads are particularly + attractive when using -builtin, because they are much faster + than named methods. + + If you're using the -builtin option to swig, and you want to define + python operator overloads beyond the defaults defined in this file, + here's what you need to know: + + There are two ways to define a python slot function: dispatch to a + statically defined function; or dispatch to a method defined on the + operand. + + To dispatch to a statically defined function, use %feature("python:"), + where is the name of a field in a PyTypeObject, PyNumberMethods, + PyMappingMethods, PySequenceMethods, or PyBufferProcs. For example: + + %{ + + static long myHashFunc (PyObject *pyobj) { + MyClass *cobj; + // Convert pyobj to cobj + return (cobj->field1 * (cobj->field2 << 7)); + } + + %} + + %feature("python:tp_hash") MyClass "myHashFunc"; + + NOTE: It is the responsibility of the programmer (that's you) to ensure + that a statically defined slot function has the correct signature. + + If, instead, you want to dispatch to an instance method, you can + use %feature("python:slot"). For example: + + class MyClass { + public: + long myHashFunc () const; + ... + }; + + %feature("python:slot", "tp_hash", functype="hashfunc") MyClass::myHashFunc; + + NOTE: Some python slots use a method signature which does not + match the signature of swig-wrapped methods. For those slots, + swig will automatically generate a "closure" function to re-marshall + the arguments before dispatching to the wrapped method. Setting + the "functype" attribute of the feature enables swig to generate + a correct closure function. + + For more information about python slots, including their names and + signatures, you may refer to the python documentation : + + http://docs.python.org/c-api/typeobj.html + * ------------------------------------------------------------ */