[C++-SIG] some progress: CXX_Objects.h
Paul F. Dubois
dubois1 at llnl.gov
Wed Jan 7 20:14:20 CET 1998
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 at python.org
administrivia to: c++-sig-request at python.org
_______________
More information about the Cplusplus-sig
mailing list