This is the second part of a message about my recent work. //----------------------------------*-C++-*--------------------------------- -// // Copyright 1996 The Regents of the University of California. // All rights reserved. //-------------------------------------------------------------------------- -// #ifndef __CXX_Objects__h #define __CXX_Objects__h #include "Python.h" #include "CXX_Exception.h" #include <iostream> #include <string> using namespace std; namespace Py { // Macro for assertion checking #define Require(something) \ {if(!(something)) throw PyException (#something);} // Forward declarations class Object; class Type; class Float; class Int; class Sequence; class String; class Tuple; class List; class Mapping; class Dict; // new_reference_to PyObject* new_reference_to (const Object& g); // return an owned reference to the object g owns. PyObject* new_reference_to (PyObject* p); // return an owned reference to *p // Py_Null is a PyObject* (0) for signalling error returns #define Py_Null (static_cast<PyObject*>(0)) // Nothing is what a non-function returns #define Nothing new_reference_to(Py_None) class FromAPI { // Python API routines return to you owned pointers. // FromAPI is a helper class to let you construct an object from one of these. // Usage: Object(FromAPI(p)) or Object x = FromAPI(p) // where p is an already-owned pointer such as returned by an API routine. private: PyObject *p; FromAPI& operator=(const FromAPI& other); // no assignment FromAPI(const FromAPI& other); // no copy constructor public: explicit FromAPI(PyObject *powned) { // construct from a pointer you own, only! p = powned; } virtual ~FromAPI() { Py_XDECREF(p); // we own it, so kill it } operator PyObject*() {return p;} }; //========================================================================== =// // 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 Sequence or Mapping. // 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 MyType& other): Object(other) { // validate(); // } // You may wish to add other constructors; see the classes below for examples. // Each constructor must 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= (const PyObject* rhsp) { // if(*this == rhsp) return *this; // set(rhsp); // return *this; // } // Object equality means they contain the same PyObject* int operator==(const Object& o1, const Object& o2); int operator==(const Object& o1, const PyObject *p2); int operator==(const PyObject *p1, const Object& o2); int operator!=(const Object& o1, const Object& o2); int operator!=(const Object& o1, const PyObject *p2); int operator!=(const PyObject *p1, const Object& o2); class Object { protected: PyObject* p; // the pointer to the Python object void set (const PyObject* pyob) { release(); p = const_cast<PyObject *>(pyob); Py_XINCREF (p); validate(); } void release () { Require((p==Py_Null) || (reference_count() > 0)); Py_XDECREF (p); p = Py_Null; } void validate() { // release pointer if not the right type if (! accepts (p)) { release (); if(PyErr_Occurred()) { // Error message already set throw PyException(); } string s("Error creating object of type "); s += (typeid (*this)).name(); throw PyException_TypeError (s.c_str()); } } // Constructs an illegal object; child has to fix this fast! Object (): p(Py_Null) {} public: // 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. Not so for the assignment operator. // Constructors acquire new ownership of pointer explicit Object (PyObject* pyob): p (pyob) { Py_XINCREF (p); validate(); } // Copy constructor acquires new ownership of pointer Object (const Object& ob): p(*ob) { Py_XINCREF (p); validate(); } // Assignment acquires new ownership of pointer Object& operator= (const Object& rhs) { return (*this = *rhs); } Object& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } // Destructor virtual ~Object () { release (); } // Loaning the pointer to others, retain ownership PyObject* operator* () const return p; } PyObject* pointer () const { return p; } // // Queries // // Can pyob be used in this object's constructor? virtual bool accepts (PyObject *pyob) const { return (pyob != Py_Null); } 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 String repr () const; // the repr () representation int hasAttr (const char *s) const { return PyObject_HasAttrString (p, const_cast<char*>(s)); } Object getAttr (const char *s) const { return Object(FromAPI( PyObject_GetAttrString (p, const_cast<char*> (s)) )); } Object getItem (const Object& key) const { return Object(FromAPI(PyObject_GetItem(p, *key))); } long hashValue () const { return PyObject_Hash (p); } // int print (FILE* fp, int flags=Py_Print_RAW) { // return PyObject_Print (p, fp, flags); // } int isCallable () const { return PyCallable_Check (p); } int isMapping () const { return PyMapping_Check (p); } int isNumeric () const { return PyNumber_Check (p); } int isSequence () const { return PySequence_Check (p); } int isTrue () const { return PyObject_IsTrue (p); } int isType (const Type& t) const; int isTuple() const { return PyTuple_Check(p); } // Commands void setAttr (const char* attr_name, const Object& value) { if(PyObject_SetAttrString (p, const_cast<char*>(attr_name), *value) == -1) throw PyException_AttributeError ("getAttr failed."); } void delAttr (char *s) { if(PyObject_DelAttrString (p, s) == -1) throw PyException_AttributeError ("delAttr failed."); } void setItem (const Object& key, const Object& value) { if(PyObject_SetItem(p, *key, *value) == -1) throw PyException_KeyError("setItem failed."); } void delItem (const Object& key) { if(PyObject_DelItem(p, *key) == -1) throw PyException_KeyError("delItem failed."); } }; // End of class Object ostream& operator<< (ostream& os, const Object& ob); // Class Type class Type: public Object { public: explicit Type (PyObject* pyob): Object(pyob) { validate(); } explicit Type (const Object& ob): Object(*ob) { validate(); } Type(const Type& t): Object(t) { validate(); } Type& operator= (const Object& rhs) { return (*this = *rhs); } Type& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } virtual bool accepts (PyObject *pyob) const { return pyob && PyType_Check (pyob); } }; // =============================================== // class Int class Int: public Object { public: // Constructor explicit Int (PyObject *pyob): Object (pyob) { validate(); } Int (const Int& ob): Object(*ob) { validate(); } // create from long explicit Int (long v = 0L): Object(FromAPI(PyInt_FromLong(v))) { validate(); } // create from int explicit Int (int v) { long w = v; p = PyInt_FromLong(w); validate(); } explicit Int (const Object& ob): Object() { p = PyNumber_Int(*ob); validate(); } // Assignment acquires new ownership of pointer Int& operator= (const Object& rhs) { return (*this = *rhs); } Int& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (FromAPI(PyNumber_Int(const_cast<PyObject*>(rhsp)))); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyInt_Check (pyob); } // convert to long operator long() const { return PyInt_AsLong (p); } // assign from an int Int& operator= (int v) { *this = FromAPI(PyInt_FromLong (long(v))); return *this; } // assign from long Int& operator= (long v) { *this = FromAPI(PyInt_FromLong (v)); return *this; } }; // =============================================== // class Long class Long: public Object { public: // Constructor explicit Long (PyObject *pyob): Object (pyob) { validate(); } Long (const Long& ob): Object(*ob) { validate(); } // create from long explicit Long (long v = 0L): Object(FromAPI(PyLong_FromLong(v))) { validate(); } // create from int explicit Long (int v) { long w = v; p = PyLong_FromLong(w); validate(); } // try to create from any object explicit Long (const Object& ob): Object() { p = PyNumber_Long(*ob); validate(); } // Assignment acquires new ownership of pointer Long& operator= (const Object& rhs) { return (*this = *rhs); } Long& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (FromAPI(PyNumber_Long(const_cast<PyObject*>(rhsp)))); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyLong_Check (pyob); } // convert to long operator long() const { return PyLong_AsLong (p); } operator double() const { return PyLong_AsDouble (p); } // assign from an int Long& operator= (int v) { *this = FromAPI(PyLong_FromLong (long(v))); return *this; } // assign from long Long& operator= (long v) { *this = FromAPI(PyLong_FromLong (v)); return *this; } }; // =============================================== // class Float // No constructor from a double since it is bad to overload on a pointer // and a numerical type. class Float: public Object { public: // Constructor explicit Float (PyObject *pyob): Object(pyob) { validate(); } Float (const Float& f): Object(f) { validate(); } // make from double explicit Float (double v=0.0) { p = PyFloat_FromDouble (v); validate(); } // try to make from any object explicit Float (const Object& ob): Object() { p = PyNumber_Float(*ob); validate(); } Float& operator= (const Object& rhs) { return (*this = *rhs); } Float& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (FromAPI(PyNumber_Float(const_cast<PyObject*>(rhsp)))); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyFloat_Check (pyob); } // convert to double operator double () const { return PyFloat_AsDouble (p); } // assign from a double Float& operator= (double v) { *this = FromAPI(PyFloat_FromDouble (v)); return *this; } // assign from an int Float& operator= (int v) { *this = FromAPI(PyFloat_FromDouble (double(v))); return *this; } // assign from long Float& operator= (long v) { *this = FromAPI(PyFloat_FromDouble (double(v))); return *this; } // assign from an Int Float& operator= (const Int& iob) { *this = FromAPI(PyFloat_FromDouble (double(long(iob)))); return *this; } }; // class Sequence // ...the base class for all sequence types class Sequence: public Object { protected: explicit Sequence (): Object() {} public: // Items are the result of the [] and *iterator operations class Item protected: Sequence* s; // the sequence int offset; // item number public: Item (Sequence* seq, int i) : s(seq), offset(i) {}; ~Item() {} operator Object() const { return s->getItem (offset); } Item& operator=(const Item& rhs) { //lvalue s->setItem(offset, Object(rhs)); return *this; } Item& operator=(Object ob){ // lvalue s->setItem(offset, ob); return *this; } }; // end of Item class iterator; explicit Sequence (PyObject* pyob): Object(pyob) { validate(); } Sequence (const Sequence& ob): Object(ob) { validate(); } // Assignment acquires new ownership of pointer Sequence& operator= (const Object& rhs) { return (*this = *rhs); } Sequence& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } virtual bool accepts (PyObject *pyob) const { return pyob && PySequence_Check (pyob); } int length () const { return PySequence_Length (p); } // Element access const Item operator[](int index) const { return Item(const_cast<Sequence*>(this), index); } Item operator[](int index) { return Item(const_cast<Sequence*>(this), index); } virtual Object getItem (int i) const { return Object(FromAPI(PySequence_GetItem (p, i))); } virtual void setItem (int i, const Object& ob) { if (PySequence_SetItem (p, i, *ob) == -1) { throw PyException(); } } Sequence repeat (int count) const { Require (count >= 0); return Sequence (FromAPI (PySequence_Repeat (p, count))); } Sequence concat (const Sequence& other) const { return Sequence (FromAPI (PySequence_Concat(p, *other))); } class iterator { protected: Sequence* seq; int count; iterator (Sequence* s, int where): seq(s), count(where) {} public: ~iterator () {} bool operator!= (const iterator& other) { return (seq != other.seq) || (count != other.count); } Item operator*() { return Item(seq, count);} // prefix ++ iterator& operator++ () { count++; return *this;} // postfix ++ const iterator operator++ (int) { return iterator(seq, count++);} // prefix -- iterator& operator-- () { count--; return *this;} // postfix -- const iterator operator-- (int) { return iterator(seq, count--);} }; // end of class Sequence::iterator iterator begin () const { return iterator(const_cast<Sequence*>(this), 0); } iterator end () const { return iterator(const_cast<Sequence*>(this), length()); } }; // ================================================== // class String class String: public Sequence { public: virtual void setItem (int offset, PyObject* pitem) { throw PyException("Cannot assign to a String element."); } explicit String (PyObject *pyob): Sequence (pyob) { validate(); } String (const String& ob): Sequence(ob) { validate(); } // Create from CString String (const char *v = ""): Sequence(){ p = PyString_FromString (const_cast<char *> (v)); validate(); } String (const char *v, int vsize): Sequence() { p = PyString_FromStringAndSize (const_cast<char *>(v), vsize); validate(); } // Assignment acquires new ownership of pointer String& operator= (const Object& rhs) { return (*this = *rhs); } String& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyString_Check (pyob); } // Convert to C string operator char* () const { return PyString_AsString (p); } // Assignment from C string String& operator= (const char *v) { *this = FromAPI(PyString_FromString (const_cast<char *> (v))); return *this; } // Append to present string void operator+= (const Sequence& s) { *this = concat(s); } void operator+= (const char* cs) { *this = concat(String(cs)); } // Replicate present string void operator*= (int n) { Require(n > 0); *this = repeat(n); } // Queries int size () const { // same as length as far as I know? return PyString_Size (p); } }; // ================================================== // class Tuple class Tuple: public Sequence { public: virtual void setItem (int offset, const Object&ob) { // note PyTuple_SetItem is a thief... if(PyTuple_SetItem (p, offset, new_reference_to(ob)) == -1) { throw PyException(); } } // Constructor explicit Tuple (PyObject *pyob): Sequence (pyob) { validate(); } Tuple (const Tuple& ob): Sequence(ob) { validate(); } // New tuple of a given size explicit Tuple (int size = 0): Sequence() { p = PyTuple_New (size); validate (); for (int i=0; i < size; i++) { if(PyTuple_SetItem (p, i, new_reference_to(Py_None)) == -1) { throw PyException(); } } } // Tuple from any sequence explicit Tuple (const Sequence& s) p = PyTuple_New (s.length()); validate(); for(int i=0; i < s.length(); i++) { if(PyTuple_SetItem (p, i, new_reference_to(s[i])) == -1) { throw PyException(); } } } // Assignment acquires new ownership of pointer Tuple& operator= (const Object& rhs) { return (*this = *rhs); } Tuple& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyTuple_Check (pyob); } Tuple getSlice (int i, int j) const { return Tuple (FromAPI(PySequence_GetSlice (p, i, j))); } }; // ================================================== // class List class List: public Sequence public: // Constructor explicit List (PyObject *pyob): Sequence(pyob) { validate(); } List (const List& ob): Sequence(ob) { validate(); } // Creation at a fixed size List (int size = 0): Sequence() { p = PyList_New (size); validate(); for (int i=0; i < size; i++) { if(PyList_SetItem (p, i, new_reference_to(Py_None)) == -1) { throw PyException(); } } } // List from a sequence List (const Sequence& s): Sequence() { int n = s.length(); p = PyList_New (n); validate(); for (int i=0; i < n; i++) { if(PyList_SetItem (p, i, new_reference_to(s[i])) == -1) { throw PyException(); } } } // Assignment acquires new ownership of pointer List& operator= (const Object& rhs) { return (*this = *rhs); } List& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set (rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyList_Check (pyob); } List getSlice (int i, int j) const { return List (FromAPI(PyList_GetSlice (p, i, j))); } void setSlice (int i, int j, const Object& v) { if(PyList_SetSlice (p, i, j, *v) == -1) { throw PyException(); } } void append (const Object& ob) { if(PyList_Append (p, *ob) == -1) { throw PyException(); } } void insert (int i, const Object& ob) { if(PyList_Insert (p, i, *ob) == -1) { throw PyException(); } } void sort () { if(PyList_Sort(p) == -1) { throw PyException(); } } void reverse () { if(PyList_Reverse(p) == -1) { throw PyException(); } } }; // class Mapping // ================================================== class Mapping: public Object { protected: explicit Mapping(): Object() { } public: // MappingItem: proxy class for implementing [] class MappingItem private: Mapping& s; const char *key; public: MappingItem (Mapping& seq, const char *k) : s(seq), key(k) {}; MappingItem& operator=(const MappingItem& rhs); //lvalue MappingItem& operator=(Object ob); // lvalue operator Object() const; // rvalue }; // end of MappingItem // Constructor explicit Mapping (PyObject *pyob): Object(pyob) { validate(); } Mapping (const Mapping& ob): Object(ob) { validate(); } // Assignment acquires new ownership of pointer Mapping& operator= (const Object& rhs) { return (*this = *rhs); } Mapping& operator= (const PyObject* rhsp) { if(*this == 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); } } // Element Access const MappingItem operator[](const char* key) const; MappingItem operator[](const char* key); int length () const { return PyMapping_Length (p); } int hasKey (const char* s) const { Require(s != 0); return PyMapping_HasKeyString (p, const_cast<char*> (s)); } Object getItem (const char* s) const { Require (s != 0); return Object( FromAPI( PyMapping_GetItemString (p,const_cast<char*> (s)) ) ); } void setItem (const char* s, const Object& ob) { if (PyMapping_SetItemString (p, const_cast<char*> (s), *ob) == -1){ throw PyException(); } } void delItem (const char* s) { Require (s != 0); if (PyMapping_DelItemString (p, const_cast<char*> (s)) == -1){ throw PyException(); } } void delItem (const Object& s) { if (PyMapping_DelItem (p, *s) == -1){ throw PyException(); } } // Queries List keys () const { return List(FromAPI(PyMapping_Keys(p))); } List values () const { // each returned item is a (key, value) pair return List(FromAPI(PyMapping_Values(p))); } List items () const { return List(FromAPI(PyMapping_Items(p))); } }; // ================================================== // class Dict class Dict: public Mapping { public: // Constructor explicit Dict (PyObject *pyob): Mapping (pyob) { validate(); } Dict (const Dict& ob): Mapping(ob) { validate(); } // Creation Dict (): Mapping() { p = PyDict_New (); validate(); } // Assignment acquires new ownership of pointer Dict& operator= (const Object& rhs) { return (*this = *rhs); } Dict& operator= (const PyObject* rhsp) { if(*this == rhsp) return *this; set(rhsp); return *this; } // Membership virtual bool accepts (PyObject *pyob) const { return pyob && PyDict_Check (pyob); } }; class Callable: public Object { protected: explicit Callable (): Object() {} public: // Constructor explicit Callable (PyObject *pyob): Object (pyob) { validate(); } Callable (const Callable& ob): Object(ob) { validate(); } // Assignment acquires new ownership of pointer Callable& operator= (const Object& rhs) { return (*this = *rhs); } Callable& operator= (const PyObject* rhsp) { if(*this == 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 Object(FromAPI(PyObject_CallObject(p, *args))); } Object apply(PyObject* args = Py_Null) const { return Object(FromAPI(PyObject_CallObject(p, args))); } }; Object operator+ (const Object& a); Object operator- (const Object& a); Object abs(const Object& a); pair<Object,Object> coerce (Object& a, Object& b); Object operator+ (const Object& a, const Object& b); Object operator+ (const Object& a, int j); Object operator+ (const Object& a, double v); Object operator+ (int j, const Object& b); Object operator+ (double v, const Object& b); Object operator- (const Object& a, const Object& b); Object operator- (const Object& a, int j); Object operator- (const Object& a, double v); Object operator- (int j, const Object& b); Object operator- (double v, const Object& b); Object operator* (const Object& a, const Object& b); Object operator* (const Object& a, int j); Object operator* (const Object& a, double v); Object operator* (int j, const Object& b); Object operator* (double v, const Object& b); Object operator/ (const Object& a, const Object& b); Object operator/ (const Object& a, int j); Object operator/ (const Object& a, double v); Object operator/ (int j, const Object& b); Object operator/ (double v, const Object& b); Object operator% (const Object& a, const Object& b); Object operator% (const Object& a, int j); Object operator% (const Object& a, double v); Object operator% (int j, const Object& b); Object operator% (double v, const Object& b); } // end namespace Py #endif // __CXX_Objects__h _______________ C++-SIG - SIG for Development of a C++ Binding to Python send messages to: c++-sig@python.org administrivia to: c++-sig-request@python.org _______________
participants (1)
-
Paul F. Dubois