//----------------------------------*-C++-*----------------------------------// // Copyright 1998 The Regents of the University of California. // All rights reserved. See LEGAL.LLNL for full text and disclaimer. //---------------------------------------------------------------------------// #ifndef __CXX_Objects__h #define __CXX_Objects__h // Prevent warnings #if defined(_XOPEN_SOURCE) #undef _XOPEN_SOURCE #endif #include "Python.h" #include "Config.hxx" #include "Exception.hxx" #include #include #include #include #include #include namespace Py { typedef int sequence_index_type; // type of an index into a sequence // Forward declarations class Object; class Type; template class SeqBase; class String; class List; template class MapBase; // new_reference_to also overloaded below on Object inline PyObject* new_reference_to(PyObject* p) { Py::_XINCREF(p); return p; } // returning Null() from an extension method triggers a // Python exception inline PyObject* Null() { return (static_cast(0)); } //===========================================================================// // class Object // The purpose of this class is to serve as the most general kind of // Python object, for the purpose of writing C++ extensions in Python // Objects hold a PyObject* which they own. This pointer is always a // valid pointer to a Python object. In children we must maintain this behavior. // // Instructions on how to make your own class MyType descended from Object: // (0) Pick a base class, either Object or perhaps SeqBase or MapBase. // This example assumes Object. // (1) Write a routine int MyType_Check (PyObject *) modeled after PyInt_Check, // PyFloat_Check, etc. // (2) Add method accepts: // virtual bool accepts (PyObject *pyob) const { // return pyob && MyType_Check (pyob); // } // (3) Include the following constructor and copy constructor // /* explicit MyType (PyObject *pyob): Object(pyob) { validate(); } MyType(const Object& other): Object(other.ptr()) { validate(); } */ // Alernate version for the constructor to allow for construction from owned pointers: /* explicit MyType (PyObject *pyob): Object(pyob) { validate(); } */ // You may wish to add other constructors; see the classes below for examples. // Each constructor must use "set" to set the pointer // and end by validating the pointer you have created. // (4) Each class needs at least these two assignment operators: /* MyType& operator= (const Object& rhs) { return (*this = *rhs); } Mytype& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set(rhsp); return *this; } */ // Note on accepts: constructors call the base class // version of a virtual when calling the base class constructor, // so the test has to be done explicitly in a descendent. // If you are inheriting from PythonExtension to define an object // note that it contains PythonExtension::check // which you can use in accepts when writing a wrapper class. // See Demo/range.h and Demo/range.cxx for an example. class Object { private: // the pointer to the Python object // Only Object sets this directly. // The default constructor for Object sets it to Py_None and // child classes must use "set" to set it // PyObject* p; protected: void set (PyObject* pyob, bool owned = false) { release(); p = pyob; if (!owned) { Py::_XINCREF (p); } validate(); } void release () { Py::_XDECREF (p); p = 0; } void validate() { // release pointer if not the right type if (! accepts (p)) { release (); if(PyErr_Occurred()) { // Error message already set throw Exception(); } // Better error message if RTTI available #if defined( _CPPRTTI ) std::string s("Error creating object of type "); s += (typeid (*this)).name(); throw TypeError (s); #else throw TypeError ("CXX: type error."); #endif } } public: // Constructor acquires new ownership of pointer unless explicitly told not to. explicit Object (PyObject* pyob=Py::_None(), bool owned = false): p (pyob) { if(!owned) { Py::_XINCREF (p); } validate(); } // Copy constructor acquires new ownership of pointer Object (const Object& ob): p(ob.p) { Py::_XINCREF (p); validate(); } // Assignment acquires new ownership of pointer Object& operator= (const Object& rhs) { set(rhs.p); return *this; } Object& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (rhsp); return *this; } // Destructor virtual ~Object () { release (); } // Loaning the pointer to others, retain ownership PyObject* operator* () const { return p; } // Explicit reference_counting changes void increment_reference_count() { Py::_XINCREF(p); } void decrement_reference_count() { // not allowed to commit suicide, however if(reference_count() == 1) throw RuntimeError("Object::decrement_reference_count error."); Py::_XDECREF(p); } // Would like to call this pointer() but messes up STL in SeqBase PyObject* ptr () const { return p; } // // Queries // // Can pyob be used in this object's constructor? virtual bool accepts (PyObject *pyob) const { return (pyob != 0); } int reference_count () const { // the reference count return p ? p->ob_refcnt : 0; } Type type () const; // the type object associated with this one String str () const; // the str() representation std::string as_string() const; String repr () const; // the repr () representation List dir () const; // the dir() list bool hasAttr (const std::string& s) const { return PyObject_HasAttrString (p, const_cast(s.c_str())) ? true: false; } Object getAttr (const std::string& s) const { return Object (PyObject_GetAttrString (p, const_cast(s.c_str())), true); } Object getItem (const Object& key) const { return Object (PyObject_GetItem(p, *key), true); } long hashValue () const { return PyObject_Hash (p); } // // int print (FILE* fp, int flags=Py_Print_RAW) // { // return PyObject_Print (p, fp, flags); // } // bool is(PyObject *pother) const { // identity test return p == pother; } bool is(const Object& other) const { // identity test return p == other.p; } bool isCallable () const { return PyCallable_Check (p) != 0; } bool isInstance () const { return PyInstance_Check (p) != 0; } bool isDict () const { return Py::_Dict_Check (p); } bool isList () const { return Py::_List_Check (p); } bool isMapping () const { return PyMapping_Check (p) != 0; } bool isNumeric () const { return PyNumber_Check (p) != 0; } bool isSequence () const { return PySequence_Check (p) != 0; } bool isTrue () const { return PyObject_IsTrue (p) != 0; } bool isType (const Type& t) const; bool isTuple() const { return Py::_Tuple_Check(p); } bool isString() const { return Py::_String_Check(p) || Py::_Unicode_Check(p); } bool isUnicode() const { return Py::_Unicode_Check(p); } // Commands void setAttr (const std::string& s, const Object& value) { if(PyObject_SetAttrString (p, const_cast(s.c_str()), *value) == -1) throw AttributeError ("getAttr failed."); } void delAttr (const std::string& s) { if(PyObject_DelAttrString (p, const_cast(s.c_str())) == -1) throw AttributeError ("delAttr failed."); } // PyObject_SetItem is too weird to be using from C++ // so it is intentionally omitted. void delItem (const Object& /*key*/) { //if(PyObject_DelItem(p, *key) == -1) // failed to link on Windows? throw KeyError("delItem failed."); } // Equality and comparison use PyObject_Compare bool operator==(const Object& o2) const { int k = PyObject_Compare (p, *o2); if (PyErr_Occurred()) throw Exception(); return k == 0; } bool operator!=(const Object& o2) const { int k = PyObject_Compare (p, *o2); if (PyErr_Occurred()) throw Exception(); return k != 0; } bool operator>=(const Object& o2) const { int k = PyObject_Compare (p, *o2); if (PyErr_Occurred()) throw Exception(); return k >= 0; } bool operator<=(const Object& o2) const { int k = PyObject_Compare (p, *o2); if (PyErr_Occurred()) throw Exception(); return k <= 0; } bool operator<(const Object& o2) const { int k = PyObject_Compare (p, *o2); if (PyErr_Occurred()) throw Exception(); return k < 0; } bool operator>(const Object& o2) const { int k = PyObject_Compare (p, *o2); if (PyErr_Occurred()) throw Exception(); return k > 0; } }; // End of class Object inline PyObject* new_reference_to(const Object& g) { PyObject* p = g.ptr(); Py::_XINCREF(p); return p; } // Nothing() is what an extension method returns if // there is no other return value. inline Object Nothing() { return Object(Py::_None()); } // Python special None value inline Object None() { return Object(Py::_None()); } // TMM: 31May'01 - Added the #ifndef so I can exlude iostreams. #ifndef CXX_NO_IOSTREAMS std::ostream& operator<< (std::ostream& os, const Object& ob); #endif // Class Type class Type: public Object { public: explicit Type (PyObject* pyob, bool owned = false): Object(pyob, owned) { validate(); } Type (const Object& ob): Object(*ob) { validate(); } Type(const Type& t): Object(t) { validate(); } Type& operator= (const Object& rhs) { return (*this = *rhs); } Type& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (rhsp); return *this; } virtual bool accepts (PyObject *pyob) const { return pyob && Py::_Type_Check (pyob); } }; // // Convert an owned Python pointer into a CXX Object // inline Object asObject (PyObject *p) { return Object(p, true); } // =============================================== // class Int class Int: public Object { public: // Constructor explicit Int (PyObject *pyob, bool owned = false): Object (pyob, owned) { validate(); } Int (const Int& ob): Object(*ob) { validate(); } // create from long explicit Int (long v = 0L): Object(PyInt_FromLong(v), true) { validate(); } // create from int explicit Int (int v) { long w = v; set(PyInt_FromLong(w), true); validate(); } Int (const Object& ob) { set(PyNumber_Int(*ob), true); validate(); } // Assignment acquires new ownership of pointer Int& operator= (const Object& rhs) { return (*this = *rhs); } Int& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (PyNumber_Int(rhsp), true); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && Py::_Int_Check (pyob); } // convert to long operator long() const { return PyInt_AsLong (ptr()); } // assign from an int Int& operator= (int v) { set (PyInt_FromLong (long(v)), true); return *this; } // assign from long Int& operator= (long v) { set (PyInt_FromLong (v), true); return *this; } }; // =============================================== // class Long class Long: public Object { public: // Constructor explicit Long (PyObject *pyob, bool owned = false): Object (pyob, owned) { validate(); } Long (const Long& ob): Object(ob.ptr()) { validate(); } // create from long explicit Long (long v = 0L) : Object(PyLong_FromLong(v), true) { validate(); } // create from int explicit Long (int v) : Object(PyLong_FromLong(static_cast(v)), true) { validate(); } // create from unsigned long explicit Long (unsigned long v) : Object(PyLong_FromUnsignedLong(v), true) { validate(); } // try to create from any object Long (const Object& ob) : Object(PyNumber_Long(*ob), true) { validate(); } // Assignment acquires new ownership of pointer Long& operator= (const Object& rhs) { return (*this = *rhs); } Long& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (PyNumber_Long(rhsp), true); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && Py::_Long_Check (pyob); } // convert to long operator long() const { return PyLong_AsLong (ptr()); } operator double() const { return PyLong_AsDouble (ptr()); } operator unsigned long() const { return PyLong_AsUnsignedLong (ptr()); } // assign from an int Long& operator= (int v) { set(PyLong_FromLong (long(v)), true); return *this; } // assign from long Long& operator= (long v) { set(PyLong_FromLong (v), true); return *this; } // assign from unsigned long Long& operator= (unsigned long v) { set(PyLong_FromUnsignedLong (v), true); return *this; } }; // =============================================== // class Float // class Float: public Object { public: // Constructor explicit Float (PyObject *pyob, bool owned = false): Object(pyob, owned) { validate(); } Float (const Float& f): Object(f) { validate(); } // make from double explicit Float (double v=0.0) : Object(PyFloat_FromDouble (v), true) { validate(); } // try to make from any object Float (const Object& ob) : Object(PyNumber_Float(*ob), true) { validate(); } Float& operator= (const Object& rhs) { return (*this = *rhs); } Float& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (PyNumber_Float(rhsp), true); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && Py::_Float_Check (pyob); } // convert to double operator double () const { return PyFloat_AsDouble (ptr()); } // assign from a double Float& operator= (double v) { set(PyFloat_FromDouble (v), true); return *this; } // assign from an int Float& operator= (int v) { set(PyFloat_FromDouble (double(v)), true); return *this; } // assign from long Float& operator= (long v) { set(PyFloat_FromDouble (double(v)), true); return *this; } // assign from an Int Float& operator= (const Int& iob) { set(PyFloat_FromDouble (double(long(iob))), true); return *this; } }; // =============================================== // class Complex class Complex: public Object { public: // Constructor explicit Complex (PyObject *pyob, bool owned = false): Object(pyob, owned) { validate(); } Complex (const Complex& f): Object(f) { validate(); } // make from double explicit Complex (double v=0.0, double w=0.0) :Object(PyComplex_FromDoubles (v, w), true) { validate(); } Complex& operator= (const Object& rhs) { return (*this = *rhs); } Complex& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && Py::_Complex_Check (pyob); } // convert to Py_complex operator Py_complex () const { return PyComplex_AsCComplex (ptr()); } // assign from a Py_complex Complex& operator= (const Py_complex& v) { set(PyComplex_FromCComplex (v), true); return *this; } // assign from a double Complex& operator= (double v) { set(PyComplex_FromDoubles (v, 0.0), true); return *this; } // assign from an int Complex& operator= (int v) { set(PyComplex_FromDoubles (double(v), 0.0), true); return *this; } // assign from long Complex& operator= (long v) { set(PyComplex_FromDoubles (double(v), 0.0), true); return *this; } // assign from an Int Complex& operator= (const Int& iob) { set(PyComplex_FromDoubles (double(long(iob)), 0.0), true); return *this; } double real() const { return PyComplex_RealAsDouble(ptr()); } double imag() const { return PyComplex_ImagAsDouble(ptr()); } }; // Sequences // Sequences are here represented as sequences of items of type T. // The base class SeqBase represents that. // In basic Python T is always "Object". // seqref is what you get if you get elements from a non-const SeqBase. // Note: seqref could probably be a nested class in SeqBase but that might stress // some compilers needlessly. Simlarly for mapref later. // While this class is not intended for enduser use, it needs some public // constructors for the benefit of the STL. // See Scott Meyer's More Essential C++ for a description of proxies. // This application is even more complicated. We are doing an unusual thing // in having a double proxy. If we want the STL to work // properly we have to compromise by storing the rvalue inside. The // entire Object API is repeated so that things like s[i].isList() will // work properly. // Still, once in a while a weird compiler message may occur using expressions like x[i] // Changing them to Object(x[i]) helps the compiler to understand that the // conversion of a seqref to an Object is wanted. template class seqref { protected: SeqBase& s; // the sequence int offset; // item number T the_item; // lvalue public: seqref (SeqBase& seq, sequence_index_type j) : s(seq), offset(j), the_item (s.getItem(j)) {} seqref (const seqref& range) : s(range.s), offset(range.offset), the_item(range.the_item) {} // TMM: added this seqref ctor for use with STL algorithms seqref (Object& obj) : s(dynamic_cast< SeqBase&>(obj)) , offset( NULL ) , the_item(s.getItem(offset)) {} ~seqref() {} operator T() const { // rvalue return the_item; } seqref& operator=(const seqref& rhs) { //used as lvalue the_item = rhs.the_item; s.setItem(offset, the_item); return *this; } seqref& operator=(const T& ob) { // used as lvalue the_item = ob; s.setItem(offset, ob); return *this; } // forward everything else to the item PyObject* ptr () const { return the_item.ptr(); } int reference_count () const { // the reference count return the_item.reference_count(); } Type type () const { return the_item.type(); } String str () const; String repr () const; bool hasAttr (const std::string& attr_name) const { return the_item.hasAttr(attr_name); } Object getAttr (const std::string& attr_name) const { return the_item.getAttr(attr_name); } Object getItem (const Object& key) const { return the_item.getItem(key); } long hashValue () const { return the_item.hashValue(); } bool isCallable () const { return the_item.isCallable(); } bool isInstance () const { return the_item.isInstance(); } bool isDict () const { return the_item.isDict(); } bool isList () const { return the_item.isList(); } bool isMapping () const { return the_item.isMapping(); } bool isNumeric () const { return the_item.isNumeric(); } bool isSequence () const { return the_item.isSequence(); } bool isTrue () const { return the_item.isTrue(); } bool isType (const Type& t) const { return the_item.isType (t); } bool isTuple() const { return the_item.isTuple(); } bool isString() const { return the_item.isString(); } // Commands void setAttr (const std::string& attr_name, const Object& value) { the_item.setAttr(attr_name, value); } void delAttr (const std::string& attr_name) { the_item.delAttr(attr_name); } void delItem (const Object& key) { the_item.delItem(key); } bool operator==(const Object& o2) const { return the_item == o2; } bool operator!=(const Object& o2) const { return the_item != o2; } bool operator>=(const Object& o2) const { return the_item >= o2; } bool operator<=(const Object& o2) const { return the_item <= o2; } bool operator<(const Object& o2) const { return the_item < o2; } bool operator>(const Object& o2) const { return the_item > o2; } }; // end of seqref // class SeqBase // ...the base class for all sequence types template class SeqBase: public Object { public: // STL definitions typedef size_t size_type; typedef seqref reference; typedef T const_reference; typedef seqref* pointer; typedef int difference_type; typedef T value_type; // TMM: 26Jun'01 virtual size_type max_size() const { return std::string::npos; // ? } virtual size_type capacity() const { return size(); } virtual void swap(SeqBase& c) { SeqBase temp = c; c = ptr(); set(temp.ptr()); } virtual size_type size () const { return PySequence_Length (ptr()); } explicit SeqBase () :Object(PyTuple_New(0), true) { validate(); } explicit SeqBase (PyObject* pyob, bool owned=false) : Object(pyob, owned) { validate(); } SeqBase (const Object& ob): Object(ob) { validate(); } // Assignment acquires new ownership of pointer SeqBase& operator= (const Object& rhs) { return (*this = *rhs); } SeqBase& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (rhsp); return *this; } virtual bool accepts (PyObject *pyob) const { return pyob && PySequence_Check (pyob); } size_type length () const { return PySequence_Length (ptr()); } // Element access const T operator[](sequence_index_type index) const { return getItem(index); } seqref operator[](sequence_index_type index) { return seqref(*this, index); } virtual T getItem (sequence_index_type i) const { return T(asObject(PySequence_GetItem (ptr(), i))); } virtual void setItem (sequence_index_type i, const T& ob) { if (PySequence_SetItem (ptr(), i, *ob) == -1) { throw Exception(); } } SeqBase repeat (int count) const { return SeqBase (PySequence_Repeat (ptr(), count), true); } SeqBase concat (const SeqBase& other) const { return SeqBase (PySequence_Concat(ptr(), *other), true); } // more STL compatability const T front () const { return getItem(0); } seqref front() { return seqref(this, 0); } const T back () const { return getItem(size()-1); } seqref back() { return seqref(this, size()-1); } void verify_length(size_type required_size) const { if (size() != required_size) throw IndexError ("Unexpected SeqBase length."); } void verify_length(size_type min_size, size_type max_size) const { size_type n = size(); if (n < min_size || n > max_size) throw IndexError ("Unexpected SeqBase length."); } class iterator : public random_access_iterator_parent(seqref) { protected: friend class SeqBase; SeqBase* seq; int count; public: ~iterator () {} iterator () : seq( 0 ) , count( 0 ) {} iterator (SeqBase* s, int where) : seq( s ) , count( where ) {} iterator (const iterator& other) : seq( other.seq ) , count( other.count ) {} bool eql (const iterator& other) const { return (*seq == *other.seq) && (count == other.count); } bool neq (const iterator& other) const { return (*seq != *other.seq) || (count != other.count); } bool lss (const iterator& other) const { return (count < other.count); } bool gtr (const iterator& other) const { return (count > other.count); } bool leq (const iterator& other) const { return (count <= other.count); } bool geq (const iterator& other) const { return (count >= other.count); } seqref operator*() { return seqref(*seq, count); } seqref operator[] (sequence_index_type i) { return seqref(*seq, count + i); } iterator& operator=(const iterator& other) { if (this == &other) return *this; seq = other.seq; count = other.count; return *this; } iterator operator+(int n) const { return iterator(seq, count + n); } iterator operator-(int n) const { return iterator(seq, count - n); } iterator& operator+=(int n) { count = count + n; return *this; } iterator& operator-=(int n) { count = count - n; return *this; } int operator-(const iterator& other) const { if (*seq != *other.seq) throw RuntimeError ("SeqBase::iterator comparison error"); return count - other.count; } // prefix ++ iterator& operator++ () { count++; return *this;} // postfix ++ iterator operator++ (int) { return iterator(seq, count++);} // prefix -- iterator& operator-- () { count--; return *this;} // postfix -- iterator operator-- (int) { return iterator(seq, count--);} std::string diagnose() const { std::ostringstream oss; oss << "iterator diagnosis " << seq << ", " << count << std::ends; return std::string(oss.str()); } }; // end of class SeqBase::iterator iterator begin () { return iterator(this, 0); } iterator end () { return iterator(this, length()); } class const_iterator : public random_access_iterator_parent(const Object) { protected: friend class SeqBase; const SeqBase* seq; sequence_index_type count; public: ~const_iterator () {} const_iterator () : seq( 0 ) , count( 0 ) {} const_iterator (const SeqBase* s, int where) : seq( s ) , count( where ) {} const_iterator(const const_iterator& other) : seq( other.seq ) , count( other.count ) {} const T operator*() const { return seq->getItem(count); } const T operator[] (sequence_index_type i) const { return seq->getItem(count + i); } const_iterator& operator=(const const_iterator& other) { if (this == &other) return *this; seq = other.seq; count = other.count; return *this; } const_iterator operator+(int n) const { return const_iterator(seq, count + n); } bool eql (const const_iterator& other) const { return (*seq == *other.seq) && (count == other.count); } bool neq (const const_iterator& other) const { return (*seq != *other.seq) || (count != other.count); } bool lss (const const_iterator& other) const { return (count < other.count); } bool gtr (const const_iterator& other) const { return (count > other.count); } bool leq (const const_iterator& other) const { return (count <= other.count); } bool geq (const const_iterator& other) const { return (count >= other.count); } const_iterator operator-(int n) { return const_iterator(seq, count - n); } const_iterator& operator+=(int n) { count = count + n; return *this; } const_iterator& operator-=(int n) { count = count - n; return *this; } int operator-(const const_iterator& other) const { if (*seq != *other.seq) throw RuntimeError ("SeqBase::const_iterator::- error"); return count - other.count; } // prefix ++ const_iterator& operator++ () { count++; return *this;} // postfix ++ const_iterator operator++ (int) { return const_iterator(seq, count++);} // prefix -- const_iterator& operator-- () { count--; return *this;} // postfix -- const_iterator operator-- (int) { return const_iterator(seq, count--);} }; // end of class SeqBase::const_iterator const_iterator begin () const { return const_iterator(this, 0); } const_iterator end () const { return const_iterator(this, length()); } }; // Here's an important typedef you might miss if reading too fast... typedef SeqBase Sequence; template bool operator==(const typename SeqBase::iterator& left, const typename SeqBase::iterator& right); template bool operator!=(const typename SeqBase::iterator& left, const typename SeqBase::iterator& right); template bool operator< (const typename SeqBase::iterator& left, const typename SeqBase::iterator& right); template bool operator> (const typename SeqBase::iterator& left, const typename SeqBase::iterator& right); template bool operator<=(const typename SeqBase::iterator& left, const typename SeqBase::iterator& right); template bool operator>=(const typename SeqBase::iterator& left, const typename SeqBase::iterator& right); template bool operator==(const typename SeqBase::const_iterator& left, const typename SeqBase::const_iterator& right); template bool operator!=(const typename SeqBase::const_iterator& left, const typename SeqBase::const_iterator& right); template bool operator< (const typename SeqBase::const_iterator& left, const typename SeqBase::const_iterator& right); template bool operator> (const typename SeqBase::const_iterator& left, const typename SeqBase::const_iterator& right); template bool operator<=(const typename SeqBase::const_iterator& left, const typename SeqBase::const_iterator& right); template bool operator>=(const typename SeqBase::const_iterator& left, const typename SeqBase::const_iterator& right); extern bool operator==(const Sequence::iterator& left, const Sequence::iterator& right); extern bool operator!=(const Sequence::iterator& left, const Sequence::iterator& right); extern bool operator< (const Sequence::iterator& left, const Sequence::iterator& right); extern bool operator> (const Sequence::iterator& left, const Sequence::iterator& right); extern bool operator<=(const Sequence::iterator& left, const Sequence::iterator& right); extern bool operator>=(const Sequence::iterator& left, const Sequence::iterator& right); extern bool operator==(const Sequence::const_iterator& left, const Sequence::const_iterator& right); extern bool operator!=(const Sequence::const_iterator& left, const Sequence::const_iterator& right); extern bool operator< (const Sequence::const_iterator& left, const Sequence::const_iterator& right); extern bool operator> (const Sequence::const_iterator& left, const Sequence::const_iterator& right); extern bool operator<=(const Sequence::const_iterator& left, const Sequence::const_iterator& right); extern bool operator>=(const Sequence::const_iterator& left, const Sequence::const_iterator& right); // ================================================== // class Char // Python strings return strings as individual elements. // I'll try having a class Char which is a String of length 1 // typedef std::basic_string unicodestring; extern Py_UNICODE unicode_null_string[1]; class Char: public Object { public: explicit Char (PyObject *pyob, bool owned = false): Object(pyob, owned) { validate(); } Char (const Object& ob): Object(ob) { validate(); } Char (const std::string& v = "") :Object(PyString_FromStringAndSize (const_cast(v.c_str()),1), true) { validate(); } Char (char v) : Object(PyString_FromStringAndSize (&v, 1), true) { validate(); } Char (Py_UNICODE v) : Object(PyUnicode_FromUnicode (&v, 1), true) { validate(); } // Assignment acquires new ownership of pointer Char& operator= (const Object& rhs) { return (*this = *rhs); } Char& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && (Py::_String_Check(pyob) || Py::_Unicode_Check(pyob)) && PySequence_Length (pyob) == 1; } // Assignment from C string Char& operator= (const std::string& v) { set(PyString_FromStringAndSize (const_cast(v.c_str()),1), true); return *this; } Char& operator= (char v) { set(PyString_FromStringAndSize (&v, 1), true); return *this; } Char& operator= (const unicodestring& v) { set(PyUnicode_FromUnicode (const_cast(v.data()),1), true); return *this; } Char& operator= (Py_UNICODE v) { set(PyUnicode_FromUnicode (&v, 1), true); return *this; } // Conversion operator String() const; operator std::string () const { return std::string(PyString_AsString (ptr())); } }; class String: public SeqBase { public: virtual size_type capacity() const { return max_size(); } explicit String (PyObject *pyob, bool owned = false): SeqBase(pyob, owned) { validate(); } String (const Object& ob): SeqBase(ob) { validate(); } String() : SeqBase( PyString_FromStringAndSize( "", 0 ), true ) { validate(); } String( const std::string& v ) : SeqBase( PyString_FromStringAndSize( const_cast(v.data()), static_cast( v.length() ) ), true ) { validate(); } String( const char *s, const char *encoding, const char *error="strict" ) : SeqBase( PyUnicode_Decode( s, strlen( s ), encoding, error ), true ) { validate(); } String( const char *s, int len, const char *encoding, const char *error="strict" ) : SeqBase( PyUnicode_Decode( s, len, encoding, error ), true ) { validate(); } String( const std::string &s, const char *encoding, const char *error="strict" ) : SeqBase( PyUnicode_Decode( s.c_str(), s.length(), encoding, error ), true ) { validate(); } String( const std::string& v, std::string::size_type vsize ) : SeqBase(PyString_FromStringAndSize( const_cast(v.data()), static_cast( vsize ) ), true) { validate(); } String( const char *v, int vsize ) : SeqBase(PyString_FromStringAndSize( const_cast(v), vsize ), true ) { validate(); } String( const char* v ) : SeqBase( PyString_FromString( v ), true ) { validate(); } // Assignment acquires new ownership of pointer String& operator= ( const Object& rhs ) { return *this = *rhs; } String& operator= (PyObject* rhsp) { if( ptr() == rhsp ) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && (Py::_String_Check(pyob) || Py::_Unicode_Check(pyob)); } // Assignment from C string String& operator= (const std::string& v) { set( PyString_FromStringAndSize( const_cast( v.data() ), static_cast( v.length() ) ), true ); return *this; } String& operator= (const unicodestring& v) { set( PyUnicode_FromUnicode( const_cast( v.data() ), static_cast( v.length() ) ), true ); return *this; } // Encode String encode( const char *encoding, const char *error="strict" ) { if( isUnicode() ) { return String( PyUnicode_AsEncodedString( ptr(), encoding, error ) ); } else { return String( PyString_AsEncodedObject( ptr(), encoding, error ) ); } } String decode( const char *encoding, const char *error="strict" ) { return Object( PyString_AsDecodedObject( ptr(), encoding, error ) ); } // Queries virtual size_type size () const { if( isUnicode() ) { return static_cast( PyUnicode_GET_SIZE (ptr()) ); } else { return static_cast( PyString_Size (ptr()) ); } } operator std::string () const { return as_std_string(); } std::string as_std_string() const { if( isUnicode() ) { throw TypeError("cannot return std::string from Unicode object"); } else { return std::string( PyString_AsString( ptr() ), static_cast( PyString_Size( ptr() ) ) ); } } unicodestring as_unicodestring() const { if( isUnicode() ) { return unicodestring( PyUnicode_AS_UNICODE( ptr() ), static_cast( PyUnicode_GET_SIZE( ptr() ) ) ); } else { throw TypeError("can only return unicodestring from Unicode object"); } } }; // ================================================== // class Tuple class Tuple: public Sequence { public: virtual void setItem (sequence_index_type offset, const Object&ob) { // note PyTuple_SetItem is a thief... if(PyTuple_SetItem (ptr(), offset, new_reference_to(ob)) == -1) { throw Exception(); } } // Constructor explicit Tuple (PyObject *pyob, bool owned = false): Sequence (pyob, owned) { validate(); } Tuple (const Object& ob): Sequence(ob) { validate(); } // New tuple of a given size explicit Tuple (int size = 0) { set(PyTuple_New (size), true); validate (); for (sequence_index_type i=0; i < size; i++) { if(PyTuple_SetItem (ptr(), i, new_reference_to(Py::_None())) == -1) { throw Exception(); } } } // Tuple from any sequence explicit Tuple (const Sequence& s) { sequence_index_type limit( sequence_index_type( s.length() ) ); set(PyTuple_New (limit), true); validate(); for(sequence_index_type i=0; i < limit; i++) { if(PyTuple_SetItem (ptr(), i, new_reference_to(s[i])) == -1) { throw Exception(); } } } // Assignment acquires new ownership of pointer Tuple& operator= (const Object& rhs) { return (*this = *rhs); } Tuple& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && Py::_Tuple_Check (pyob); } Tuple getSlice (int i, int j) const { return Tuple (PySequence_GetSlice (ptr(), i, j), true); } }; // ================================================== // class List class List: public Sequence { public: // Constructor explicit List (PyObject *pyob, bool owned = false): Sequence(pyob, owned) { validate(); } List (const Object& ob): Sequence(ob) { validate(); } // Creation at a fixed size List (int size = 0) { set(PyList_New (size), true); validate(); for (sequence_index_type i=0; i < size; i++) { if(PyList_SetItem (ptr(), i, new_reference_to(Py::_None())) == -1) { throw Exception(); } } } // List from a sequence List (const Sequence& s): Sequence() { int n = s.length(); set(PyList_New (n), true); validate(); for (sequence_index_type i=0; i < n; i++) { if(PyList_SetItem (ptr(), i, new_reference_to(s[i])) == -1) { throw Exception(); } } } virtual size_type capacity() const { return max_size(); } // Assignment acquires new ownership of pointer List& operator= (const Object& rhs) { return (*this = *rhs); } List& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && Py::_List_Check (pyob); } List getSlice (int i, int j) const { return List (PyList_GetSlice (ptr(), i, j), true); } void setSlice (int i, int j, const Object& v) { if(PyList_SetSlice (ptr(), i, j, *v) == -1) { throw Exception(); } } void append (const Object& ob) { if(PyList_Append (ptr(), *ob) == -1) { throw Exception(); } } void insert (int i, const Object& ob) { if(PyList_Insert (ptr(), i, *ob) == -1) { throw Exception(); } } void sort () { if(PyList_Sort(ptr()) == -1) { throw Exception(); } } void reverse () { if(PyList_Reverse(ptr()) == -1) { throw Exception(); } } }; // Mappings // ================================================== template class mapref { protected: MapBase& s; // the map Object key; // item key T the_item; public: mapref (MapBase& map, const std::string& k) : s(map), the_item() { key = String(k); if(map.hasKey(key)) the_item = map.getItem(key); }; mapref (MapBase& map, const Object& k) : s(map), key(k), the_item() { if(map.hasKey(key)) the_item = map.getItem(key); }; ~mapref() {} // MapBase stuff // lvalue mapref& operator=(const mapref& other) { if(this == &other) return *this; the_item = other.the_item; s.setItem(key, other.the_item); return *this; }; mapref& operator= (const T& ob) { the_item = ob; s.setItem (key, ob); return *this; } // rvalue operator T() const { return the_item; } // forward everything else to the_item PyObject* ptr () const { return the_item.ptr(); } int reference_count () const { // the mapref count return the_item.reference_count(); } Type type () const { return the_item.type(); } String str () const { return the_item.str(); } String repr () const { return the_item.repr(); } bool hasAttr (const std::string& attr_name) const { return the_item.hasAttr(attr_name); } Object getAttr (const std::string& attr_name) const { return the_item.getAttr(attr_name); } Object getItem (const Object& k) const { return the_item.getItem(k); } long hashValue () const { return the_item.hashValue(); } bool isCallable () const { return the_item.isCallable(); } bool isList () const { return the_item.isList(); } bool isMapping () const { return the_item.isMapping(); } bool isNumeric () const { return the_item.isNumeric(); } bool isSequence () const { return the_item.isSequence(); } bool isTrue () const { return the_item.isTrue(); } bool isType (const Type& t) const { return the_item.isType (t); } bool isTuple() const { return the_item.isTuple(); } bool isString() const { return the_item.isString(); } // Commands void setAttr (const std::string& attr_name, const Object& value) { the_item.setAttr(attr_name, value); } void delAttr (const std::string& attr_name) { the_item.delAttr(attr_name); } void delItem (const Object& k) { the_item.delItem(k); } }; // end of mapref // TMM: now for mapref template< class T > bool operator==(const mapref& left, const mapref& right) { return true; // NOT completed. } template< class T > bool operator!=(const mapref& left, const mapref& right) { return true; // not completed. } template class MapBase: public Object { protected: explicit MapBase() {} public: // reference: proxy class for implementing [] // TMM: 26Jun'01 - the types // If you assume that Python mapping is a hash_map... // hash_map::value_type is not assignable, but // (*it).second = data must be a valid expression typedef size_t size_type; typedef Object key_type; typedef mapref data_type; typedef std::pair< const T, T > value_type; typedef std::pair< const T, mapref > reference; typedef const std::pair< const T, const T > const_reference; typedef std::pair< const T, mapref > pointer; // Constructor explicit MapBase (PyObject *pyob, bool owned = false): Object(pyob, owned) { validate(); } // TMM: 02Jul'01 - changed MapBase to Object in next line MapBase (const Object& ob): Object(ob) { validate(); } // Assignment acquires new ownership of pointer MapBase& operator= (const Object& rhs) { return (*this = *rhs); } MapBase& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyMapping_Check(pyob); } // Clear -- PyMapping Clear is missing // void clear () { List k = keys(); for(List::iterator i = k.begin(); i != k.end(); i++) { delItem(*i); } } virtual size_type size() const { return PyMapping_Length (ptr()); } // Element Access T operator[](const std::string& key) const { return getItem(key); } T operator[](const Object& key) const { return getItem(key); } mapref operator[](const std::string& key) { return mapref(*this, key); } mapref operator[](const Object& key) { return mapref(*this, key); } int length () const { return PyMapping_Length (ptr()); } bool hasKey (const std::string& s) const { return PyMapping_HasKeyString (ptr(),const_cast(s.c_str())) != 0; } bool hasKey (const Object& s) const { return PyMapping_HasKey (ptr(), s.ptr()) != 0; } T getItem (const std::string& s) const { return T( asObject(PyMapping_GetItemString (ptr(),const_cast(s.c_str()))) ); } T getItem (const Object& s) const { return T( asObject(PyObject_GetItem (ptr(), s.ptr())) ); } virtual void setItem (const char *s, const Object& ob) { if (PyMapping_SetItemString (ptr(), const_cast(s), *ob) == -1) { throw Exception(); } } virtual void setItem (const std::string& s, const Object& ob) { if (PyMapping_SetItemString (ptr(), const_cast(s.c_str()), *ob) == -1) { throw Exception(); } } virtual void setItem (const Object& s, const Object& ob) { if (PyObject_SetItem (ptr(), s.ptr(), ob.ptr()) == -1) { throw Exception(); } } void delItem (const std::string& s) { if (PyMapping_DelItemString (ptr(), const_cast(s.c_str())) == -1) { throw Exception(); } } void delItem (const Object& s) { if (PyMapping_DelItem (ptr(), *s) == -1) { throw Exception(); } } // Queries List keys () const { return List(PyMapping_Keys(ptr()), true); } List values () const { // each returned item is a (key, value) pair return List(PyMapping_Values(ptr()), true); } List items () const { return List(PyMapping_Items(ptr()), true); } // iterators for MapBase // Added by TMM: 2Jul'01 - NOT COMPLETED // There is still a bug. I decided to stop, before fixing the bug, because // this can't be halfway efficient until Python gets built-in iterators. // My current soln is to iterate over the map by getting a copy of its keys // and iterating over that. Not a good solution. // The iterator holds a MapBase* rather than a MapBase because that's // how the sequence iterator is implemented and it works. But it does seem // odd to me - we are iterating over the map object, not the reference. #if 0 // here is the test code with which I found the (still existing) bug typedef cxx::Dict d_t; d_t d; cxx::String s1("blah"); cxx::String s2("gorf"); d[ "one" ] = s1; d[ "two" ] = s1; d[ "three" ] = s2; d[ "four" ] = s2; d_t::iterator it; it = d.begin(); // this (using the assignment operator) is causing // a problem; if I just use the copy ctor it works fine. for( ; it != d.end(); ++it ) { d_t::value_type vt( *it ); cxx::String rs = vt.second.repr(); std::string ls = rs.operator std::string(); fprintf( stderr, "%s\n", ls ); } #endif // 0 class iterator { // : public forward_iterator_parent( std::pair ) { protected: typedef std::forward_iterator_tag iterator_category; typedef std::pair< const T, T > value_type; typedef int difference_type; typedef std::pair< const T, mapref > pointer; typedef std::pair< const T, mapref > reference; friend class MapBase; // MapBase* map; List keys; // for iterating over the map List::iterator pos; // index into the keys public: ~iterator () {} iterator () : map( 0 ) , keys() , pos() {} iterator (MapBase* m, bool end = false ) : map( m ) , keys( m->keys() ) , pos( end ? keys.end() : keys.begin() ) {} iterator (const iterator& other) : map( other.map ) , keys( other.keys ) , pos( other.pos ) {} reference operator*() { Object key = *pos; return std::make_pair(key, mapref(*map,key)); } iterator& operator=(const iterator& other) { if (this == &other) return *this; map = other.map; keys = other.keys; pos = other.pos; return *this; } bool eql(const iterator& right) const { return *map == *right.map && pos == right.pos; } bool neq( const iterator& right ) const { return *map != *right.map || pos != right.pos; } // pointer operator->() { // return ; // } // prefix ++ iterator& operator++ () { pos++; return *this;} // postfix ++ iterator operator++ (int) { return iterator(map, keys, pos++);} // prefix -- iterator& operator-- () { pos--; return *this;} // postfix -- iterator operator-- (int) { return iterator(map, keys, pos--);} std::string diagnose() const { std::ostringstream oss; oss << "iterator diagnosis " << map << ", " << pos << std::ends; return std::string(oss.str()); } }; // end of class MapBase::iterator iterator begin () { return iterator(this); } iterator end () { return iterator(this, true); } class const_iterator { protected: typedef std::forward_iterator_tag iterator_category; typedef const std::pair< const T, T > value_type; typedef int difference_type; typedef const std::pair< const T, T > pointer; typedef const std::pair< const T, T > reference; friend class MapBase; const MapBase* map; List keys; // for iterating over the map List::iterator pos; // index into the keys public: ~const_iterator () {} const_iterator () : map( 0 ) , keys() , pos() {} const_iterator (const MapBase* m, List k, List::iterator p ) : map( m ) , keys( k ) , pos( p ) {} const_iterator(const const_iterator& other) : map( other.map ) , keys( other.keys ) , pos( other.pos ) {} bool eql(const const_iterator& right) const { return *map == *right.map && pos == right.pos; } bool neq( const const_iterator& right ) const { return *map != *right.map || pos != right.pos; } // const_reference operator*() { // Object key = *pos; // return std::make_pair( key, map->[key] ); // GCC < 3 barfes on this line at the '['. // } const_iterator& operator=(const const_iterator& other) { if (this == &other) return *this; map = other.map; keys = other.keys; pos = other.pos; return *this; } // prefix ++ const_iterator& operator++ () { pos++; return *this;} // postfix ++ const_iterator operator++ (int) { return const_iterator(map, keys, pos++);} // prefix -- const_iterator& operator-- () { pos--; return *this;} // postfix -- const_iterator operator-- (int) { return const_iterator(map, keys, pos--);} }; // end of class MapBase::const_iterator const_iterator begin () const { return const_iterator(this, 0); } const_iterator end () const { return const_iterator(this, length()); } }; // end of MapBase typedef MapBase Mapping; template bool operator==(const typename MapBase::iterator& left, const typename MapBase::iterator& right); template bool operator!=(const typename MapBase::iterator& left, const typename MapBase::iterator& right); template bool operator==(const typename MapBase::const_iterator& left, const typename MapBase::const_iterator& right); template bool operator!=(const typename MapBase::const_iterator& left, const typename MapBase::const_iterator& right); extern bool operator==(const Mapping::iterator& left, const Mapping::iterator& right); extern bool operator!=(const Mapping::iterator& left, const Mapping::iterator& right); extern bool operator==(const Mapping::const_iterator& left, const Mapping::const_iterator& right); extern bool operator!=(const Mapping::const_iterator& left, const Mapping::const_iterator& right); // ================================================== // class Dict class Dict: public Mapping { public: // Constructor explicit Dict (PyObject *pyob, bool owned=false): Mapping (pyob, owned) { validate(); } Dict (const Dict& ob): Mapping(ob) { validate(); } // Creation Dict () { set(PyDict_New (), true); validate(); } // Assignment acquires new ownership of pointer Dict& operator= (const Object& rhs) { return (*this = *rhs); } Dict& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set(rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && Py::_Dict_Check (pyob); } }; class Callable: public Object { protected: explicit Callable (): Object() {} public: // Constructor explicit Callable (PyObject *pyob, bool owned = false): Object (pyob, owned) { validate(); } Callable (const Object& ob): Object(ob) { validate(); } // Assignment acquires new ownership of pointer Callable& operator= (const Object& rhs) { return (*this = *rhs); } Callable& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyCallable_Check (pyob); } // Call Object apply(const Tuple& args) const { return asObject(PyObject_CallObject(ptr(), args.ptr())); } // Call with keywords Object apply(const Tuple& args, const Dict& kw) const { return asObject( PyEval_CallObjectWithKeywords( ptr(), args.ptr(), kw.ptr() ) ); } Object apply(PyObject* pargs = 0) const { return apply (Tuple(pargs)); } }; class Module: public Object { public: explicit Module (PyObject* pyob, bool owned = false): Object (pyob, owned) { validate(); } // Construct from module name explicit Module (const std::string&s): Object() { PyObject *m = PyImport_AddModule( const_cast(s.c_str()) ); set( m, false ); validate (); } // Copy constructor acquires new ownership of pointer Module (const Module& ob): Object(*ob) { validate(); } Module& operator= (const Object& rhs) { return (*this = *rhs); } Module& operator= (PyObject* rhsp) { if(ptr() == rhsp) return *this; set(rhsp); return *this; } Dict getDict() { return Dict(PyModule_GetDict(ptr())); // Caution -- PyModule_GetDict returns borrowed reference! } }; // Numeric interface inline Object operator+ (const Object& a) { return asObject(PyNumber_Positive(*a)); } inline Object operator- (const Object& a) { return asObject(PyNumber_Negative(*a)); } inline Object abs(const Object& a) { return asObject(PyNumber_Absolute(*a)); } inline std::pair coerce(const Object& a, const Object& b) { PyObject *p1, *p2; p1 = *a; p2 = *b; if(PyNumber_Coerce(&p1,&p2) == -1) { throw Exception(); } return std::pair(asObject(p1), asObject(p2)); } inline Object operator+ (const Object& a, const Object& b) { return asObject(PyNumber_Add(*a, *b)); } inline Object operator+ (const Object& a, int j) { return asObject(PyNumber_Add(*a, *Int(j))); } inline Object operator+ (const Object& a, double v) { return asObject(PyNumber_Add(*a, *Float(v))); } inline Object operator+ (int j, const Object& b) { return asObject(PyNumber_Add(*Int(j), *b)); } inline Object operator+ (double v, const Object& b) { return asObject(PyNumber_Add(*Float(v), *b)); } inline Object operator- (const Object& a, const Object& b) { return asObject(PyNumber_Subtract(*a, *b)); } inline Object operator- (const Object& a, int j) { return asObject(PyNumber_Subtract(*a, *Int(j))); } inline Object operator- (const Object& a, double v) { return asObject(PyNumber_Subtract(*a, *Float(v))); } inline Object operator- (int j, const Object& b) { return asObject(PyNumber_Subtract(*Int(j), *b)); } inline Object operator- (double v, const Object& b) { return asObject(PyNumber_Subtract(*Float(v), *b)); } inline Object operator* (const Object& a, const Object& b) { return asObject(PyNumber_Multiply(*a, *b)); } inline Object operator* (const Object& a, int j) { return asObject(PyNumber_Multiply(*a, *Int(j))); } inline Object operator* (const Object& a, double v) { return asObject(PyNumber_Multiply(*a, *Float(v))); } inline Object operator* (int j, const Object& b) { return asObject(PyNumber_Multiply(*Int(j), *b)); } inline Object operator* (double v, const Object& b) { return asObject(PyNumber_Multiply(*Float(v), *b)); } inline Object operator/ (const Object& a, const Object& b) { return asObject(PyNumber_Divide(*a, *b)); } inline Object operator/ (const Object& a, int j) { return asObject(PyNumber_Divide(*a, *Int(j))); } inline Object operator/ (const Object& a, double v) { return asObject(PyNumber_Divide(*a, *Float(v))); } inline Object operator/ (int j, const Object& b) { return asObject(PyNumber_Divide(*Int(j), *b)); } inline Object operator/ (double v, const Object& b) { return asObject(PyNumber_Divide(*Float(v), *b)); } inline Object operator% (const Object& a, const Object& b) { return asObject(PyNumber_Remainder(*a, *b)); } inline Object operator% (const Object& a, int j) { return asObject(PyNumber_Remainder(*a, *Int(j))); } inline Object operator% (const Object& a, double v) { return asObject(PyNumber_Remainder(*a, *Float(v))); } inline Object operator% (int j, const Object& b) { return asObject(PyNumber_Remainder(*Int(j), *b)); } inline Object operator% (double v, const Object& b) { return asObject(PyNumber_Remainder(*Float(v), *b)); } inline Object type(const Exception&) // return the type of the error { PyObject *ptype, *pvalue, *ptrace; PyErr_Fetch(&ptype, &pvalue, &ptrace); Object result(ptype); PyErr_Restore(ptype, pvalue, ptrace); return result; } inline Object value(const Exception&) // return the value of the error { PyObject *ptype, *pvalue, *ptrace; PyErr_Fetch(&ptype, &pvalue, &ptrace); Object result; if(pvalue) result = pvalue; PyErr_Restore(ptype, pvalue, ptrace); return result; } inline Object trace(const Exception&) // return the traceback of the error { PyObject *ptype, *pvalue, *ptrace; PyErr_Fetch(&ptype, &pvalue, &ptrace); Object result; if(ptrace) result = ptrace; PyErr_Restore(ptype, pvalue, ptrace); return result; } template String seqref::str () const { return the_item.str(); } template String seqref::repr () const { return the_item.repr(); } } // namespace Py #endif // __CXX_Objects__h