Skip to content

Instantly share code, notes, and snippets.

Created November 23, 2014 18:20
Show Gist options
  • Save anonymous/87246729a9860db9c87c to your computer and use it in GitHub Desktop.
Save anonymous/87246729a9860db9c87c to your computer and use it in GitHub Desktop.
#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