/******************************************** copyright 1999 McMillan Enterprises, Inc. www.mcmillan-inc.com modified for weave by eric jones. *********************************************/ #if !defined(OBJECT_H_INCLUDED_) #define OBJECT_H_INCLUDED_ #include #include #include #include // for debugging #include namespace py { void fail(PyObject*, const char* msg); //--------------------------------------------------------------------------- // py::object -- A simple C++ interface to Python objects. // // This is the basic type from which all others are derived from. It is // also quite useful on its own. The class is very light weight as far as // data contents, carrying around only two python pointers. //--------------------------------------------------------------------------- class object { protected: //------------------------------------------------------------------------- // _obj is the underlying pointer to the real python object. //------------------------------------------------------------------------- PyObject* _obj; //------------------------------------------------------------------------- // grab_ref (rename to grab_ref) // // incref new owner, decref old owner, and adjust to new owner //------------------------------------------------------------------------- void grab_ref(PyObject* newObj) { // be careful to incref before decref if old is same as new Py_XINCREF(newObj); Py_XDECREF(_own); _own = _obj = newObj; }; //------------------------------------------------------------------------- // lose_ref (rename to lose_ref) // // decrease reference count without destroying the object. //------------------------------------------------------------------------- static PyObject* lose_ref(PyObject* o) { if (o != 0) --(o->ob_refcnt); return o; } private: //------------------------------------------------------------------------- // _own is set to _obj if we "own" a reference to _obj, else zero //------------------------------------------------------------------------- PyObject* _own; public: //------------------------------------------------------------------------- // forward declaration of reference obj returned when [] used as an lvalue. //------------------------------------------------------------------------- class keyed_ref; object() : _obj (0), _own (0) { }; object(const object& other) : _obj (0), _own (0) { grab_ref(other); }; object(PyObject* obj) : _obj (0), _own (0) { grab_ref(obj); }; //------------------------------------------------------------------------- // Numeric constructors //------------------------------------------------------------------------- object(bool val) { _obj = _own = PyInt_FromLong((int)val); }; object(int val) { _obj = _own = PyInt_FromLong((int)val); }; object(unsigned int val) { _obj = _own = PyLong_FromUnsignedLong(val); }; object(long val) { _obj = _own = PyInt_FromLong(val); }; object(unsigned long val) { _obj = _own = PyLong_FromUnsignedLong(val); }; object(double val) { _obj = _own = PyFloat_FromDouble(val); }; object(const std::complex& val) { _obj = _own = PyComplex_FromDoubles(val.real(),val.imag()); }; //------------------------------------------------------------------------- // string constructors //------------------------------------------------------------------------- object(const char* val) { _obj = _own = PyString_FromString((char*) val); }; object(const std::string& val) : _obj (0), _own (0) { _obj = _own = PyString_FromString((char*)val.c_str()); }; //------------------------------------------------------------------------- // destructor //------------------------------------------------------------------------- virtual ~object() { Py_XDECREF(_own); }; //------------------------------------------------------------------------- // casting operators //------------------------------------------------------------------------- operator PyObject* () const { return _obj; }; operator int () const { int _val; _val = PyInt_AsLong(_obj); if (PyErr_Occurred()) { PyErr_Clear(); fail(PyExc_TypeError, "cannot convert value to integer"); } return _val; }; operator float () const { float _val; _val = (float) PyFloat_AsDouble(_obj); if (PyErr_Occurred()) { PyErr_Clear(); fail(PyExc_TypeError, "cannot convert value to float"); } return _val; }; operator double () const { double _val; _val = PyFloat_AsDouble(_obj); if (PyErr_Occurred()) { PyErr_Clear(); fail(PyExc_TypeError, "cannot convert value to double"); } return _val; }; operator std::complex () const { double real, imag; real = PyComplex_RealAsDouble(_obj); imag = PyComplex_ImagAsDouble(_obj); if (PyErr_Occurred()) { PyErr_Clear(); fail(PyExc_TypeError, "cannot convert value to complex"); } return std::complex(real, imag); }; operator std::string () const { if (!PyString_Check(_obj)) fail(PyExc_TypeError, "cannot convert value to std::string"); return std::string(PyString_AsString(_obj)); }; operator char* () const { if (!PyString_Check(_obj)) fail(PyExc_TypeError, "cannot convert value to char*"); return PyString_AsString(_obj); }; //------------------------------------------------------------------------- // equal operator //------------------------------------------------------------------------- object& operator=(const object& other) { grab_ref(other); return *this; }; //------------------------------------------------------------------------- // printing // // This needs to become more sophisticated and handle objects that // implement the file protocol. //------------------------------------------------------------------------- void print(FILE* f, int flags=0) const { int res = PyObject_Print(_obj, f, flags); if (res == -1) throw 1; }; void print(object f, int flags=0) const { int res = PyFile_WriteObject(_obj, f, flags); if (res == -1) throw 1; }; //------------------------------------------------------------------------- // hasattr -- test if object has specified attribute //------------------------------------------------------------------------- int hasattr(const char* nm) const { return PyObject_HasAttrString(_obj, (char*) nm) == 1; }; int hasattr(const std::string& nm) const { return PyObject_HasAttrString(_obj, (char*) nm.c_str()) == 1; }; int hasattr(object& nm) const { return PyObject_HasAttr(_obj, nm) == 1; }; //------------------------------------------------------------------------- // attr -- retreive attribute/method from object //------------------------------------------------------------------------- object attr(const char* nm) const { PyObject* val = PyObject_GetAttrString(_obj, (char*) nm); if (!val) throw 1; return object(lose_ref(val)); }; object attr(const std::string& nm) const { return attr(nm.c_str()); }; object attr(const object& nm) const { PyObject* val = PyObject_GetAttr(_obj, nm); if (!val) throw 1; return object(lose_ref(val)); }; //------------------------------------------------------------------------- // setting attributes // // There is a combinatorial explosion here of function combinations. // perhaps there is a casting fix someone can suggest. //------------------------------------------------------------------------- void set_attr(const char* nm, object& val) { int res = PyObject_SetAttrString(_obj, (char*) nm, val); if (res == -1) throw 1; }; void set_attr(const std::string& nm, object& val) { int res = PyObject_SetAttrString(_obj, (char*) nm.c_str(), val); if (res == -1) throw 1; }; void set_attr(const object& nm, object& val) { int res = PyObject_SetAttr(_obj, nm, val); if (res == -1) throw 1; }; ////////////// int ////////////// void set_attr(const char* nm, int val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm, _val); if (res == -1) throw 1; }; void set_attr(const std::string& nm, int val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm.c_str(), _val); if (res == -1) throw 1; }; void set_attr(const object& nm, int val) { py::object _val = py::object(val); int res = PyObject_SetAttr(_obj, nm, _val); if (res == -1) throw 1; }; ////////////// unsigned long ////////////// void set_attr(const char* nm, unsigned long val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm, _val); if (res == -1) throw 1; }; void set_attr(const std::string& nm, unsigned long val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm.c_str(), _val); if (res == -1) throw 1; }; void set_attr(const object& nm, unsigned long val) { py::object _val = py::object(val); int res = PyObject_SetAttr(_obj, nm, _val); if (res == -1) throw 1; }; ////////////// double ////////////// void set_attr(const char* nm, double val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm, _val); if (res == -1) throw 1; }; void set_attr(const std::string& nm, double val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm.c_str(), _val); if (res == -1) throw 1; }; void set_attr(const object& nm, double val) { py::object _val = py::object(val); int res = PyObject_SetAttr(_obj, nm, _val); if (res == -1) throw 1; }; ////////////// complex ////////////// void set_attr(const char* nm, const std::complex& val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm, _val); if (res == -1) throw 1; }; void set_attr(const std::string& nm, const std::complex& val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm.c_str(), _val); if (res == -1) throw 1; }; void set_attr(const object& nm, const std::complex& val) { py::object _val = py::object(val); int res = PyObject_SetAttr(_obj, nm, _val); if (res == -1) throw 1; }; ////////////// char* ////////////// void set_attr(const char* nm, const char* val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm, _val); if (res == -1) throw 1; }; void set_attr(const std::string& nm, const char* val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm.c_str(), _val); if (res == -1) throw 1; }; void set_attr(const object& nm, const char* val) { py::object _val = py::object(val); int res = PyObject_SetAttr(_obj, nm, _val); if (res == -1) throw 1; }; ////////////// std::string ////////////// void set_attr(const char* nm, const std::string& val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm, _val); if (res == -1) throw 1; }; void set_attr(const std::string& nm, const std::string& val) { py::object _val = py::object(val); int res = PyObject_SetAttrString(_obj, (char*) nm.c_str(), _val); if (res == -1) throw 1; }; void set_attr(const object& nm, const std::string& val) { py::object _val = py::object(val); int res = PyObject_SetAttr(_obj, nm, _val); if (res == -1) throw 1; }; //------------------------------------------------------------------------- // del attributes/methods from object //------------------------------------------------------------------------- void del(const char* nm) { int result = PyObject_DelAttrString(_obj, (char*) nm); if (result == -1) throw 1; }; void del(const std::string& nm) { int result = PyObject_DelAttrString(_obj, (char*) nm.c_str()); if (result == -1) throw 1; }; void del(const object& nm) { int result = PyObject_DelAttr(_obj, nm); if (result ==-1) throw 1; }; //------------------------------------------------------------------------- // comparison // !! NOT TESTED //------------------------------------------------------------------------- int cmp(const object& other) const { int rslt = 0; int rc = PyObject_Cmp(_obj, other, &rslt); if (rc == -1) fail(PyExc_TypeError, "cannot make the comparison"); return rslt; }; int cmp(int other) const { object _other = object(other); return cmp(_other); }; int cmp(unsigned long other) const { object _other = object(other); return cmp(_other); }; int cmp(double other) const { object _other = object(other); return cmp(_other); }; int cmp(const std::complex& other) const { object _other = object(other); return cmp(_other); }; int cmp(const char* other) const { object _other = object((char*)other); return cmp(_other); }; int cmp(const std::string& other) const { object _other = object(other); return cmp(_other); }; bool operator == (const object& other) const { return cmp(other) == 0; }; bool operator == (int other) const { return cmp(other) == 0; }; bool operator == (unsigned long other) const { return cmp(other) == 0; }; bool operator == (double other) const { return cmp(other) == 0; }; bool operator == (const std::complex& other) const { return cmp(other) == 0; }; bool operator == (const std::string& other) const { return cmp(other) == 0; }; bool operator == (const char* other) const { return cmp(other) == 0; }; bool operator != (const object& other) const { return cmp(other) != 0; }; bool operator != (int other) const { return cmp(other) != 0; }; bool operator != (unsigned long other) const { return cmp(other) != 0; }; bool operator != (double other) const { return cmp(other) != 0; }; bool operator != (const std::complex& other) const { return cmp(other) != 0; }; bool operator != (const std::string& other) const { return cmp(other) != 0; }; bool operator != (const char* other) const { return cmp(other) != 0; }; bool operator < (const object& other) const { return cmp(other) < 0; }; bool operator < (int other) const { return cmp(other) < 0; }; bool operator < (unsigned long other) const { return cmp(other) < 0; }; bool operator < (double other) const { return cmp(other) < 0; }; bool operator < (const std::complex& other) const { return cmp(other) < 0; }; bool operator < (const std::string& other) const { return cmp(other) < 0; }; bool operator < (const char* other) const { return cmp(other) < 0; }; bool operator > (const object& other) const { return cmp(other) > 0; }; bool operator > (int other) const { return cmp(other) > 0; }; bool operator > (unsigned long other) const { return cmp(other) > 0; }; bool operator > (double other) const { return cmp(other) > 0; }; bool operator > (const std::complex& other) const { return cmp(other) > 0; }; bool operator > (const std::string& other) const { return cmp(other) > 0; }; bool operator > (const char* other) const { return cmp(other) > 0; }; bool operator >= (const object& other) const { return cmp(other) >= 0; }; bool operator >= (int other) const { return cmp(other) >= 0; }; bool operator >= (unsigned long other) const { return cmp(other) >= 0; }; bool operator >= (double other) const { return cmp(other) >= 0; }; bool operator >= (const std::complex& other) const { return cmp(other) >= 0; }; bool operator >= (const std::string& other) const { return cmp(other) >= 0; }; bool operator >= (const char* other) const { return cmp(other) >= 0; }; bool operator <= (const object& other) const { return cmp(other) <= 0; }; bool operator <= (int other) const { return cmp(other) <= 0; }; bool operator <= (unsigned long other) const { return cmp(other) <= 0; }; bool operator <= (double other) const { return cmp(other) <= 0; }; bool operator <= (const std::complex& other) const { return cmp(other) <= 0; }; bool operator <= (const std::string& other) const { return cmp(other) <= 0; }; bool operator <= (const char* other) const { return cmp(other) <= 0; }; //------------------------------------------------------------------------- // string representations // //------------------------------------------------------------------------- std::string repr() const { object result = PyObject_Repr(_obj); if (!(PyObject*)result) throw 1; return std::string(PyString_AsString(result)); }; std::string str() const { object result = PyObject_Str(_obj); if (!(PyObject*)result) throw 1; return std::string(PyString_AsString(result)); }; // !! Not Tested object unicode() const { object result = PyObject_Unicode(_obj); if (!(PyObject*)result) throw 1; lose_ref(result); return result; }; //------------------------------------------------------------------------- // calling methods on object // // Note: I changed args_tup from a tuple& to a object& so that it could // be inlined instead of implemented i weave_imp.cpp. This // provides less automatic type checking, but is potentially faster. //------------------------------------------------------------------------- object mcall(const char* nm) { object method = attr(nm); PyObject* result = PyEval_CallObjectWithKeywords(method,NULL,NULL); if (!result) throw 1; // signal exception has occurred. return object(lose_ref(result)); } object mcall(const char* nm, object& args_tup) { object method = attr(nm); PyObject* result = PyEval_CallObjectWithKeywords(method,args_tup,NULL); if (!result) throw 1; // signal exception has occurred. return object(lose_ref(result)); } object mcall(const char* nm, object& args_tup, object& kw_dict) { object method = attr(nm); PyObject* result = PyEval_CallObjectWithKeywords(method,args_tup,kw_dict); if (!result) throw 1; // signal exception has occurred. return object(lose_ref(result)); } object mcall(const std::string& nm) { return mcall(nm.c_str()); } object mcall(const std::string& nm, object& args_tup) { return mcall(nm.c_str(),args_tup); } object mcall(const std::string& nm, object& args_tup, object& kw_dict) { return mcall(nm.c_str(),args_tup,kw_dict); } //------------------------------------------------------------------------- // calling callable objects // // Note: see not on mcall() //------------------------------------------------------------------------- object call() const { PyObject *rslt = PyEval_CallObjectWithKeywords(*this, NULL, NULL); if (rslt == 0) throw 1; return object(lose_ref(rslt)); } object call(object& args_tup) const { PyObject *rslt = PyEval_CallObjectWithKeywords(*this, args_tup, NULL); if (rslt == 0) throw 1; return object(lose_ref(rslt)); } object call(object& args_tup, object& kw_dict) const { PyObject *rslt = PyEval_CallObjectWithKeywords(*this, args_tup, kw_dict); if (rslt == 0) throw 1; return object(lose_ref(rslt)); } //------------------------------------------------------------------------- // check if object is callable //------------------------------------------------------------------------- bool is_callable() const { return PyCallable_Check(_obj) == 1; }; //------------------------------------------------------------------------- // retreive the objects hash value //------------------------------------------------------------------------- int hash() const { int result = PyObject_Hash(_obj); if (result == -1 && PyErr_Occurred()) throw 1; return result; }; //------------------------------------------------------------------------- // test whether object is true //------------------------------------------------------------------------- bool is_true() const { return PyObject_IsTrue(_obj) == 1; }; //------------------------------------------------------------------------- // test for null //------------------------------------------------------------------------- bool is_null() { return (_obj == NULL); } /* * //------------------------------------------------------------------------- // test whether object is not true //------------------------------------------------------------------------- #if defined(__GNUC__) && __GNUC__ < 3 bool not() const { #else bool operator not() const { #endif return PyObject_Not(_obj) == 1; }; */ //------------------------------------------------------------------------- // return the variable type for the object //------------------------------------------------------------------------- object type() const { PyObject* result = PyObject_Type(_obj); if (!result) throw 1; return lose_ref(result); }; object is_int() const { return PyInt_Check(_obj) == 1; }; object is_float() const { return PyFloat_Check(_obj) == 1; }; object is_complex() const { return PyComplex_Check(_obj) == 1; }; object is_list() const { return PyList_Check(_obj) == 1; }; object is_tuple() const { return PyTuple_Check(_obj) == 1; }; object is_dict() const { return PyDict_Check(_obj) == 1; }; object is_string() const { return PyString_Check(_obj) == 1; }; //------------------------------------------------------------------------- // size, len, and length are all synonyms. // // length() is useful because it allows the same code to work with STL // strings as works with py::objects. //------------------------------------------------------------------------- int size() const { int result = PyObject_Size(_obj); if (result == -1) throw 1; return result; }; int len() const { return size(); }; int length() const { return size(); }; //------------------------------------------------------------------------- // set_item // // To prevent a combonatorial explosion, only objects are allowed for keys. // Users are encouraged to use the [] interface for setting values. //------------------------------------------------------------------------- virtual void set_item(const object& key, const object& val) { int rslt = PyObject_SetItem(_obj, key, val); if (rslt==-1) throw 1; }; //------------------------------------------------------------------------- // operator[] //------------------------------------------------------------------------- // !! defined in weave_imp.cpp // !! I'd like to refactor things so that they can be defined here. keyed_ref operator [] (object& key); keyed_ref operator [] (const char* key); keyed_ref operator [] (const std::string& key); keyed_ref operator [] (int key); keyed_ref operator [] (double key); keyed_ref operator [] (const std::complex& key); //------------------------------------------------------------------------- // iter methods // !! NOT TESTED //------------------------------------------------------------------------- //------------------------------------------------------------------------- // iostream operators //------------------------------------------------------------------------- friend std::ostream& operator <<(std::ostream& os, py::object& obj); //------------------------------------------------------------------------- // refcount utilities //------------------------------------------------------------------------- PyObject* disown() { _own = 0; return _obj; }; int refcount() { return _obj->ob_refcnt; } }; //--------------------------------------------------------------------------- // keyed_ref // // Provides a reference value when operator[] returns an lvalue. The // reference has to keep track of its parent object and its key in the parent // object so that it can insert a new value into the parent at the // appropriate place when a new value is assigned to the keyed_ref object. // // The keyed_ref class is also used by the py::dict class derived from // py::object // !! Note: Need to check ref counting on key and parent here. //--------------------------------------------------------------------------- class object::keyed_ref : public object { object& _parent; object _key; public: keyed_ref(object obj, object& parent, object& key) : object(obj), _parent(parent), _key(key) {}; virtual ~keyed_ref() {}; keyed_ref& operator=(const object& other) { grab_ref(other); _parent.set_item(_key, other); return *this; } keyed_ref& operator=(int other) { object _other = object(other); return operator=(_other); } keyed_ref& operator=(double other) { object _other = object(other); return operator=(_other); } keyed_ref& operator=(const std::complex& other) { object _other = object(other); return operator=(_other); } keyed_ref& operator=(const char* other) { object _other = object(other); return operator=(_other); } keyed_ref& operator=(const std::string& other) { object _other = object(other); return operator=(_other); } keyed_ref& operator=(const keyed_ref& other) { object _other = object(other); return operator=(_other); } }; } // namespace #endif // !defined(OBJECT_H_INCLUDED_)