Created
November 23, 2014 18:20
-
-
Save anonymous/87246729a9860db9c87c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
/* | |
Wrap Python stock objects e.g. List String Dict Bool etc | |
*/ | |
#include "Base.hxx" | |
#include <iostream> | |
#include STR_STREAM | |
#include <string> | |
#include <iterator> | |
#include <utility> | |
#include <typeinfo> | |
namespace Py | |
{ | |
#define EXPOSE( returntype, func ) \ | |
returntype func() const { return the_item.func(); } | |
#define EXPOSE1( returntype, func, param1_type ) \ | |
returntype func( param1_type x ) const { return the_item.func(x); } | |
//using sequence_index_type = int32_t; // type of an index into a sequence (must allow -ve index) | |
// Forward declarations | |
class Object; | |
class Type; | |
class Bytes; | |
class String; | |
class List; | |
class Tuple; | |
class Dict; | |
template<TEMPLATE_TYPENAME T> class SeqBase; | |
template<TEMPLATE_TYPENAME T> class MapBase; | |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
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<T> or MapBase<T>. | |
This example assumes Object. | |
(1) Write "check" method: | |
int MyType_Check( PyObject* ) | |
modeled after PyInt_Check, PyFloat_Check, etc. | |
(2) Add "accepts" method: | |
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 ) | |
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 ExtObj_old<T> to define an object | |
note that it contains ExtObj_old<T>::check | |
which you can use in accepts when writing a wrapper class. | |
See Demo/range.h and Demo/range.cxx for an example. | |
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
// !!! π ToDo: http://www.gotw.ca/publications/using_auto_ptr_effectively.htm | |
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) _XINCREF(p); | |
validate(); | |
} | |
void release() { _XDECREF(p); p = nullptr; } | |
void validate(); | |
public: | |
// Constructor acquires new ownership of pointer unless explicitly told not to. | |
explicit Object( PyObject* pyob=_None(), bool owned=false ) : p{pyob} { | |
if( !owned ) _XINCREF(p); | |
validate(); | |
} | |
// Copy constructor acquires new ownership of pointer | |
Object( const Object& ob ) : p{ob.p} { | |
_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) set(rhsp) ; return *this; } | |
// Can pyob be used in this object's constructor? | |
virtual bool accepts( PyObject* pyob ) const { return true; } // allow any object or NULL | |
// - - - | |
virtual ~Object() { release(); } | |
// Loaning the pointer to others, retain ownership | |
//PyObject* operator*() const { return p; } | |
// Explicit reference_counting changes | |
void increment_reference_count() { _XINCREF(p); } | |
void decrement_reference_count() { | |
// not allowed to commit suicide, however | |
if( reference_count() == 1 ) throw RuntimeError{ "Object::decrement_reference_count error." }; | |
_XDECREF(p); | |
} | |
// Would like to call this pointer() but messes up STL in SeqBase<T> | |
PyObject* ptr() const | |
{ | |
return p; | |
} | |
// Queries | |
Py_ssize_t reference_count() const { 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<char*>( s.c_str() ) ) ? true : false; } | |
Object getAttr( const std::string& s ) const { return Object{ PyObject_GetAttrString( p, const_cast<char*>( s.c_str() ) ), true }; } | |
Object callMemberFunction( const std::string& function_name ) const; | |
Object callMemberFunction( const std::string& function_name, const Tuple& args ) const; | |
Object callMemberFunction( const std::string& function_name, const Tuple& args, const Dict& kw ) const; | |
Object getItem( const Object& key ) const { return Object{ PyObject_GetItem( p, key.ptr() ), true }; } | |
long hashValue() const { return PyObject_Hash(p); } | |
// convert to bool | |
bool as_bool() const { return PyObject_IsTrue(ptr()) != 0; } | |
//operator bool() const | |
//{ | |
// return as_bool(); | |
//} | |
// int print( FILE *fp, int flags=Py_Print_RAW ) | |
//{ | |
// return PyObject_Print( p, fp, flags ); | |
//} | |
// identity tests | |
bool is( PyObject* pother ) const {return p == pother; } | |
bool is( const Object& other ) const {return p == other.p; } | |
bool isNull() const { return p == nullptr;} | |
bool isNone() const { return p == _None();} | |
bool isBoolean() const { return _Bool_Check (p);} | |
bool isBytes() const { return _Bytes_Check (p);} | |
bool isString() const { return _Unicode_Check (p);} | |
bool isTuple() const { return _Tuple_Check (p);} | |
bool isList() const { return _List_Check (p);} | |
bool isDict() const { return _Dict_Check (p);} | |
bool isTrue() const { return PyObject_IsTrue (p) != 0;} | |
bool isCallable() const { return PyCallable_Check (p) != 0;} | |
bool isNumeric() const { return PyNumber_Check (p) != 0;} | |
bool isSequence() const { return PySequence_Check (p) != 0;} | |
bool isMapping() const { return PyMapping_Check (p) != 0;} | |
bool isType( const Type& t ) const; | |
// Commands | |
void setAttr( const std::string& s, const Object& value ) { if( PyObject_SetAttrString( p, const_cast<char*>(s.c_str()), value.ptr() ) == -1 ) throw AttributeError{ "setAttr failed." }; } | |
void delAttr( const std::string& s ) { if( PyObject_DelAttrString( p, const_cast<char*>(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 | |
}; | |
// End of class Object | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
// Null can be return from when it is require to return NULL to Python from a method | |
class Null: public Object | |
{ | |
public: | |
Null() : Object{nullptr} { } | |
virtual ~Null() { } | |
bool accepts( PyObject* pyob ) const override { return pyob == nullptr; } | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
bool operator==( const Object& o1, const Object& o2 ); | |
bool operator!=( const Object& o1, const Object& o2 ); | |
bool operator>=( const Object& o1, const Object& o2 ); | |
bool operator<=( const Object& o1, const Object& o2 ); | |
bool operator< ( const Object& o1, const Object& o2 ); | |
bool operator> ( const Object& o1, const Object& o2 ); | |
// | |
// Convert an owned Python pointer into a PyCXX Object | |
// | |
inline Object asObject( PyObject* p ) { | |
return Object{ p, true }; } | |
// new_reference_to also overloaded below on Object | |
inline PyObject* new_reference_to( PyObject* p ) | |
{ | |
_XINCREF( p ); | |
return p; | |
} | |
inline PyObject* new_reference_to( const Object& g ) | |
{ | |
PyObject* p = g.ptr(); | |
_XINCREF( p ); | |
return p; | |
} | |
// Python special None& Boolean values | |
inline Object None() { return Object{ _None() }; } | |
inline Object True() { return Object{ _True() }; } | |
inline Object False() { return Object{ _False() }; } | |
// 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 | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
// π -- considered http://ideone.com/KBHFPK | |
#define COMMON4( CLASS, BASE, ASSIGN, CHECK ) \ | |
explicit CLASS( PyObject* pyob, bool owned=false ) : BASE{ pyob, owned } { validate(); } \ | |
\ | |
CLASS& operator=( const Object& rhs ) { return *this = rhs.ptr(); } \ | |
CLASS& operator=( PyObject* rhsp ) { if(ptr()!=rhsp) set(ASSIGN); return *this; } \ | |
\ | |
bool accepts( PyObject* pyob ) const override { return pyob && CHECK; } | |
#define COMMON5( CLASS, BASE, CHECK ) \ | |
COMMON4( CLASS, BASE, rhsp, CHECK ) \ | |
CLASS( const Object& ob ) : BASE{ ob.ptr() } { validate(); } \ | |
// http://coliru.stacked-crooked.com/a/f949960d21a867b4 | |
using CheckType = bool(PyObject*); // using CheckType = (PyObject*) -> bool; would be nice? | |
inline PyObject* setDefault(PyObject* pyob){ return pyob; }; | |
template< typename Derived, typename Base, CheckType checkfunc, decltype(setDefault) setfunc = setDefault > | |
class ObjBase : public Object | |
{ | |
public: | |
ObjBase(){ }; | |
explicit ObjBase( PyObject* pyob, bool owned=false ) : Base{pyob,owned} { validate(); } | |
ObjBase& operator=( const Object& rhs ) { return *this = rhs.ptr(); } | |
ObjBase& operator=( PyObject* rhsp ) { if(ptr()!=rhsp) set(setfunc(rhsp)); return *this; } | |
bool accepts( PyObject* pyob ) const override { return pyob && checkfunc(pyob); } | |
ObjBase( const Object& ob ) : Base{ob.ptr()} { validate(); } | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
// Class Type | |
class Type: public ObjBase< Type, Object, _Type_Check > { | |
using ObjBase< Type, Object, _Type_Check >::ObjBase; | |
public: | |
// COMMON5( Type, Object, _Type_Check(pyob) ) | |
Type( const Type& t ) : ObjBase{t} { validate(); } | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
// class Boolean: public Object | |
// { | |
//// using ObjBase< Boolean, Object, bool_check >::ObjBase; | |
// | |
// public: | |
// COMMON5( Boolean, Object, PyObject_IsTrue(pyob) != -1 ) | |
// | |
// Boolean( const Boolean& ob ) : Object{ob} { validate(); } | |
// | |
// Boolean( bool v=false ) { set( PyBool_FromLong(v?1:0), true ); validate(); } // create from bool | |
// | |
// Boolean& operator=( bool v ) { set( PyBool_FromLong(v?1:0), true ); return *this; } | |
// | |
// operator bool() const { return as_bool(); } | |
// }; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
inline bool bool_check( PyObject* pyob ){ return PyObject_IsTrue(pyob) != -1; } | |
class Boolean: public ObjBase< Boolean, Object, bool_check > | |
{ | |
using ObjBase< Boolean, Object, bool_check >::ObjBase; | |
public: | |
// COMMON5( Boolean, Object, PyObject_IsTrue(pyob) != -1 ) | |
Boolean( const Boolean& ob ) : ObjBase{ob} { validate(); } | |
Boolean( bool v=false ) { set( PyBool_FromLong(v?1:0), true ); validate(); } // create from bool | |
Boolean& operator=( bool v ) { set( PyBool_FromLong(v?1:0), true ); return *this; } | |
operator bool() const { return as_bool(); } | |
}; | |
class Long: public Object | |
{ | |
public: | |
COMMON4( Long, Object, PyNumber_Long(rhsp), _Long_Check(pyob) ) | |
// π I *think* the explicit was originally on the wrong function below | |
explicit Long( const Long& ob ) : Object{ ob } { validate(); } | |
Long( const Object& ob ) : Object{ PyNumber_Long(ob.ptr()) , true } { validate(); } // ... any object | |
// try to create from ... | |
explicit Long( long v = 0L ) : Object{ PyLong_FromLong(v) , true } { validate(); } | |
explicit Long( unsigned long v ) : Object{ PyLong_FromUnsignedLong(v) , true } { validate(); } | |
explicit Long( int v ) : Object{ PyLong_FromLong( static_cast<long>(v) ) , true } { validate(); } // ... int | |
#ifdef HAVE_LONG_LONG | |
explicit Long( PY_LONG_LONG v ) : Object{ PyLong_FromLongLong(v) , true } { validate(); } | |
explicit Long( unsigned PY_LONG_LONG v ) : Object{ PyLong_FromUnsignedLongLong(v) , true } { validate(); } | |
#endif | |
Long& operator=( int v ) { set( PyLong_FromLong(long(v)) , true ); return *this; } | |
Long& operator=( long v ) { set( PyLong_FromLong(v) , true ); return *this; } | |
Long& operator=( unsigned long v ) { set( PyLong_FromUnsignedLong(v) , true ); return *this; } | |
#ifdef HAVE_LONG_LONG | |
Long& operator=( PY_LONG_LONG v ) { set( PyLong_FromLongLong(v) , true ); return *this; } | |
Long& operator=( unsigned PY_LONG_LONG v ) { set( PyLong_FromUnsignedLongLong(v) , true ); return *this; } | |
#endif | |
long as_long() const { return PyLong_AsLong ( ptr() ); } // convert to long | |
long as_unsigned_long() const { return PyLong_AsUnsignedLong ( ptr() ); } // convert to unsigned | |
double as_double() const { return PyLong_AsDouble ( ptr() ); } // convert to double | |
#ifdef HAVE_LONG_LONG | |
PY_LONG_LONG as_long_long() const { return PyLong_AsLongLong ( ptr() ); } | |
unsigned PY_LONG_LONG as_unsigned_long_long() const { return PyLong_AsUnsignedLongLong ( ptr() ); } | |
#endif | |
operator long() const { return as_long(); } | |
operator int() const { return static_cast<int>(as_long()); } | |
operator unsigned long() const { return as_unsigned_long(); } | |
operator double() const { return as_double(); } | |
#ifdef HAVE_LONG_LONG | |
operator PY_LONG_LONG() const { return as_long_long(); } | |
operator unsigned PY_LONG_LONG() const { return as_unsigned_long_long(); } | |
#endif | |
// prefix | |
Long operator++() { set( PyNumber_Add ( ptr(), Long{1}.ptr() ) ); return *this; } | |
Long operator--() { set( PyNumber_Subtract( ptr(), Long{1}.ptr() ) ); return *this; } | |
// postfix | |
Long operator++( int ) { Long a{ *this }; set( PyNumber_Add ( ptr(), Long{1}.ptr() ) ); return a; } | |
Long operator--( int ) { Long a{ *this }; set( PyNumber_Subtract( ptr(), Long{1}.ptr() ) ); return a; } | |
}; | |
#ifdef PYCXX_PYTHON_2TO3 | |
// PyCXX for Python2 had an Int and LongLong classes | |
typedef Long Int; | |
# ifdef HAVE_LONG_LONG | |
typedef Long LongLong; | |
# endif | |
#endif | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
class Float: public Object | |
{ | |
public: | |
COMMON4( Float, Object, PyNumber_Float(rhsp), _Float_Check(pyob) ) | |
// try to make from any object | |
Float( const Object& ob ) : Object{ PyNumber_Float(ob.ptr()), true } { validate(); } | |
Float( const Float& f ) : Object{ f } { validate(); } | |
explicit Float( double v=0.0 ) : Object{ PyFloat_FromDouble(v), true } { validate(); } // make from double | |
double as_double() const { return PyFloat_AsDouble( ptr() ); } | |
operator double() const { return as_double(); } // convert to double | |
Float& operator=( double v ) { set( PyFloat_FromDouble(v) , true ); return *this; } // assign from a double | |
Float& operator=( int v ) { set( PyFloat_FromDouble(double(v)) , true ); return *this; } // assign from an int | |
Float& operator=( long v ) { set( PyFloat_FromDouble(double(v)) , true ); return *this; } // assign from long | |
Float& operator=( const Long& iob ) { set( PyFloat_FromDouble(double(iob.as_long())), true ); return *this; } // assign from an Long | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
class Complex: public Object | |
{ | |
public: | |
COMMON4( Complex, Object, rhsp, _Complex_Check(pyob) ) | |
Complex( const Complex& f ) : Object{ f } { validate(); } | |
explicit Complex( double v=0.0, double w=0.0 ) : Object{ PyComplex_FromDoubles( v, w ) , true } { validate(); } // make from double | |
operator Py_complex() const { return PyComplex_AsCComplex( ptr() ); } // convert to Py_complex | |
// assign from... | |
Complex& operator=( const Py_complex& v ) { set( PyComplex_FromCComplex(v) , true ); return *this; } | |
Complex& operator=( double v ) { set( PyComplex_FromDoubles(v,0.0) , true ); return *this; } | |
Complex& operator=( int v ) { set( PyComplex_FromDoubles(double(v),0.0) , true ); return *this; } | |
Complex& operator=( long v ) { set( PyComplex_FromDoubles(double(v),0.0) , true ); return *this; } | |
Complex& operator=( const Long& iob ) { set( PyComplex_FromDoubles(double(iob.as_long()),0.0), true ); return *this; } | |
double real() const { return PyComplex_RealAsDouble(ptr()); } | |
double imag() const { return PyComplex_ImagAsDouble(ptr()); } | |
}; | |
#define OPS( A, B ) \ | |
bool operator == ( A, B ); \ | |
bool operator != ( A, B ); \ | |
bool operator > ( A, B ); \ | |
bool operator < ( A, B ); \ | |
bool operator >= ( A, B ); \ | |
bool operator <= ( A, B ); | |
#define UNI( A ) \ | |
OPS( A, A ) | |
#define BI_( A, B ) \ | |
OPS( A, B ) \ | |
OPS( B, A ) | |
UNI( const Long& ) | |
BI_( const Long& , int ) | |
BI_( const Long& , long ) | |
#ifdef HAVE_LONG_LONG | |
BI_( const Long& , PY_LONG_LONG ) | |
#endif | |
UNI( const Float& ) | |
BI_( const Float& , double ) | |
#undef BI_ | |
#undef UNI | |
#undef OPS | |
#undef OP | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
// Sequences | |
// Sequences are here represented as sequences of items of type T. | |
// The base class SeqBase<T> represents that. | |
// In basic Python T is always "Object". | |
// Proxy is what you get if you get elements from a non-const SeqBase<T>. | |
// Note: Proxy could probably be a nested class in SeqBase<T> but that might stress | |
// some compilers needlessly. Simlarly for proxy 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 Proxy to an Object is wanted. | |
// class SeqBase<T> | |
// ...the base class for all sequence types | |
/* π | |
User creates 'SeqBase<T>' -- a sequence of T-s. | |
Internally we create SeqBase<Proxy> -- a sequence of proxy objects. | |
*/ | |
template<typename T> | |
class SeqBase: public Object | |
{ | |
private: | |
using This = SeqBase<T>; | |
class Proxy | |
{ | |
private: | |
This& s; // the sequence | |
int32_t index; // item number | |
T the_item; // lvalue | |
public: | |
Proxy( This& seq, int32_t j ) : s{seq} , index{j} , the_item{s.getItem(j)} { } | |
Proxy( const Proxy& range ) : s{range.s} , index{range.index} , the_item{range.the_item} { } | |
Proxy( Object& obj ) : s{ dynamic_cast< This& >(obj) }, index{0} , the_item{s.getItem(index)} { } // TMM: added this Proxy ctor for use with STL algorithms | |
~Proxy() { } | |
// r-value | |
operator T() const { return the_item; } | |
// l-value | |
Proxy& operator=( const Proxy& rhs ) { s.setItem( index, the_item = rhs.the_item ); return *this; } | |
Proxy& operator=( const T& ob ) { s.setItem( index, the_item = ob ); return *this; } | |
// someone is attempting to type convert this proxy back into an Object | |
//explicit operator Object() const { return Object{ the_item }; } | |
/* Forward everything else to the item. | |
EXPOSE defined as: | |
#define EXPOSE( returntype, func ) \ | |
returntype func() const { return the_item.func(); } | |
*/ | |
EXPOSE( PyObject*, ptr ) | |
EXPOSE( int , reference_count ) | |
EXPOSE( Type , type ) | |
EXPOSE( long , hashValue ) | |
EXPOSE( bool , isCallable ) | |
EXPOSE( bool , isInstance ) | |
EXPOSE( bool , isDict ) | |
EXPOSE( bool , isList ) | |
EXPOSE( bool , isMapping ) | |
EXPOSE( bool , isNumeric ) | |
EXPOSE( bool , isSequence ) | |
EXPOSE( bool , isTrue ) | |
EXPOSE( bool , isTuple ) | |
EXPOSE( bool , isString ) | |
EXPOSE1( bool , isType , const Type& ) | |
EXPOSE1( bool , hasAttr, const std::string& ) | |
EXPOSE1( Object , getAttr, const std::string& ) | |
EXPOSE1( Object , getItem, const Object& ) | |
/* | |
can't yet do: | |
EXPOSE( String , str ) | |
EXPOSE( String , repr ) | |
as String hasn't been defined yet, so we are not permitted to use myString.str() or .repr(); | |
*/ | |
String str() const; | |
String repr() const; | |
// 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 Proxy | |
// - - - | |
public: | |
// stdlib definitions -- so that this class qualifies as a stdlib container | |
using size_type = size_t; | |
using value_type = T; // TMM: 26Jun'01 | |
using const_reference = T; | |
using reference = Proxy; | |
using pointer = Proxy*; | |
using difference_type = int; | |
COMMON5( SeqBase<T>, Object, PySequence_Check(pyob) ) | |
explicit SeqBase<T>( ) : Object{ PyTuple_New(0), true } { validate(); } | |
virtual size_type size() const { return PySequence_Length( ptr() ); } | |
size_type length() const { return PySequence_Length( ptr() ); } | |
virtual size_type max_size() const { return std::string::npos; } // ? | |
virtual size_type capacity() const { return size(); } | |
virtual void swap( This& c ) { This temp = c; c = Object::ptr(); Object::set(temp.ptr()); } | |
virtual T getItem( int32_t index ) const { return T( asObject( PySequence_GetItem(ptr(),index) ) ); } | |
virtual void setItem( int32_t index, const T& ob ) { if( PySequence_SetItem(ptr(),index,ob.ptr()) == -1 ) throw Exception{}; } | |
/* Element access | |
Consumer does e.g. | |
auto s = new SeqBase<Int>(); | |
: | |
Int foo = s[3] | |
'const T operator[]' gets called, returning our item using Object::getItem(3) | |
s[3] = Int{42} | |
'Proxy operator[]' gets called, creating and returning a Proxy-object | |
The Proxy-object's L-value assignment operator will get invoked, as we are doing 'Proxy-object = ...' | |
i.e. Proxy-object is on the LEFT HAND SIDE. | |
which, if you care to look, sets the item | |
Why do we need this design pattern? | |
How else to handle 's[3] = ...'? | |
*/ | |
const T operator[]( int32_t index ) const { return getItem( index ); } | |
const T front() const { return getItem( 0 ); } | |
const T back() const { return getItem( size() - 1 ); } | |
Proxy operator[]( int32_t index ) { return Proxy{ *this, index }; } | |
Proxy front() { return Proxy{ *this, 0 }; } | |
Proxy back() { return Proxy{ *this, (int)size()-1 }; } | |
This repeat( int count ) const { return This{ PySequence_Repeat( ptr(), count ) , true }; } | |
This concat( const This& other ) const { return This{ PySequence_Concat( ptr(), *other ), true }; } | |
// more stdlib compatability | |
void verify_length( size_type required_size ) const { verify_length( required_size, required_size ); } | |
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<T> length." }; | |
} | |
// - - - | |
template<typename I> | |
class iterator_base | |
: public std::iterator< std::random_access_iterator_tag, Proxy, int > | |
{ | |
public: | |
friend class SeqBase<T>; // allow SeqBase<T> to see our data | |
This* seq; | |
int32_t count; | |
// protected constructors ensure base class is only used as abstract | |
iterator_base() : seq{ 0 } , count{ 0 } { } | |
iterator_base( This *s, int where ) : seq{ s } , count{ where } { } | |
iterator_base( const iterator_base& other ) : seq{ other.seq } , count{ other.count } { } | |
// virtual ~iterator_base() = 0; | |
// { }; | |
public: | |
I& operator=( const I& other ) { | |
if( this != &other ) { | |
seq = other.seq; | |
count = other.count; | |
} | |
return static_cast<I&>( *this ); | |
} | |
bool eql( const I& other ) const { return seq->ptr() == other.seq->ptr() && count == other.count; } | |
bool neq( const I& other ) const { return seq->ptr() != other.seq->ptr() || count != other.count; } | |
bool lss( const I& other ) const { return count < other.count; } | |
bool gtr( const I& other ) const { return count > other.count; } | |
bool leq( const I& other ) const { return count <= other.count; } | |
bool geq( const I& other ) const { return count >= other.count; } | |
I operator + ( int n ) const { return I{ seq, count + n }; } | |
I operator - ( int n ) const { return I{ seq, count - n }; } | |
I& operator += ( int n ) { count += n; return static_cast<I&>( *this ); } | |
I& operator -= ( int n ) { count -= n; return static_cast<I&>( *this ); } | |
int operator-( const I& other ) const { | |
if( seq->ptr() != other.seq->ptr() ) | |
throw RuntimeError{ "SeqBase<T>::iterator comparison error" }; | |
return count - other.count; | |
} | |
// prefix | |
I& operator++() { count++; return static_cast<I&>( *this ); } | |
I& operator--() { count--; return static_cast<I&>( *this ); } | |
// postfix | |
I operator++( int ) { return I{ seq, count++ }; } | |
I operator--( int ) { return I{ seq, count-- }; } | |
std::string diagnose() const { | |
std::stringstream oss; | |
oss << "iterator diagnosis " << seq << ", " << count; | |
return oss.str(); | |
} | |
}; // end of class SeqBase<T>::iterator_base | |
// - - - | |
class iterator : public iterator_base<iterator> | |
{ | |
public: | |
using iterator_base<iterator>::iterator_base; // C++11 has inherited constructors, Nice! | |
// need this->foo, Base<T>::foo or this->Base<T>::foo to access members of base class | |
Proxy operator*() { return Proxy{ *(this->seq), this->count }; } // the value this iterator is pointing to | |
Proxy operator[]( int32_t i ) { return Proxy{ *(this->seq), this->count + i }; } | |
}; | |
// - - - | |
class const_iterator : public iterator_base<const_iterator> | |
{ | |
public: | |
using iterator_base<const_iterator>::iterator_base; | |
const T operator*() const { return (this->seq)->getItem( this->count ); } | |
const T operator[]( int32_t i ) const { return (this->seq)->getItem( this->count + i ); } | |
}; | |
// - - - | |
iterator begin() { return iterator{ this, 0 }; } | |
iterator end() { return iterator{ this, (int)length() }; } | |
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<Object> Sequence; | |
template <typename T> bool operator ==( const typename SeqBase<T>::iterator& l, const typename SeqBase<T>::iterator& r ); | |
template <typename T> bool operator !=( const typename SeqBase<T>::iterator& l, const typename SeqBase<T>::iterator& r ); | |
template <typename T> bool operator > ( const typename SeqBase<T>::iterator& l, const typename SeqBase<T>::iterator& r ); | |
template <typename T> bool operator >=( const typename SeqBase<T>::iterator& l, const typename SeqBase<T>::iterator& r ); | |
template <typename T> bool operator < ( const typename SeqBase<T>::iterator& l, const typename SeqBase<T>::iterator& r ); | |
template <typename T> bool operator <=( const typename SeqBase<T>::iterator& l, const typename SeqBase<T>::iterator& r ); | |
template <typename T> bool operator ==( const typename SeqBase<T>::const_iterator& l, const typename SeqBase<T>::const_iterator& r ); | |
template <typename T> bool operator !=( const typename SeqBase<T>::const_iterator& l, const typename SeqBase<T>::const_iterator& r ); | |
template <typename T> bool operator > ( const typename SeqBase<T>::const_iterator& l, const typename SeqBase<T>::const_iterator& r ); | |
template <typename T> bool operator >=( const typename SeqBase<T>::const_iterator& l, const typename SeqBase<T>::const_iterator& r ); | |
template <typename T> bool operator < ( const typename SeqBase<T>::const_iterator& l, const typename SeqBase<T>::const_iterator& r ); | |
template <typename T> bool operator <=( const typename SeqBase<T>::const_iterator& l, const typename SeqBase<T>::const_iterator& r ); | |
#define _OP6(T) \ | |
extern bool operator == ( T l, T r ); \ | |
extern bool operator != ( T l, T r ); \ | |
extern bool operator < ( T l, T r ); \ | |
extern bool operator > ( T l, T r ); \ | |
extern bool operator <= ( T l, T r ); \ | |
extern bool operator >= ( T l, T r ); | |
#define OP6( T ) \ | |
_OP6(const T::iterator &) \ | |
_OP6(const T::const_iterator &) | |
OP6( Sequence ) | |
#undef _OP6 | |
#undef OP6 | |
// ================================================== | |
// 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<Py_UNICODE> unicodestring; | |
extern Py_UNICODE unicode_null_string[1]; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
class Byte: public Object | |
{ | |
public: | |
COMMON5( Byte, Object, _Unicode_Check(pyob) && PySequence_Length(pyob) == 1 ) | |
Byte( const std::string& v = "" ) : Object{ PyBytes_FromStringAndSize( const_cast<char*>( v.c_str() ), 1 ), true } { validate(); } | |
Byte( char v ) : Object{ PyBytes_FromStringAndSize(& v, 1 ) , true } { validate(); } | |
// Assignment from C string | |
Byte& operator=( const std::string& v ) { set( PyBytes_FromStringAndSize( const_cast<char*>( v.c_str() ),1 ), true ); return *this; } | |
Byte& operator=( char v ) { set( PyUnicode_FromStringAndSize( &v, 1 ) , true ); return *this; } | |
// Conversion | |
operator Bytes() const; | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
class Bytes: public SeqBase<Byte> | |
{ | |
public: | |
COMMON5( Bytes, SeqBase<Byte>, _Bytes_Check(pyob) ) | |
Bytes( ) : SeqBase<Byte>{ PyBytes_FromStringAndSize( "" , 0 ) , true } { validate(); } | |
Bytes( const std::string& v ) : SeqBase<Byte>{ PyBytes_FromStringAndSize( const_cast<char*>(v.data()), static_cast<int>(v.length()) ) , true } { validate(); } | |
Bytes( const std::string& v, Py_ssize_t vsize ) : SeqBase<Byte>{ PyBytes_FromStringAndSize( const_cast<char*>(v.data()), static_cast<int>(vsize) ) , true } { validate(); } | |
Bytes( const char *v ) : SeqBase<Byte>{ PyBytes_FromString( v ) , true } { validate(); } | |
Bytes( const char *v, Py_ssize_t vsize ) : SeqBase<Byte>{ PyBytes_FromStringAndSize( const_cast<char*>(v) , vsize ) , true } { validate(); } | |
// Membership | |
size_type capacity() const override { return max_size(); } | |
// Assignment from C string | |
Bytes& operator=( const std::string& v ) { set( PyBytes_FromStringAndSize( const_cast<char*>( v.data() ), static_cast<int>( v.length() ) ), true ); return *this; } | |
String decode( const char *encoding, const char *error="strict" ); | |
// Queries | |
size_type size() const override { return static_cast<size_type>( PyBytes_Size( ptr() ) ); } | |
operator std::string() const { return as_std_string(); } | |
std::string as_std_string() const { return std::string( PyBytes_AsString(ptr()), static_cast<size_type>( PyBytes_Size(ptr()) ) ); } | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
class Char: public Object | |
{ | |
public: | |
COMMON5( Char, Object, _Unicode_Check(pyob) && PySequence_Length(pyob) == 1 ) | |
Char( int v ) : Object{ PyUnicode_FromOrdinal( v ) , true } { validate(); } | |
Char( Py_UNICODE v ) : Object{ PyUnicode_FromOrdinal( v ) , true } { validate(); } | |
Char( const unicodestring& v ) : Object{ PyUnicode_FromUnicode(const_cast<Py_UNICODE*>(v.data()),1 ) , true } { validate(); } | |
Char& operator=( const unicodestring& v ) { set( PyUnicode_FromUnicode( const_cast<Py_UNICODE*>( v.data() ) , 1 ), true ); return *this; } | |
Char& operator=( int v_ ) { Py_UNICODE v( v_ ); set( PyUnicode_FromUnicode(& v , 1 ), true ); return *this; } | |
Char& operator=( Py_UNICODE v ) { set( PyUnicode_FromUnicode(& v , 1 ), true ); return *this; } | |
// Conversion | |
operator String() const; | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
/* [Taken from Pythons's unicode.h] | |
Many of these APIs take two arguments encoding and errors. These | |
parameters encoding and errors have the same semantics as the ones | |
of the builtin unicode() API. | |
Setting encoding to NULL causes the default encoding to be used. | |
Error handling is set by errors which may also be set to NULL | |
meaning to use the default handling defined for the codec. Default | |
error handling for all builtin codecs is "strict" (ValueErrors are | |
raised). | |
The codecs all use a similar interface. Only deviation from the | |
generic ones are documented. | |
*/ | |
class String: public SeqBase<Char> | |
{ | |
public: | |
COMMON5( String, SeqBase<Char>, _Unicode_Check(pyob) ) | |
String( ) : SeqBase<Char>{ PyUnicode_FromString( "" ) } { validate(); } | |
String( const char* latin1 ) : SeqBase<Char>{ PyUnicode_FromString( latin1 ) } { validate(); } | |
String( const std::string& latin1 ) : SeqBase<Char>{ PyUnicode_FromStringAndSize( latin1.c_str(), latin1.size() ) } { validate(); } | |
String( const char* latin1 , Py_ssize_t size ) : SeqBase<Char>{ PyUnicode_FromStringAndSize( latin1, size ) } { validate(); } | |
String( const std::string& s , const char* enc, const char* err=nullptr ) : SeqBase<Char>{ PyUnicode_Decode( s.c_str(), s.size() , enc, err ) , true } { validate(); } | |
String( const char* s , const char* enc, const char* err=nullptr ) : SeqBase<Char>{ PyUnicode_Decode( s , strlen(s) , enc, err ) , true } { validate(); } | |
String( const char* s, Py_ssize_t size, const char* enc, const char* err=nullptr ) : SeqBase<Char>{ PyUnicode_Decode( s , size , enc, err ) , true } { validate(); } | |
String( const Py_UNICODE* s , int length ) : SeqBase<Char>{ PyUnicode_FromUnicode( s , length ) , true } { validate(); } | |
size_type capacity() const override { return max_size(); } | |
String& operator=( const unicodestring& v ) { | |
set( | |
PyUnicode_FromUnicode( const_cast<Py_UNICODE*>(v.data()), static_cast<int>(v.length()) ) | |
, true | |
); | |
return *this; | |
} | |
// Encode | |
Bytes encode( const char *encoding , const char *error="strict" ) const { return Bytes{ PyUnicode_AsEncodedString(ptr(),encoding,error), true }; } | |
std::string as_std_string( const char *encoding=nullptr, const char *error="strict" ) const { return Bytes{ encode(encoding,error) }.as_std_string(); } | |
operator std::string() const { return as_std_string(); } // use default encoding | |
explicit operator const char*() const { return as_std_string().c_str(); } | |
size_type size() const override { return static_cast<size_type>(PyUnicode_GET_SIZE(ptr())); } // Queries | |
const Py_UNICODE* unicode_data() const { return PyUnicode_AS_UNICODE(ptr()); } | |
unicodestring as_unicodestring() const { return unicodestring( unicode_data(), size() ); } | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
class Tuple: public Sequence | |
{ | |
public: | |
// Constructor | |
COMMON5( Tuple, Sequence, _Tuple_Check(pyob) ) | |
Tuple getSlice( int i, int j ) const { return Tuple{ PySequence_GetSlice(ptr(),i,j), true }; } | |
// New tuple of a given size | |
explicit Tuple( int size = 0 ) | |
{ | |
set( PyTuple_New(size), true ); | |
validate(); | |
for( int32_t i=0; i < size; i++ ) | |
if( PyTuple_SetItem( ptr(), i, new_reference_to(_None()) ) == -1 ) | |
throw Exception{}; | |
} | |
// Tuple from any sequence | |
explicit Tuple( const Sequence& s ) | |
{ | |
//int32_t limit( int32_t(s.length()) ); | |
int32_t limit{(int32_t)s.length()}; | |
set( PyTuple_New(limit), true ); | |
validate(); | |
for( auto i=0; i < limit; i++ ) | |
if( PyTuple_SetItem( ptr(), i, new_reference_to(s[i]) ) == -1 ) | |
throw Exception{}; | |
} | |
virtual void setItem( int32_t index, const Object& ob ) | |
{ | |
// note PyTuple_SetItem is a thief... | |
if( PyTuple_SetItem( ptr(), index, new_reference_to(ob) ) == -1 ) | |
throw Exception{}; | |
} | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
class TupleN: public Tuple | |
{ | |
public: | |
template <typename ... Types> TupleN(Types&& ... args) | |
: Tuple{ sizeof...(args) } | |
{ | |
setItems(0, std::forward<Types>(args)...); | |
} | |
virtual ~TupleN() | |
{ } | |
private: | |
template <typename Car, typename... Cdr> | |
void setItems(int idx, Car&& car, Cdr&&... cdr) | |
{ | |
setItem(idx, std::forward<Car>(car)); | |
setItems(idx + 1, std::forward<Cdr>(cdr)...); | |
} | |
void setItems(int) // recursion terminator | |
{ } | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
class List: public Sequence | |
{ | |
public: | |
// Constructor | |
COMMON5( List, Sequence, _List_Check(pyob) ) | |
// Creation at a fixed size | |
List( int size=0 ) | |
{ | |
set( PyList_New(size), true ); | |
validate(); | |
for( int32_t i=0; i < size; i++ ) | |
if( PyList_SetItem( ptr(), i, new_reference_to(_None()) ) == -1 ) | |
throw Exception{}; | |
} | |
// List from a sequence | |
List( const Sequence& s ) | |
: Sequence{} | |
{ | |
int n =(int)s.length(); | |
set( PyList_New(n), true ); | |
validate(); | |
for( int32_t 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(); } | |
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.ptr() ) == -1 ) throw Exception{}; } | |
void append( const Object& ob ) { if( PyList_Append ( ptr(), ob.ptr() ) == -1 ) throw Exception{}; } | |
void insert( int i, const Object& ob ) { if( PyList_Insert ( ptr(), i, ob.ptr() ) == -1 ) throw Exception{}; } | |
void sort( ) { if( PyList_Sort ( ptr() ) == -1 ) throw Exception{}; } | |
void reverse( ) { if( PyList_Reverse ( ptr() ) == -1 ) throw Exception{}; } | |
void extend( const Object& ob ) { setSlice( (int)size(), (int)size(), ob ); } // π fixed type-conv warning | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
// Mappings | |
//#if 0 | |
// // TMM: now for proxy | |
// template< typename T > | |
// bool operator==( const proxy& left, const proxy& right ) | |
// { | |
// return true; // NOT completed. | |
// } | |
// | |
// template< typename T > | |
// bool operator!=( const proxy& left, const proxy& right ) | |
// { | |
// return true; // not completed. | |
// } | |
//#endif | |
template<TEMPLATE_TYPENAME T> | |
class MapBase: public Object | |
{ | |
private: | |
class Proxy | |
{ | |
protected: | |
MapBase<T>& map; // the map | |
Object key; // item key | |
T the_item; | |
public: | |
Proxy( MapBase<T>& m, const std::string& k ) : map{m}, key{String{k}} , the_item{ map.hasKey(key) ? map.getItem(key) : T{} } { } | |
Proxy( MapBase<T>& m, const Object& k ) : map{m}, key{k} , the_item{ map.hasKey(key) ? map.getItem(key) : T{} } { } | |
virtual ~Proxy() | |
{} | |
// rvalue | |
operator T() const { return the_item; } | |
// MapBase<T> stuff | |
// lvalue | |
Proxy& operator=( const Proxy& other ) { if( this != &other ){ map.setItem( key, the_item = other.the_item ); } return *this; } | |
Proxy& operator=( const T& ob ) { map.setItem( key, the_item = ob ); return *this; } | |
EXPOSE( PyObject*, ptr ) // forward everything else to the item | |
EXPOSE( int , reference_count ) | |
EXPOSE( Type , type ) | |
EXPOSE( String , str ) | |
EXPOSE( String , repr ) | |
EXPOSE( long , hashValue ) | |
EXPOSE( bool , isCallable ) | |
EXPOSE( bool , isInstance ) | |
EXPOSE( bool , isList ) | |
EXPOSE( bool , isMapping ) | |
EXPOSE( bool , isNumeric ) | |
EXPOSE( bool , isSequence ) | |
EXPOSE( bool , isTrue ) | |
EXPOSE( bool , isTuple ) | |
EXPOSE( bool , isString ) | |
EXPOSE1( bool , isType , const Type& ) | |
EXPOSE1( bool , hasAttr , const std::string& ) | |
EXPOSE1( Object , getAttr , const std::string& ) | |
EXPOSE1( Object , getItem , const Object& ) | |
// 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 Proxy | |
protected: | |
explicit MapBase<T>() | |
{ } | |
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 Proxy data_type; | |
typedef std::pair< const T , T > value_type; | |
typedef std::pair< const T , Proxy > reference; | |
typedef const std::pair< const T , const T > const_reference; | |
typedef std::pair< const T , Proxy > pointer; | |
COMMON5( MapBase<T>, Object, PyMapping_Check(pyob) ) | |
void clear() { for( const auto& i : keys() ) delItem(i); } // Clear -- PyMapping Clear is missing | |
// Element Access | |
T operator[]( const std::string& key ) const { return getItem( key );} | |
T operator[]( const Object& key ) const { return getItem( key );} | |
Proxy operator[]( const std::string& key ) { return Proxy{ *this, key };} | |
Proxy operator[]( const Object& key ) { return Proxy{ *this, key };} | |
Proxy operator[]( const char* key ) { return Proxy{ *this, key };} | |
bool hasKey( const std::string& s ) const { return PyMapping_HasKeyString( ptr(), const_cast<char*>(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<char*>(s.c_str()) ) ) };} | |
T getItem( const Object& s ) const { return T{ asObject( PyObject_GetItem ( ptr(), s.ptr() ) ) };} | |
void delItem( const std::string& s ) { if( PyMapping_DelItemString( ptr(), const_cast<char*>(s.c_str()) ) == -1 ) throw Exception{};} | |
void delItem( const Object& s ) { if( PyMapping_DelItem ( ptr(), s.ptr() ) == -1 ) throw Exception{};} | |
// Queries | |
List keys() const { return List{ PyMapping_Keys ( ptr() ), true }; } | |
List values() const { return List{ PyMapping_Values( ptr() ), true }; } // each returned item is a (key, value) pair | |
List items() const { return List{ PyMapping_Items ( ptr() ), true }; } | |
int length() const { return (int)PyMapping_Length(ptr()); } | |
virtual size_type size() const { return PyMapping_Length(ptr()); } | |
virtual void setItem( const char* s , const Object& ob ) { if( PyMapping_SetItemString( ptr(), const_cast<char*>(s) , ob.ptr() ) == -1 ) throw Exception{};} | |
virtual void setItem( const std::string& s , const Object& ob ) { if( PyMapping_SetItemString( ptr(), const_cast<char*>(s.c_str()), ob.ptr() ) == -1 ) throw Exception{};} | |
virtual void setItem( const Object& s , const Object& ob ) { if( PyObject_SetItem ( ptr(), s.ptr() , ob.ptr() ) == -1 ) throw Exception{};} | |
template<bool isConst> //typename I, typename M> | |
class iterator_base | |
{ | |
protected: | |
using M = typename std::conditional< isConst, const MapBase<T>, MapBase<T> >::type; | |
using P = typename std::conditional< isConst, Proxy , T >::type; | |
using I = iterator_base<isConst>; // iterator or const_iterator | |
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, P> pointer; | |
typedef const std::pair<const T, P> reference; | |
friend class MapBase<T>; | |
M* map; | |
List keys; // for iterating over the map | |
int pos; // index into the keys | |
public: | |
iterator_base( ) : map{0} , keys{} , pos{0} { } | |
iterator_base( MapBase<T>* m, bool end=false ) : map{m} , keys{m->keys()} , pos{ end ? (int)keys.length() : 0 } { } | |
iterator_base( const I& o ) : map{o.map}, keys{o.keys} , pos{o.pos} { } | |
iterator_base( M* m, List k, int p ) : map{m} , keys{k} , pos{p} { } | |
~iterator_base() { }; | |
/* | |
This consumer code... | |
Dict::iterator it{ dict.begin() }; | |
test_assert( "STL ad hoc", true, it != dict.end() ); | |
while( it != dict.end() ) | |
{ | |
Dict::value_type vt{ *it }; | |
^^ problem is here | |
... Causes an error below, on 'Object key{ keys[pos] };' | |
*/ | |
reference operator*() | |
{ | |
/* | |
keys is a List, and List : Sequence, a.k.a. SeqBase<Object>, and SeqBase<T> uses a proxy | |
so that 'cout<<s[5]' and 's[5]="pudding"' both work. | |
So keys[foo] is actually returning a SeqBase<Object>.Proxy object, which needs to get resolved to an Object | |
shouldn't initialising an Object with a Proxy attempt to get the Proxy to convert itself to an Object? | |
Looking into SeqBase<Object>.Proxy, I find: | |
// r-value | |
operator T() const { return the_item; } | |
// l-value | |
Proxy& operator=( const Proxy& rhs ) { the_item = rhs.the_item; s.setItem( index, the_item ); return *this; } | |
Proxy& operator=( const T& ob ) { the_item = ob; s.setItem( index, ob ); return *this; } | |
r-value overload is the one that should be getting invoked, because T in this case is Object. | |
The below 'Object key{ keys[pos] };' is attempting to initialise an Object with a SeqBase<Object>.Proxy object | |
This should be invoking that r-value overload... | |
SeqBase<Object>'s Proxy subclass is private, but I don't see that matters. And switching it to public makes a difference. | |
*/ | |
//Object key{ keys[pos] }; | |
Object key{ static_cast<Object>( keys[pos] ) }; | |
/* doing: 'key{ static_cast<Object>( keys[pos] ) };' fixes it, but ARGH. This was working | |
until a recent minor revision. Don't know what I've done to trip it. | |
The original code was commented to say "sometimes you may bump into this problem and need to explicitly cast" | |
*/ | |
return std::make_pair( key, Proxy{ *map, key } ); // note that this is MapBase<T>'s Proxy class now, not SeqBase<...>'s Proxy class | |
} | |
I& operator=( const I& other ) | |
{ | |
if( this != &other ) { | |
map = other.map; | |
keys = other.keys; | |
pos = other.pos; | |
} | |
return *this; | |
} | |
bool eql( const I& other ) const { return map->ptr() == other.map->ptr() && pos == other.pos; } | |
bool neq( const I& other ) const { return map->ptr() != other.map->ptr() || pos != other.pos; } | |
// pointer operator->() { | |
// return ; | |
// } | |
// prefix | |
I& operator++() { pos++; return *this; } | |
I& operator--() { pos--; return *this; } | |
// postfix | |
I operator++( int ) { return I{ map, keys, pos++ }; } | |
I operator--( int ) { return I{ map, keys, pos-- }; } | |
std::string diagnose() const { | |
std::stringstream oss; | |
oss << "iterator diagnosis " << map << ", " << pos; | |
return oss.str(); | |
} | |
}; | |
typedef iterator_base<false> iterator; | |
typedef iterator_base<true> const_iterator; | |
iterator begin() { return iterator{ this, false }; } | |
iterator end() { return iterator{ this, true }; } | |
const_iterator begin() const { return const_iterator{ this, keys(), 0 }; } | |
const_iterator end() const { return const_iterator{ this, keys(), length() }; } | |
}; | |
typedef MapBase<Object> Mapping; | |
template <typename T> bool operator ==( const typename MapBase<T>::iterator& l, const typename MapBase<T>::iterator& r ); | |
template <typename T> bool operator !=( const typename MapBase<T>::iterator& l, const typename MapBase<T>::iterator& r ); | |
template <typename T> bool operator ==( const typename MapBase<T>::const_iterator& l, const typename MapBase<T>::const_iterator& r ); | |
template <typename T> bool operator !=( const typename MapBase<T>::const_iterator& l, const typename MapBase<T>::const_iterator& r ); | |
#define OP( T, op ) \ | |
extern bool operator op ( const T::iterator& l, const T::iterator& r ); \ | |
extern bool operator op ( const T::const_iterator& l, const T::const_iterator& r ); | |
OP( Mapping, == ) | |
OP( Mapping, != ) | |
#undef OP | |
// end of MapBase<T> | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
// ================================================== | |
// class Dict | |
class Dict: public Mapping | |
{ | |
public: | |
COMMON5( Dict, Mapping, _Dict_Check(pyob) ) | |
// Creation | |
Dict() { set(PyDict_New(),true); validate(); } | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
class Callable: public Object | |
{ | |
public: | |
COMMON5( Callable, Object, PyCallable_Check(pyob) ) | |
explicit Callable() : Object{} { } | |
// Call | |
Object apply( PyObject* pargs = 0 ) const { return apply( Tuple{pargs} ); } | |
Object apply( const Tuple& args ) const { return asObject( PyObject_CallObject ( ptr(), args.ptr() ) ); } | |
Object apply( const Tuple& args, const Dict& kw ) const { return asObject( PyEval_CallObjectWithKeywords( ptr(), args.ptr(), kw.ptr() ) ); } | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
class Module: public Object | |
{ | |
public: | |
COMMON5( Module, Object, true ) | |
// Construct from module name | |
explicit Module( const std::string& s ) : Object{} { | |
PyObject* m = PyImport_AddModule( const_cast<char *>(s.c_str()) ); | |
set( m, false ); | |
validate(); | |
} | |
Dict getDict() const { return Dict{ PyModule_GetDict(ptr()) }; } // Caution -- PyModule_GetDict returns borrowed reference! | |
}; | |
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = | |
// Call function helper -- these have to go after the definition of Callable, as we use one of Callable's methods (apply) | |
inline Object Object::callMemberFunction( const std::string& function_name ) const { | |
DBG_LINE( "% Object::callMemberFunction" << function_name << " % " ); | |
Callable target{ getAttr(function_name) }; | |
return target.apply( /* Tuple{0} */ ); | |
} | |
inline Object Object::callMemberFunction( const std::string& function_name, const Tuple& args ) const { | |
DBG_LINE( "% % Object::callMemberFunction" << function_name << " % % " ); | |
Callable target{ getAttr(function_name) }; | |
return target.apply( args ); | |
} | |
inline Object Object::callMemberFunction( const std::string& function_name, const Tuple& args, const Dict& kw ) const { | |
DBG_LINE( "% % % Object::callMemberFunction" << function_name << " % % " ); | |
Callable target{ getAttr(function_name) }; | |
return target.apply( args, kw ); | |
} | |
// Numeric interface | |
inline Object operator+( const Object& a ) { return asObject( PyNumber_Positive(a.ptr()) );} | |
inline Object operator-( const Object& a ) { return asObject( PyNumber_Negative(a.ptr()) );} | |
inline Object abs ( const Object& a ) { return asObject( PyNumber_Absolute(a.ptr()) );} | |
#define OP( op, func, param_a, param_b, expr_A, expr_B ) \ | |
inline Object operator op( param_a, param_b ) \ | |
{ \ | |
return asObject( func(expr_A,expr_B) ); \ | |
} | |
#define OPS( ... ) \ | |
OP( +, PyNumber_Add , __VA_ARGS__ ) \ | |
OP( -, PyNumber_Subtract , __VA_ARGS__ ) \ | |
OP( *, PyNumber_Multiply , __VA_ARGS__ ) \ | |
OP( /, PyNumber_TrueDivide , __VA_ARGS__ ) \ | |
OP( %, PyNumber_Remainder , __VA_ARGS__ ) | |
#define BI_( param_a, param_b, expr_A, expr_B ) \ | |
OPS( param_a, param_b, expr_A, expr_B ) \ | |
OPS( param_b, param_a, expr_B, expr_A ) | |
//------------------------------------------------------------ | |
// operator + | |
OP( +, PyNumber_Add, long j , const Object& b, Long{j}.ptr(), b.ptr() ) | |
OP( +, PyNumber_Add, const Object& b, long j , b.ptr() , Long{j}.ptr() ) | |
OPS( const Object& a, const Object& b , a.ptr(), b.ptr() ) | |
BI_( const Object& a, int b , a.ptr(), Long{b}.ptr() ) | |
BI_( const Object& a, double b , a.ptr(), Float{b}.ptr() ) | |
#undef BI_ | |
#undef UNI | |
#undef OPS | |
#undef OP | |
template<TEMPLATE_TYPENAME T> String SeqBase<T>::Proxy::str() const { return the_item.str(); } | |
template<TEMPLATE_TYPENAME T> String SeqBase<T>::Proxy::repr() const { return the_item.repr(); } | |
} // namespace Py |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment