diff --git a/special.html b/special.html new file mode 100644 index 00000000..22ed7d04 --- /dev/null +++ b/special.html @@ -0,0 +1,401 @@ + + + Special Method Name Support + +
+

+ c++boost.gif (8819 bytes)Special Method Name + Support +

+

+ Overview +

+

+ Py_cpp supports all of the standard + special method names supported by real Python class instances + except: +

+ + (more on the latter two below). So, for example, we can wrap a + std::map<std::size_t,std::string> as follows: +

+ Example +

+
+
+typedef std::map<std::size_t, std::string> StringMap;
+
+// A helper function for dealing with errors. Throw a Python exception
+// if p == m.end().
+void throw_key_error_if_end(
+        const StringMap& m, 
+        StringMap::const_iterator p, 
+        std::size_t key)
+{
+    if (p == m.end())
+    {
+        PyErr_SetObject(PyExc_KeyError, py::converters::to_python(key));
+        throw py::ErrorAlreadySet();
+    }
+}
+
+// Define some simple wrapper functions which match the Python  protocol
+// for __getitem__, __setitem__, and __delitem__.  Just as in Python, a
+// free function with a "self" first parameter makes a fine class method.
+
+const std::string& get_item(const StringMap& self, std::size_t key)
+{
+    const StringMap::const_iterator p = self.find(key);
+    throw_key_error_if_end(self, p, key);
+    return p->second;
+}
+
+// Sets the item corresponding to key in the map.
+void StringMapPythonClass::set_item(StringMap& self, std::size_t key, const std::string& value)
+{
+    self[key] = value;
+}
+
+// Deletes the item corresponding to key from the map.
+void StringMapPythonClass::del_item(StringMap& self, std::size_t key)
+{
+    const StringMap::iterator p = self.find(key);
+    throw_key_error_if_end(self, p, key);
+    self.erase(p);
+}
+
+ClassWrapper<StringMap> string_map(my_module, "StringMap");
+string_map.def(py::Constructor<>());
+string_map.def(&StringMap::size, "__len__");
+string_map.def(get_item, "__getitem__");
+string_map.def(set_item, "__setitem__");
+string_map.def(del_item, "__delitem__");
+
+
+

+ Then in Python: +

+
+>>> m = StringMap()
+>>> m[1]
+Traceback (innermost last):
+  File "<stdin>", line 1, in ?
+KeyError: 1
+>>> m[1] = 'hello'
+>>> m[1]
+'hello'
+>>> del m[1]
+>>> m[1]            # prove that it's gone
+Traceback (innermost last):
+  File "<stdin>", line 1, in ?
+KeyError: 1
+>>> del m[2]
+Traceback (innermost last):
+  File "<stdin>", line 1, in ?
+KeyError: 2
+>>> len(m)
+0
+>>> m[3] = 'farther'
+>>> len(m)
+1
+
+
+

+ Getters and Setters +

+

+ Py_cpp extension classes support some additional "special method" + protocols not supported by built-in Python classes. Because writing + __getattr__, __setattr__, and + __delattr__ functions can be tedious in the common case + where the attributes being accessed are known statically, py_cpp checks + the special names +

+ to provide functional access to the attribute <name>. This + facility can be used from C++ or entirely from Python. For example, the + following shows how we can implement a "computed attribute" in Python: +
+
+>>> class Range(AnyPy_cppExtensionClass):
+...    def __init__(self, start, end):
+...        self.start = start
+...        self.end = end
+...    def __getattr__length__(self):
+...        return self.end - self.start
+...
+>>> x = Range(3, 9)
+>>> x.length
+6
+
+
+

+ Direct Access to Data Members +

+

+ Py_cpp uses the special + __xxxattr__<name>__ functionality described above + to allow direct access to data members through the following special + functions on ClassWrapper<> and + ExtensionClass<>: +

+

+ Note that the first two functions, used alone, may produce surprising + behavior. For example, when def_getter() is used, the + default functionality for setattr() and + delattr() remains in effect, operating on items in the extension + instance's name-space (i.e., its __dict__). For that + reason, you'll usually want to stick with def_readonly and + def_read_write. +

+ For example, to expose a std::pair<int,long> we + might write: +

+
+typedef std::pair<int,long> Pil;
+int first(const Pil& x) { return x.first; }
+long second(const Pil& x) { return x.second; }
+   ...
+my_module.def(first, "first");
+my_module.def(second, "second");
+
+ClassWrapper<Pil> pair_int_long(my_module, "Pair");
+pair_int_long.def(py::Constructor<>());
+pair_int_long.def(py::Constructor<int,long>());
+pair_int_long.def_read_write(&Pil::first, "first");
+pair_int_long.def_read_write(&Pil::second, "second");
+
+
+

+ Now your Python class has attributes first and + second which, when accessed, actually modify or reflect the + values of corresponding data members of the underlying C++ object. Now + in Python: +

+
+>>> x = Pair(3,5)
+>>> x.first
+3
+>>> x.second
+5
+>>> x.second = 8
+>>> x.second
+8
+>>> second(x) # Prove that we're not just changing the instance __dict__
+8
+
+
+

+ Numeric Method Support +

+

+ Py_cpp supports the following + Python special numeric method names: +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ Name + + Notes +
+ __add__(self, other) + + operator+(const T&, const T&) +
+ __sub__(self, other) + + operator-(const T&, const T&) +
+ __mul__(self, other) + + operator*(const T&, const T&) +
+ __div__(self, other) + + operator/(const T&, const T&) +
+ __mod__(self, other) + + operator%(const T&, const T&) +
+ __divmod__(self, other) + + return a py::Tuple initialized with + (quotient, + remainder). +
+ __pow__(self, other [, modulo]) + + use overloading to support both + forms of __pow__ +
+ __lshift__(self, other) + + operator<<(const T&, const T&) +
+ __rshift__(self, other) + + operator>>(const T&, const T&) +
+ __and__(self, other) + + operator&(const T&, const T&) +
+ __xor__(self, other) + + operator^(const T&, const T&) +
+ __or__(self, other) + + operator|(const T&, const T&) +
+ __neg__(self) + + operator-(const T&) (unary negation) +
+ __pos__(self) + + operator+(const T&) (identity) +
+ __abs__(self) + + Called to implement the built-in function abs() +
+ __invert__(self) + + operator~(const T&) +
+ __int__(self) + + operator long() const +
+ __long__(self) + + Should return a Python long object. Can be + implemented with PyLong_FromLong(value), + for example. +
+ __float__(self) + + operator double() const +
+ __oct__(self) + + Called to implement the built-in function oct(). Should return a + string value. +
+ __hex__(self) + + Called to implement the built-in function hex(). Should return a + string value. +
+ __coerce__(self, other) + + Should return a Python 2-tuple (C++ code may return a + py::Tuple) where the elements represent the values + of self and other converted to the + same type. +
+ +

Where are the __r<name>__ +functions?

+ +

+ At first we thought that supporting __radd__ and its ilk would be + impossible, since Python doesn't supply any direct support and in fact + implements a special case for its built-in class instances. This + article gives a pretty good overview of the direct support for numerics + that Python supplies for extension types. We've since discovered that it can + be done, but there are some pretty convincing arguments + out there that this arrangement is less-than-ideal. Instead of supplying a + sub-optimal solution for the sake of compatibility with built-in Python + classes, we're doing the neccessary research so we can "do it right". This + will also give us a little time to hear from users about what they want. The + direction we're headed in is based on the idea of multimethods + rather than on trying to find a coercion function bound to one of the + arguments. + +

And what about __complex__?

+

That, dear reader, is one problem we don't know how to solve. The Python + source contains the following fragment, indicating the special-case code really + is hardwired: +

+
+/* XXX Hack to support classes with __complex__ method */
+if (PyInstance_Check(r)) { ...
+
+
+

+ Previous: Function Overloading Next: A Peek Under the Hood Up: Top +

+ © Copyright David Abrahams 2000. Permission to copy, use, modify, + sell and distribute this document is granted provided this copyright + notice appears in all copies. This document is provided "as is" without + express or implied warranty, and with no claim as to its suitability + for any purpose. +

+ Updated: Oct 19, 2000 +

+