In the example below, the module only has a class extension:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | """My 3rd module with expy-cxpy.
@head:
#define VERSION "0.1.0"
"""
#File name: module3.py
from expy import *
class mate(public):
"""mate: a class for a buddy."""
_age = ifield(int, acc=private, doc='the age')
name = ifield(str, flag='RO', doc='the name')
version = cfield(rawtype(str, 'VERSION')) #class field/member
@imethod(void)
def rename(me, name=str):
"""change the name."""
return """{
if(me->name) free(me->name);
int nn = strlen(name)+1;
me->name = malloc(sizeof(char)*nn);
if(me->name) strcpy(me->name, name);
else PyErr_NoMemory();
}"""
def get_age(me, closure=rawtype('void*')):
return """{
return Py_BuildValue("l", me->_age);//Py_INCREF
}"""
@throws('!PyInt_CheckExact(v)', TypeError, "Not an Integer!")
@throws('PyInt_AsLong(v)<=0', ValueError, "Not Positive!")
def set_age(me, v, closure=rawtype('void*')):
return """{
int age = PyInt_AsLong(v);
me->_age = age;
return 0; //success
}"""
age = property(get_age, set_age, doc="the age.")
@function(Int)
def refcount(a):
return 'a->ob_refcnt'
#register the type yourself.
from expy import _typeinfo_
_typeinfo_(mate, 'module3_mate*').register()
|
The explanation of this example follows.
Line 6 start the definition of your extension type: the main thing you should notice is that it extends public, which is a class defined in the expy module. This basically says that this class should be totally visible in Python.
Line 8-9 declares two instance members, by using the ifield (i.e.: instance field) class provided in the expy module. You provide the type, the access (such as public or private), the document string, and you can also provide the flag (must be a string), which corresponds to the flags field of the PyMemberDef structure in Python C API. In Line 8, the _age member is of ‘int’ type, and it is a private member. In Line 9, the name member is of ‘str’ type (which in C it corresponds to ‘char*’), and is ‘readonly’, as indicated by the “RO” flag. The following flag constants are defined in structmember.h; they may be combined using bitwise-OR.
Constant | Meaning |
---|---|
READONLY | Never writable. |
RO | Shorthand for READONLY. |
READ_RESTRICTED | Not readable in restricted mode. |
WRITE_RESTRICTED | Not writable in restricted mode. |
RESTRICTED | Not readable or writable in restricted mode. |
An interesting advantage of using the ifield() to build fields/members at runtime is that any attribute defined this way can have an associated doc string simply by providing the text in the constructor. An application can use the introspection API to retrieve the descriptor from the class object, and get the doc string using its __doc__ attribute. One more good thing about instance members:
The order in which these fields appear will be
preserved in the generated object structure.
Class members/attributes of extension types are read only, thus they are ideal for providing constants/data to users. Line 10 defines a class member/field, which is the version of the class. Instead of using an ‘ifield’ constructor, you use a ‘cfield’ (class field) constructor, which takes only one parameter, namely, the value you want to store. This parameter is specified in the same way as default values to your function arguments. This class field is accessed by mate.version, once you have built and imported the module.
Lines 19-29 defines a method named “rename” for the extension type. In Line 19, we use the decorator imethod to declare that this is an “instance method”, whose return type is ‘void’. The first argument will be the instance, the next argument is the new name, of type ‘str’ (which translated in C is ‘char*’). It is very similar to how we defined functions for modules. The name mangling is done by appending the class name to the function name, connected by an underscore ‘_’.
Lines 12-17 defines a special method “__init__”, which initializes an instance. Those special methods does not need any decoration, they are automatically detected, because their return type, access type, etc, are known in advance. Similar to other methods, you can use @throws decorators. Unlike other special methods, you can specify arbitrary set of arguments to the __init__ method, just like you do with pure python classes. Other special methods (in the object protocal) that are recognized – more can be added if anything is missing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | special_methods={
#returns: int, with 0:success, -1:failure
'__init__':{'cat':'imethod', 'ret':int, 'acc':public},
#signature: def __new__(cls, args, kwds):
'__new__':{'cat':'cmethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __del__(me):
'__del__':{'cat':'imethod', 'ret':void, 'acc':public, 'nowrap':True},
#signature: def __call__(cls, args, kwds):
'__call__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __cmp__(me, u[=lambda:mytype]): [optional]
'__cmp__':{'cat':'imethod', 'ret':int, 'acc':public, 'nowrap':True},
#signature: def __getattr__(me, name=str):
'__getattr__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __setattr__(me, name=str, attr=object):
'__setattr__':{'cat':'imethod', 'ret':int, 'acc':public, 'nowrap':True},
#signature: def __getattro__(me, name):
'__getattro__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __setattro__(me, name, attr):
'__setattro__':{'cat':'imethod', 'ret':int, 'acc':public, 'nowrap':True},
#signature: def __hash__(me):
'__hash__':{'cat':'imethod', 'ret':long, 'acc':public, 'nowrap':True},
#signature: def __print__(me, f=raw_type('FILE*'), flags=int):
'__print__':{'cat':'imethod', 'ret':int, 'acc':public, 'nowrap':True},
#signature: def __repr__(me):
'__repr__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __str__(me):
'__str__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __iter__(me):
'__iter__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
#signature: def __next__(me):
'__next__':{'cat':'imethod', 'ret':object, 'acc':public, 'nowrap':True},
}
|
You may refer to the Python C API to learn more about those special methods, and how to implement them.
Lines 31-51 defines a property named ‘age’. Tow functions ‘get_age’ and ‘set_age’ are defined, without decoration. They must conform to that signature so that the generated C function is a well defined getter and setter function for a property. Similar to other methods, you can use @throws decorators. The main feature provided here is to throw an exception if the age argument is of the wrong type or value.
Now we use the following setup code to generate and compile this module.
1 2 3 4 5 6 7 8 9 10 11 12 | # File: module3_setup.py
from distutils.core import setup, Extension
import sys #To find the 'expy' and 'cxpy'
sys.path.insert(1, '../src')
import cxpy, expy_module3 as modu
cxpy.generate(modu) #generate the C code
# Now, finally, define that module!
modext = Extension( modu.__name__,
sources = [modu.__name__+'.c'])
setup(name = modu.__name__, version = '0.1.0',
description = 'My third module.',
ext_modules = [modext])
|
And here is the generated code with the header file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | //@head:begin
#define VERSION "0.1.0"
//@head:end
#ifndef Py_TYPE //Python 2.5
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#endif
/*--- class mate ---*/
typedef struct{
PyObject_HEAD
int _age;
char* name;
} mate;
static PyObject* module3_refcount_wrap(
PyObject* _NOUSE,
PyObject* _args_
);
void mate_rename(mate* me, const char* name);
static PyObject* mate_rename_wrap(
mate* me,
PyObject* _args_
);
int mate_set_age(mate* me, PyObject* v, void* closure);
static int mate_set_age_wrap(mate* me, PyObject* v, void* closure);
static PyObject* mate_get_age(mate* me, void* closure);
#define mate_Check(op) PyObject_TypeCheck(op, &mateType)
#define mate_CheckExact(op) (Py_TYPE(op)==&mateType)
#define mate_NEW() PyObject_NEW(mate, &mateType) //NEW reference!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | #include <Python.h>
#include "structmember.h"
#include "module3.h"
static PyTypeObject mateType;
/**======== MODULE IMPLEMENTATION ========**/
static PyObject* module3_refcount_wrap(
PyObject* _NOUSE,
PyObject* _args_
){
PyObject* a;
//no keywords support
if(!PyArg_ParseTuple(
_args_, "O"
, &a)) return 0;
/*****Argument Exception Handling*****/
unsigned int cxpyret = PyErr_Occurred()?(unsigned int)0:
a->ob_refcnt;
/*****Return Value Exception Handling*****/
if(PyErr_Occurred()) return 0;
return Py_BuildValue("I", cxpyret);
}
/*--- class mate ---*/
void mate_rename(mate* me, const char* name){
if(me->name) free(me->name);
int nn = strlen(name)+1;
me->name = malloc(sizeof(char)*nn);
if(me->name) strcpy(me->name, name);
else PyErr_NoMemory();
}
static PyObject* mate_rename_wrap(
mate* me,
PyObject* _args_
){
const char* name;
//no keywords support
if(!PyArg_ParseTuple(
_args_, "s"
, &name)) return 0;
/*****Argument Exception Handling*****/
if(!PyErr_Occurred()) mate_rename(me, name);
/*****Return Value Exception Handling*****/
if(PyErr_Occurred()) return 0;
return Py_BuildValue("");
}
int mate_set_age(mate* me, PyObject* v, void* closure){
int age = PyInt_AsLong(v);
me->_age = age;
return 0; //success
}
static int mate_set_age_wrap(mate* me, PyObject* v, void* closure){
/*****Argument Exception Handling*****/
if(!PyErr_Occurred() && (!PyInt_CheckExact(v))){ // throws
PyErr_SetString(PyExc_TypeError, "Not an Integer!");
}
if(!PyErr_Occurred() && (PyInt_AsLong(v)<=0)){ // throws
PyErr_SetString(PyExc_ValueError, "Not Positive!");
}
int cxpyret = PyErr_Occurred()?(int)1:
mate_set_age(me, v, closure);
/*****Return Value Exception Handling*****/
return cxpyret; //don't need PyObject*!
}
static PyObject* mate_get_age(mate* me, void* closure){
return Py_BuildValue("l", me->_age);//Py_INCREF
}
static struct PyMethodDef mateMeths[] = {
{ "rename",
(PyCFunction)mate_rename_wrap,
METH_VARARGS,
"change the name."
},
{NULL} /* sentinel */ };
static PyMemberDef mateFields[] = {
{"name", T_STRING, offsetof(mate, name), RO,
"the name"},
{NULL} /* Sentinel */
};
/**Type mate is not numeric.**/
/**Type mate is not mapping.**/
/**Type mate is not sequence.**/
static PyGetSetDef mateProps[] = {
{"age", (getter)mate_get_age, (setter)mate_set_age_wrap,
"the age.", NULL},
{NULL} /* Sentinel */
};
static PyTypeObject mateType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"module3.mate", /*tp_name*/
sizeof(mate), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)0, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash */
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)0,/*tp_getattro*/
(setattrofunc)0,/*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"mate: a class for a buddy.", /* tp_doc */
(traverseproc)0, /* tp_traverse */
(inquiry)0, /* tp_clear */
(richcmpfunc)0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)0, /* tp_iter */
(iternextfunc)0, /* tp_iternext */
mateMeths, /* tp_methods */
mateFields, /* tp_members */
mateProps, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
(descrgetfunc)0, /* tp_descr_get */
(descrsetfunc)0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};/*http://docs.python.org/c-api/typeobj.html*/
static struct PyMethodDef module3Funcs[] = {
{ "refcount",
(PyCFunction)module3_refcount_wrap,
METH_VARARGS,
""
},
{NULL} /* sentinel */ };
PyMODINIT_FUNC
initmodule3(void) { //must be: init<module>
PyObject *mod;
//Initialize the type table for "mate".
if(mateType.tp_new == 0)
mateType.tp_new = PyType_GenericNew;
if (PyType_Ready(&mateType) < 0) return;
mod = Py_InitModule3("module3", module3Funcs,
"My 3rd module with expy-cxpy.");
if (mod==0) return;
Py_INCREF(&mateType);
PyModule_AddObject(mod, "mate", (PyObject *)&mateType);
/* class fields for class mate: */
PyDict_SetItemString(mateType.tp_dict, "version",
Py_BuildValue("s",VERSION));
/*End class mate.*/
/* Custom Exceptions */
/* Global Members */
}
|
Note that for each extention type you create, you will get three macros for free: <type>_Check, <type>_CheckExact, and <type>_NEW. Start a Python session and test what we have got here:
ylan@montop:~/expy/Doc$ python module3_setup.py build
ylan@montop:~/expy/Doc$ cd build/lib.linux-i686-2.5/
ylan@montop:~/expy/Doc/build/lib.linux-i686-2.5$ python
Python 2.5.2 (r252:60911, Jul 31 2008, 17:28:52)
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from module3 import mate
>>> x = mate('John', 23)
>>> x.name
'John'
>>> x.age
23
>>> x.age = -23
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Must be Positive
>>> x.age = x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Must be an Integer
>>> x.age
23
>>> x.rename('Job')
>>> x.name
'Job'
If you have implemented your methods carefully, you can declare your extension types as a base type, so that somebody can extend it in pure Python. To declare that your type can serve as a base type, you simply do this:
class mybase(public, basetype):
"""this extension type can be a base type."""
def ameth(me, you):
...
For more information on how to implement a base type, please refer to Python C API.
Python allows one to create types/classes that can act like numbers, by implementing the numeric protocol. You can create a numeric extension type in similar ways as you create a pure python numeric type with expy-cxpy. There are, however, two distinct categories of numeric extension types in Python C API. Category 1: the special numeric type (by indicating that the second argument is also of the same type, see example below), whose numeric operations are only defined between an object of the defining type and an object of the same type; Category 2: the generic numeric type, whose numeric operations are defined between an object of the defining type and an arbitrary object (so the second argument is of the default ‘object’ type)). When you implement your numeric type, don’t mix up these two categories, otherwise EXPY will complain.
The following example is for Category 1. A new extension type ‘counter’ is created, it has an integer number, and a name. We only defined the __add__ operator, when two counters added together, not only their numbers are added, but also their names are merged.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | """Numeric extension type (Cat 1) with expy-cxpy."""
#File name: module4.py
from expy import *
class counter(public):
"""counter: a simple numeric class."""
num = ifield(int, doc='the counter')
name = ifield(str, flag='RO', doc='the name')
@throws(False, code="if($ret==-1) PyErr_NoMemory();")
def __init__(me, name=str, num=0):
return """{
me->num = num;
num = strlen(name)+1;
me->name = malloc(sizeof(char)*num);
if (!me->name) return -1; //fail
strcpy(me->name, name);
return 0; //success! (-1 for failure)
}"""
@throws('!counter_CheckExact(u)', TypeError,
"Second operand is not a counter!")
def __add__(me, u=lambda:counter):
"""add counters, and merge names."""
return """{
counter* sum = counter_NEW();//Py_INCREF
sum->num = me->num + u->num;
int lm = strlen(me->name);
sum->name = malloc(sizeof(char)*(lm+strlen(u->name)+2));
if(!sum->name){
Py_DECREF(sum);
return 0; //no memory
}
strcpy(sum->name, me->name);
strcpy(sum->name+lm, "&");
strcpy(sum->name+lm+1, u->name);
return (PyObject*)sum;
}"""
|
To indicate that the second argument to __add__ function must be of the same ‘counter’ type, we use ‘lambda:counter’ to specify its type. It might be more appealing to simply use something like ‘__add__(me, u=counter)’, but since ‘counter’ is not defined yet, Python compiler would complain about this and abort, so the ‘lambda’ function is used here to delay the evaluation till code generation. To enforce the type restriction, we can add a @throws decorator to check on its actual type. Here is the setup script:
1 2 3 4 5 6 7 8 9 10 11 12 | # File: module4_setup.py
from distutils.core import setup, Extension
import sys #To find the 'expy' and 'cxpy'
sys.path.insert(1, '../src')
import cxpy, expy_module4 as modu
cxpy.generate(modu) #generate the C code
# Now, finally, define that module!
modext = Extension( modu.__name__,
sources = [modu.__name__+'.c'])
setup(name = modu.__name__, version = '0.1.0',
description = 'My third module.',
ext_modules = [modext])
|
And the generated C code is also included for your information:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /*--- class counter ---*/
typedef struct{
PyObject_HEAD
int num;
char* name;
} counter;
int counter___init__(counter* me, const char* name, int num);
static int counter___init___wrap(
counter* me,
PyObject* _args_,
PyObject* _kwds_
);
PyObject* counter___add__(counter* me, counter* u);
static PyObject* counter___add___wrap(counter* me, counter* u);
#define counter_Check(op) PyObject_TypeCheck(op, &counterType)
#define counter_CheckExact(op) (Py_TYPE(op)==&counterType)
#define counter_NEW() PyObject_NEW(counter, &counterType) //NEW reference!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | #include <Python.h>
#include "structmember.h"
#include "module4.h"
static PyTypeObject counterType;
/**======== MODULE IMPLEMENTATION ========**/
/*--- class counter ---*/
int counter___init__(counter* me, const char* name, int num){
me->num = num;
num = strlen(name)+1;
me->name = malloc(sizeof(char)*num);
if (!me->name) return -1; //fail
strcpy(me->name, name);
return 0; //success! (-1 for failure)
}
static int counter___init___wrap(
counter* me,
PyObject* _args_,
PyObject* _kwds_
){
const char* name;
int num=0;
static char* _pctrl[] ={"name", "num", 0};
if(!PyArg_ParseTupleAndKeywords(
_args_, _kwds_, "s|i",
_pctrl, &name, &num)) return -1;
/*****Argument Exception Handling*****/
int cxpyret = PyErr_Occurred()?(int)-1:
counter___init__(me, name, num);
/*****Return Value Exception Handling*****/
if(!PyErr_Occurred()){
if(cxpyret==-1) PyErr_NoMemory();
}
return cxpyret; //don't need PyObject*!
}
PyObject* counter___add__(counter* me, counter* u){
counter* sum = counter_NEW();//Py_INCREF
sum->num = me->num + u->num;
int lm = strlen(me->name);
sum->name = malloc(sizeof(char)*(lm+strlen(u->name)+2));
if(!sum->name){
Py_DECREF(sum);
return 0; //no memory
}
strcpy(sum->name, me->name);
strcpy(sum->name+lm, "&");
strcpy(sum->name+lm+1, u->name);
return (PyObject*)sum;
}
static PyObject* counter___add___wrap(counter* me, counter* u){
/*****Argument Exception Handling*****/
if(!PyErr_Occurred() && (!counter_CheckExact(u))){ // throws
PyErr_SetString(PyExc_TypeError, "Second operand is not a counter!");
}
PyObject* cxpyret = PyErr_Occurred()?(PyObject*)0:
counter___add__(me, u);
/*****Return Value Exception Handling*****/
if(PyErr_Occurred()) return 0;
//Direct return: had Py_INCREF???
return cxpyret;
}
static struct PyMethodDef counterMeths[] = {
{NULL} /* sentinel */ };
static PyMemberDef counterFields[] = {
{"num", T_INT, offsetof(counter, num), 0,
"the counter"},
{"name", T_STRING, offsetof(counter, name), RO,
"the name"},
{NULL} /* Sentinel */
};
static PyNumberMethods counterAsNum = {
(binaryfunc)counter___add___wrap, /*nb_add*/
(binaryfunc)0, /*nb_subtract*/
(binaryfunc)0, /*nb_multiply*/
(binaryfunc)0, /*nb_divide*/
(binaryfunc)0, /*nb_remainder*/
(binaryfunc)0, /*nb_divmod*/
(ternaryfunc)0, /*nb_power*/
(unaryfunc)0, /*nb_negative*/
(unaryfunc)0, /*nb_positive*/
(unaryfunc)0, /*nb_absolute*/
(inquiry)0, /*nb_nonzero*/
(unaryfunc)0, /*nb_invert*/
(binaryfunc)0, /*nb_lshift*/
(binaryfunc)0, /*nb_rshift*/
(binaryfunc)0, /*nb_and*/
(binaryfunc)0, /*nb_xor*/
(binaryfunc)0, /*nb_or*/
(coercion)0, /*nb_coerce*/
(unaryfunc)0, /*nb_int*/
(unaryfunc)0, /*nb_long*/
(unaryfunc)0, /*nb_float*/
(unaryfunc)0, /*nb_oct*/
(unaryfunc)0, /*nb_hex*/
/* Added in release 2.0 */
(binaryfunc)0, /*nb_inplace_add*/
(binaryfunc)0, /* nb_inplace_subtract */
(binaryfunc)0, /* nb_inplace_multiply */
(binaryfunc)0, /* nb_inplace_divide */
(binaryfunc)0, /* nb_inplace_remainder */
(ternaryfunc)0, /*nb_inplace_power */
(binaryfunc)0, /* nb_inplace_lshift */
(binaryfunc)0, /* nb_inplace_rshift */
(binaryfunc)0, /* nb_inplace_and */
(binaryfunc)0, /* nb_inplace_xor */
(binaryfunc)0, /* nb_inplace_or */
/* Added in release 2.2 */
(binaryfunc)0, /* nb_floor_divide */
(binaryfunc)0, /* nb_true_divide */
(binaryfunc)0, /* nb_inplace_floor_divide */
(binaryfunc)0, /* nb_inplace_true_divide */
/* Added in release 2.5 */
(unaryfunc)0, /* nb_index */
};
/**Type counter is not mapping.**/
/**Type counter is not sequence.**/
/* NO PROPS: counter */
static PyTypeObject counterType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"module4.counter", /*tp_name*/
sizeof(counter), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)0, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
&counterAsNum, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash */
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)0,/*tp_getattro*/
(setattrofunc)0,/*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"counter: a simple numeric class.", /* tp_doc */
(traverseproc)0, /* tp_traverse */
(inquiry)0, /* tp_clear */
(richcmpfunc)0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)0, /* tp_iter */
(iternextfunc)0, /* tp_iternext */
counterMeths, /* tp_methods */
counterFields, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
(descrgetfunc)0, /* tp_descr_get */
(descrsetfunc)0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)counter___init___wrap, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};/*http://docs.python.org/c-api/typeobj.html*/
static struct PyMethodDef module4Funcs[] = {
{NULL} /* sentinel */ };
PyMODINIT_FUNC
initmodule4(void) { //must be: init<module>
PyObject *mod;
//Initialize the type table for "counter".
if(counterType.tp_new == 0)
counterType.tp_new = PyType_GenericNew;
if (PyType_Ready(&counterType) < 0) return;
mod = Py_InitModule3("module4", module4Funcs,
"Numeric extension type (Cat 1) with expy-cxpy.");
if (mod==0) return;
Py_INCREF(&counterType);
PyModule_AddObject(mod, "counter", (PyObject *)&counterType);
/* class fields for class counter: */
/*End class counter.*/
/* Custom Exceptions */
/* Global Members */
}
|
And a testing session is invoked to illustrate the result:
ylan@montop:~/expy/Doc/build/lib.linux-i686-2.5$ python
>>> from module4 import *
>>> a = counter('VA', 20)
>>> b = counter('MD', 14)
>>> c = a+b
>>> c.name, c.num
('VA&MD', 34)
Defining numeric extension of Category 2 is similar, only that the second operand will be of PyObject type, so the prototype would be something like this:
def __add__(me, u):
"""The C prototype would be:
static PyObject* counter___add__(counter* me, PyObject* u);"""
And before you work on the argument ‘u’, you usually do type checking to ensure that it is of the right type. As there are quite a number of numeric methods, enlisting all of them here would be boring. Please refer to the cxpy.py source code, where you shall find all of them.
You can also implement the mapping protocol for your extension types. Below is a simple example, where our mapping type only help access some static integer array.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | """Mapping extension type with expy-cxpy.
@body:
#define MEMLEN 30
static int mapst[MEMLEN];
"""
#File name: module5.py
from expy import *
class statmap(public):
"""A Map type to access static array data."""
def __maplen__(me):
"""The length of the map."""
return """{ return MEMLEN; }"""
@throws('!PyInt_Check(key)', TypeError, 'key must be an integer')
def __mapget__(me, key):
return """{
int i = PyInt_AsLong(key);
if (i<0) i += MEMLEN;
if(i >= MEMLEN || i <0){
PyErr_SetString(PyExc_ValueError, "Index out of Range");
return NULL; //failed
}
return PyInt_FromLong(mapst[i]); //implicit Py_INCREF
}"""
def __mapset__(me, key, v):
return """{
if(!PyInt_Check(key)){
PyErr_SetString(PyExc_TypeError, "Key must be an Integer");
return 1; //failed
}
if(!PyInt_Check(v)){
PyErr_SetString(PyExc_TypeError, "Must be an Integer");
return 1; //failed
}
int i = PyInt_AsLong(key);
if (i<0) i += MEMLEN;
if(i >= MEMLEN || i <0){
PyErr_SetString(PyExc_ValueError, "Index out of Range");
return 1; //fail
}
mapst[i] = PyInt_AsLong(v);
return 0; //good
}"""
|
Note that the three special method names and the signature, which are different from the way you define a pure python mapping type. This is to avoid name conflicts with the sequence protocal (next). And here is the generated C code:
1 2 3 4 5 6 7 8 9 10 11 12 13 | /*--- class statmap ---*/
typedef struct{
PyObject_HEAD
} statmap;
static int statmap___mapset__(statmap* me, PyObject* key, PyObject* v);
PyObject* statmap___mapget__(statmap* me, PyObject* key);
static PyObject* statmap___mapget___wrap(statmap* me, PyObject* key);
static int statmap___maplen__(statmap* me);
#define statmap_Check(op) PyObject_TypeCheck(op, &statmapType)
#define statmap_CheckExact(op) (Py_TYPE(op)==&statmapType)
#define statmap_NEW() PyObject_NEW(statmap, &statmapType) //NEW reference!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | #include <Python.h>
#include "structmember.h"
#include "module5.h"
static PyTypeObject statmapType;
/**======== MODULE IMPLEMENTATION ========**/
//@body:begin
#define MEMLEN 30
static int mapst[MEMLEN];
//@body:end
/*--- class statmap ---*/
static int statmap___mapset__(statmap* me, PyObject* key, PyObject* v){
if(!PyInt_Check(key)){
PyErr_SetString(PyExc_TypeError, "Key must be an Integer");
return 1; //failed
}
if(!PyInt_Check(v)){
PyErr_SetString(PyExc_TypeError, "Must be an Integer");
return 1; //failed
}
int i = PyInt_AsLong(key);
if (i<0) i += MEMLEN;
if(i >= MEMLEN || i <0){
PyErr_SetString(PyExc_ValueError, "Index out of Range");
return 1; //fail
}
mapst[i] = PyInt_AsLong(v);
return 0; //good
}
PyObject* statmap___mapget__(statmap* me, PyObject* key){
int i = PyInt_AsLong(key);
if (i<0) i += MEMLEN;
if(i >= MEMLEN || i <0){
PyErr_SetString(PyExc_ValueError, "Index out of Range");
return NULL; //failed
}
return PyInt_FromLong(mapst[i]); //implicit Py_INCREF
}
static PyObject* statmap___mapget___wrap(statmap* me, PyObject* key){
/*****Argument Exception Handling*****/
if(!PyErr_Occurred() && (!PyInt_Check(key))){ // throws
PyErr_SetString(PyExc_TypeError, "key must be an integer");
}
PyObject* cxpyret = PyErr_Occurred()?(PyObject*)0:
statmap___mapget__(me, key);
/*****Return Value Exception Handling*****/
if(PyErr_Occurred()) return 0;
//Direct return: had Py_INCREF???
return cxpyret;
}
static int statmap___maplen__(statmap* me){ return MEMLEN; }
static struct PyMethodDef statmapMeths[] = {
{NULL} /* sentinel */ };
static PyMemberDef statmapFields[]={{0}};
/**Type statmap is not numeric.**/
static PyMappingMethods statmapAsMap = {
(lenfunc)statmap___maplen__, /*mp_length*/
(binaryfunc)statmap___mapget___wrap, /*mp_subscript*/
(objobjargproc)statmap___mapset__, /*mp_ass_subscript*/
};/*http://docs.python.org/c-api/typeobj.html#PyMappingMethods*/
/**Type statmap is not sequence.**/
/* NO PROPS: statmap */
static PyTypeObject statmapType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"module5.statmap", /*tp_name*/
sizeof(statmap), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)0, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
&statmapAsMap, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash */
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)0,/*tp_getattro*/
(setattrofunc)0,/*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"A Map type to access static array data.", /* tp_doc */
(traverseproc)0, /* tp_traverse */
(inquiry)0, /* tp_clear */
(richcmpfunc)0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)0, /* tp_iter */
(iternextfunc)0, /* tp_iternext */
statmapMeths, /* tp_methods */
statmapFields, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
(descrgetfunc)0, /* tp_descr_get */
(descrsetfunc)0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};/*http://docs.python.org/c-api/typeobj.html*/
static struct PyMethodDef module5Funcs[] = {
{NULL} /* sentinel */ };
PyMODINIT_FUNC
initmodule5(void) { //must be: init<module>
PyObject *mod;
//Initialize the type table for "statmap".
if(statmapType.tp_new == 0)
statmapType.tp_new = PyType_GenericNew;
if (PyType_Ready(&statmapType) < 0) return;
mod = Py_InitModule3("module5", module5Funcs,
"Mapping extension type with expy-cxpy.");
if (mod==0) return;
Py_INCREF(&statmapType);
PyModule_AddObject(mod, "statmap", (PyObject *)&statmapType);
/* class fields for class statmap: */
/*End class statmap.*/
/* Custom Exceptions */
/* Global Members */
}
|
The testing of this is left for the reader. Again, for a full list of all related special methods, you are encouraged to take a look at the cxpy.py source file.
To implement the sequence protocol, simply define those special methods for the sequence protocol.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | """Sequence extension type with expy-cxpy."""
#File name: module6.py
from expy import *
class strbuf(public):
"""A Sequence type to simulate StringBuffer."""
size = ifield(int)
leng = ifield(int)
buff = ifield(str, flag='RO')
def __init__(me, size=int):
return """{
me->size = size;
me->leng = 0;
me->buff = malloc(sizeof(char)*(size+1));
if(!me->buff) return -1; //fail
me->buff[me->leng] = 0;
return 0;
}"""
def __len__(me):
"""The length of the buffer."""
return """{ return me->leng; }"""
def __getitem__(me, i=int):
return """{
if (i<0) i += me->leng;
if(i >= me->leng || i<0){
PyErr_SetString(PyExc_ValueError, "Index out of Range");
return 0;
}
//implicit Py_INCREF:
return PyString_FromStringAndSize(me->buff+i,1);
}"""
def __setitem__(me, i=int, v=object):
return """{
if (i<0) i += me->leng;
if(i >= me->leng || i<0){
PyErr_SetString(PyExc_ValueError, "Index out of Range");
return 1; //failed
}
if(!PyString_Check(v) || PyString_Size(v)!=1){
PyErr_SetString(PyExc_TypeError, "Must be a String of size 1.");
return 1; //failed
}
me->buff[i] = PyString_AsString(v)[0];
return 0;
}"""
@imethod(int)
def enlarge(me, size=0):
"""enlarge to the given size, or double the capacity,
whichever is the bigger."""
return """{
if(size < me->size*2 +1)
size = 1 + me->size * 2;
char* buff = malloc(sizeof(char)*size);
if(!buff) return -1; //fail
strcpy(buff, me->buff);
free(me->buff);
me->buff = buff;
me->size = size;
return size;
}"""
def __concat__(me, u):
return """{
if(!PyString_Check(u) && !strbuf_Check(u))
return NULL; //failed
const char* buf = NULL;
int len = me->leng;
if(PyString_Check(u)){
buf = PyString_AsString(u);
len += PyString_Size(u);
}else if(strbuf_Check(u)){
buf= ((strbuf*)u)->buff;
len += ((strbuf*)u)->leng;
}
strbuf* sb = strbuf_NEW(); //implict Py_INCREF
if(!sb) return NULL;
sb->size = sb->leng = len;
sb->buff = malloc(sizeof(char)*(len+1));
if(!sb->buff){ Py_DECREF(sb); return NULL; }
strcpy(sb->buff, me->buff);
strcpy(sb->buff + me->leng, buf);
return (PyObject*)sb;
}"""
def __iconcat__(me, u):
return """{
if(!PyString_CheckExact(u)
&& !strbuf_CheckExact(u))
return NULL; //failed
int len = me->leng;
const char* buf = NULL;
if(PyString_CheckExact(u)){
buf = PyString_AsString(u);
len += PyString_Size(u);
}else if(strbuf_CheckExact(u)){
buf = ((strbuf*)u)->buff;
len += ((strbuf*)u)->leng;
}
if(len > me->size)
if(strbuf_enlarge(me, len)<0)
return NULL; //failed
strcpy(me->buff+me->leng, buf);
me->leng = len;
Py_INCREF(me); //New Reference!
return (PyObject*) me;
}"""
def __repeat__(me, t=int):
return """{
if (t<=0) return NULL;
int len = me->leng * t;
strbuf* sb = strbuf_NEW(); //implict Py_INCREF
if(!sb) return NULL;
sb->size = sb->leng = len;
sb->buff = malloc(sizeof(char)*(len+1));
if(!sb->buff){ Py_DECREF(sb); return NULL; }
char * buff = sb->buff;
for(; t > 0; t--){
strcpy(buff, me->buff);
buff += me->leng;
}
return (PyObject*) sb;
}"""
def __irepeat__(me, t=int):
return """{
if(t<=0) return NULL;
int len = me->leng * t;
if(len > me->size)
if(strbuf_enlarge(me, len)<0)
return NULL; //failed
char * buff = me->buff;
for(; t > 0; t--){
strncpy(buff, me->buff, me->leng);
buff += me->leng;
}
me->leng = len;
me->buff[len] = 0;
Py_INCREF(me);
return (PyObject*) me;
}"""
|
Again notice the choice of the special method names __concat__ and __repeat__, which are not standard in pure python, this is again to avoid name conflict between the special methods for maps and sequences. The generated C code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /*--- class strbuf ---*/
typedef struct{
PyObject_HEAD
int size;
int leng;
char* buff;
} strbuf;
int strbuf_enlarge(strbuf* me, int size);
static PyObject* strbuf_enlarge_wrap(
strbuf* me,
PyObject* _args_
);
static PyObject* strbuf___getitem__(strbuf* me, int i);
static PyObject* strbuf___concat__(strbuf* me, PyObject* u);
static int strbuf___setitem__(strbuf* me, int i, PyObject* v);
static PyObject* strbuf___irepeat__(strbuf* me, int t);
int strbuf___init__(strbuf* me, int size);
static int strbuf___init___wrap(
strbuf* me,
PyObject* _args_,
PyObject* _kwds_
);
static PyObject* strbuf___repeat__(strbuf* me, int t);
static PyObject* strbuf___iconcat__(strbuf* me, PyObject* u);
static int strbuf___len__(strbuf* me);
#define strbuf_Check(op) PyObject_TypeCheck(op, &strbufType)
#define strbuf_CheckExact(op) (Py_TYPE(op)==&strbufType)
#define strbuf_NEW() PyObject_NEW(strbuf, &strbufType) //NEW reference!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | #include <Python.h>
#include "structmember.h"
#include "module6.h"
static PyTypeObject strbufType;
/**======== MODULE IMPLEMENTATION ========**/
/*--- class strbuf ---*/
int strbuf_enlarge(strbuf* me, int size){
if(size < me->size*2 +1)
size = 1 + me->size * 2;
char* buff = malloc(sizeof(char)*size);
if(!buff) return -1; //fail
strcpy(buff, me->buff);
free(me->buff);
me->buff = buff;
me->size = size;
return size;
}
static PyObject* strbuf_enlarge_wrap(
strbuf* me,
PyObject* _args_
){
int size=0;
//no keywords support
if(!PyArg_ParseTuple(
_args_, "|i"
, &size)) return 0;
/*****Argument Exception Handling*****/
int cxpyret = PyErr_Occurred()?(int)0:
strbuf_enlarge(me, size);
/*****Return Value Exception Handling*****/
if(PyErr_Occurred()) return 0;
return Py_BuildValue("i", cxpyret);
}
static PyObject* strbuf___getitem__(strbuf* me, int i){
if (i<0) i += me->leng;
if(i >= me->leng || i<0){
PyErr_SetString(PyExc_ValueError, "Index out of Range");
return 0;
}
//implicit Py_INCREF:
return PyString_FromStringAndSize(me->buff+i,1);
}
static PyObject* strbuf___concat__(strbuf* me, PyObject* u){
if(!PyString_Check(u) && !strbuf_Check(u))
return NULL; //failed
const char* buf = NULL;
int len = me->leng;
if(PyString_Check(u)){
buf = PyString_AsString(u);
len += PyString_Size(u);
}else if(strbuf_Check(u)){
buf= ((strbuf*)u)->buff;
len += ((strbuf*)u)->leng;
}
strbuf* sb = strbuf_NEW(); //implict Py_INCREF
if(!sb) return NULL;
sb->size = sb->leng = len;
sb->buff = malloc(sizeof(char)*(len+1));
if(!sb->buff){ Py_DECREF(sb); return NULL; }
strcpy(sb->buff, me->buff);
strcpy(sb->buff + me->leng, buf);
return (PyObject*)sb;
}
static int strbuf___setitem__(strbuf* me, int i, PyObject* v){
if (i<0) i += me->leng;
if(i >= me->leng || i<0){
PyErr_SetString(PyExc_ValueError, "Index out of Range");
return 1; //failed
}
if(!PyString_Check(v) || PyString_Size(v)!=1){
PyErr_SetString(PyExc_TypeError, "Must be a String of size 1.");
return 1; //failed
}
me->buff[i] = PyString_AsString(v)[0];
return 0;
}
static PyObject* strbuf___irepeat__(strbuf* me, int t){
if(t<=0) return NULL;
int len = me->leng * t;
if(len > me->size)
if(strbuf_enlarge(me, len)<0)
return NULL; //failed
char * buff = me->buff;
for(; t > 0; t--){
strncpy(buff, me->buff, me->leng);
buff += me->leng;
}
me->leng = len;
me->buff[len] = 0;
Py_INCREF(me);
return (PyObject*) me;
}
int strbuf___init__(strbuf* me, int size){
me->size = size;
me->leng = 0;
me->buff = malloc(sizeof(char)*(size+1));
if(!me->buff) return -1; //fail
me->buff[me->leng] = 0;
return 0;
}
static int strbuf___init___wrap(
strbuf* me,
PyObject* _args_,
PyObject* _kwds_
){
int size;
static char* _pctrl[] ={"size", 0};
if(!PyArg_ParseTupleAndKeywords(
_args_, _kwds_, "i",
_pctrl, &size)) return -1;
/*****Argument Exception Handling*****/
int cxpyret = PyErr_Occurred()?(int)-1:
strbuf___init__(me, size);
/*****Return Value Exception Handling*****/
return cxpyret; //don't need PyObject*!
}
static PyObject* strbuf___repeat__(strbuf* me, int t){
if (t<=0) return NULL;
int len = me->leng * t;
strbuf* sb = strbuf_NEW(); //implict Py_INCREF
if(!sb) return NULL;
sb->size = sb->leng = len;
sb->buff = malloc(sizeof(char)*(len+1));
if(!sb->buff){ Py_DECREF(sb); return NULL; }
char * buff = sb->buff;
for(; t > 0; t--){
strcpy(buff, me->buff);
buff += me->leng;
}
return (PyObject*) sb;
}
static PyObject* strbuf___iconcat__(strbuf* me, PyObject* u){
if(!PyString_CheckExact(u)
&& !strbuf_CheckExact(u))
return NULL; //failed
int len = me->leng;
const char* buf = NULL;
if(PyString_CheckExact(u)){
buf = PyString_AsString(u);
len += PyString_Size(u);
}else if(strbuf_CheckExact(u)){
buf = ((strbuf*)u)->buff;
len += ((strbuf*)u)->leng;
}
if(len > me->size)
if(strbuf_enlarge(me, len)<0)
return NULL; //failed
strcpy(me->buff+me->leng, buf);
me->leng = len;
Py_INCREF(me); //New Reference!
return (PyObject*) me;
}
static int strbuf___len__(strbuf* me){ return me->leng; }
static struct PyMethodDef strbufMeths[] = {
{ "enlarge",
(PyCFunction)strbuf_enlarge_wrap,
METH_VARARGS,
"enlarge to the given size, or double the capacity,\n"
"whichever is the bigger."
},
{NULL} /* sentinel */ };
static PyMemberDef strbufFields[] = {
{"size", T_INT, offsetof(strbuf, size), 0,
""},
{"leng", T_INT, offsetof(strbuf, leng), 0,
""},
{"buff", T_STRING, offsetof(strbuf, buff), RO,
""},
{NULL} /* Sentinel */
};
/**Type strbuf is not numeric.**/
/**Type strbuf is not mapping.**/
/*cxpy.py: this is according to Python C API 2.6.
You may want to change the method types according
to your own version of Python!*/
static PySequenceMethods strbufAsSeq = {
(lenfunc)strbuf___len__, /*sq_length*/
(binaryfunc)strbuf___concat__, /*sq_concat*/
(ssizeargfunc)strbuf___repeat__, /*sq_repeat*/
(ssizeargfunc)strbuf___getitem__, /*sq_item*/
(ssizessizeargfunc)NULL,/*sq_slice*/
(ssizeobjargproc)strbuf___setitem__, /*sq_ass_item*/
(ssizessizeobjargproc)NULL, /*sq_ass_slice*/
(objobjproc)NULL, /*sq_contains*/
(binaryfunc)strbuf___iconcat__, /*sq_inplace_concat*/
(ssizeargfunc)strbuf___irepeat__, /*sq_inplace_repeat*/
};/*http://docs.python.org/c-api/typeobj.html#PySequenceMethods*/
/* NO PROPS: strbuf */
static PyTypeObject strbufType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"module6.strbuf", /*tp_name*/
sizeof(strbuf), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)0, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
&strbufAsSeq, /*tp_as_sequence*/
0, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash */
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)0,/*tp_getattro*/
(setattrofunc)0,/*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"A Sequence type to simulate StringBuffer.", /* tp_doc */
(traverseproc)0, /* tp_traverse */
(inquiry)0, /* tp_clear */
(richcmpfunc)0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)0, /* tp_iter */
(iternextfunc)0, /* tp_iternext */
strbufMeths, /* tp_methods */
strbufFields, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
(descrgetfunc)0, /* tp_descr_get */
(descrsetfunc)0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)strbuf___init___wrap, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};/*http://docs.python.org/c-api/typeobj.html*/
static struct PyMethodDef module6Funcs[] = {
{NULL} /* sentinel */ };
PyMODINIT_FUNC
initmodule6(void) { //must be: init<module>
PyObject *mod;
//Initialize the type table for "strbuf".
if(strbufType.tp_new == 0)
strbufType.tp_new = PyType_GenericNew;
if (PyType_Ready(&strbufType) < 0) return;
mod = Py_InitModule3("module6", module6Funcs,
"Sequence extension type with expy-cxpy.");
if (mod==0) return;
Py_INCREF(&strbufType);
PyModule_AddObject(mod, "strbuf", (PyObject *)&strbufType);
/* class fields for class strbuf: */
/*End class strbuf.*/
/* Custom Exceptions */
/* Global Members */
}
|
And we have a test session for this module:
ylan@montop:~/expy/Doc/build/lib.linux-i686-2.5$ python
Python 2.5.2 (r252:60911, Jul 31 2008, 17:28:52)
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from module6 import *
>>> x = strbuf(100)
>>> x += 'OK!'
>>> x*= 5
>>> x.buff
'OK!OK!OK!OK!OK!'
We will present a relatively complete example, which will provide utilities to test and produce prime numbers, and will factorize an integer via the iterator protocol.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | """A small module to calculate/test primes.
@head:
/**This software is under GPL.*/
#include <stdio.h>
#include <math.h>
@body:
/*KMAX=5000 covers 2^31.*/
#define KMAX 5000
static int nap=1;
static int ap[KMAX]={2};
"""
from expy import *
#total number of stored primes
nprimes = gfield(5000)
@function(int)
def isprime(n=int):
"""isprime(n) --> returns:
-- 1 if n is a prime,
-- 0 if n is not, """
return """{
int k, m, r;
if(n<2) return 0;
k = prime_prime(KMAX-1);
if(n==2 || n==k) return 1;
if(n%k==0) return 0;
for(k=0; ;++k){
m = ap[k];
r = n/m;
if(n==r*m) return 0;
if(r<=m) return 1;
}
}"""
@function(int) #default return value
def prime(k=int):
"""prime(k) --> the k-th prime number."""
return """{
if(k<0 || k>=KMAX){
PyErr_SetString(PyExc_ValueError, "Out of range!");
return -1;
}
if (k<nap) return ap[k];
int i, m, n = ap[nap-1];
while (nap <= k){
n ++;
for(m=ap[i=0];
n%m!=0 && n/m>m;
m=ap[++i]);
if (n%m!=0) ap[nap++] = n;
}
return n;
}"""
@function(int)
def primes(x=int):
"""primes(x): --> number of primes <= x."""
return """{
if (x<2) return 0;
//don't check even numbers
if (x%2==0) --x;
int sum = 1; //as 2 is a prime
for(; x>2; --x, --x)
sum += prime_isprime(x);
return sum;
}"""
@function(int)
def firstpf(a=int, k=int):
"""return the smallest prime factor k'>=k."""
return """{
if(k<0) k=0;
int p = prime_prime(k);
int r = a/p;
while(r>=p){
if(a == p*r) return k;
p = prime_prime(++k);
r = a/p;
};
return -1;
}"""
class pfact(public):
"""decompose an integer into prime factors."""
c = ifield(long, flag='RO', doc='unfactorized part') #readonly
k = ifield(int, flag='RO', doc='minimal prime order') #readonly
desc = cfield("enumerate prime factors in low to hi order")
def __init__(me, a=long):
return """{
me->c = a;
me->k = 0;
return 0; //success
}"""
#The signature must be like this:
def __new__(cls, args, kwds):
"""A test of the __new__()"""
return """{
pfact* self;
self = pfact_NEW();//Py_INCREF
if (self == NULL) return NULL;
self->k = 0;
return (PyObject*) self;
}"""
def getc(me, closure = rawtype('void*')):
return """{
return PyInt_FromLong(me->c);//Py_INCREF
}"""
def setc(me, v, closure = rawtype('void*')):
return """{
if(!PyInt_Check(v)){
PyErr_SetString(PyExc_TypeError, "Must be an Integer.");
return 1; //failed
}
me->c = PyInt_AsLong(v);
return me->k = 0; //success
}"""
rem = property(getc, setc, doc="remainder for factorization")
def __iter__(me): #special methods should not be expressions
return """{
me->k = 0;
Py_INCREF(me);
return (PyObject*) me;
}"""
def __next__(me):
return """{
if(me->c <2) return NULL;
me->k = prime_firstpf(me->c, me->k);
int n = me->k<0?me->c:prime_prime(me->k);
me->c /= n;
return PyInt_FromLong(n);//Py_INCREF
}"""
@smethod(int) #static method
def maxprid():
"""maximum prime id."""
return "KMAX"
@cmethod(int) #class method
def maxprime(cls):
return "prime_prime(KMAX-1)"
|
In the prime.py, the prologue actually defines some static storage for the primes. This small module is quite useful, it provides the first 5000 primes, and can test any integers within the range of [1, 2^31-1]. And finally, For any integer in that range, it provides the prime factors by the __iter__ and __next__ methods (iterator protocol). Here is the script file to test this module:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import sys, os
for fn in os.listdir('build'):
if fn.startswith('lib'):
print "inserted:",fn
sys.path.insert(0, 'build/'+fn)
from prime import *
print pfact.maxprid()
print pfact.maxprime()
print pfact.desc
#this class field is read only:
#pfact.desc = "set to something else"
print "stored primes:", nprimes
for i in range(10): print i, isprime(i), ';',
print
print "there are %i primes from 0 to 9999."%primes(9999)
print "The 10th prime is", prime(10)
pf = pfact(52)
fs = [i for i in pf]
print "The prime factors for 52 are:", fs
print "Property rem:", pf.rem
pf.rem = 123
print "Property rem modified:", pf.rem
print [i for i in pf]
|
The output from this test file looks like this:
ylan@montop:~/expy/examples$ python testprime.py
5000
48611
enumerate prime factors in low to hi order
stored primes: 5000
0 0 ; 1 0 ; 2 1 ; 3 1 ; 4 0 ; 5 1 ; 6 0 ; 7 1 ; 8 0 ; 9 0 ;
there are 1229 primes from 0 to 9999.
The 10th prime is 31
The prime factors for 52 are: [2, 2, 13]
Property rem: 1
Property rem modified: 123
[3, 41]
And here is the generated C file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | //@head:begin
/**This software is under GPL.*/
#include <stdio.h>
#include <math.h>
//@head:end
/*--- class pfact ---*/
typedef struct{
PyObject_HEAD
long int c;
int k;
} pfact;
int prime_prime(int k);
static PyObject* prime_prime_wrap(
PyObject* _NOUSE,
PyObject* _args_
);
int prime_primes(int x);
static PyObject* prime_primes_wrap(
PyObject* _NOUSE,
PyObject* _args_
);
int prime_firstpf(int a, int k);
static PyObject* prime_firstpf_wrap(
PyObject* _NOUSE,
PyObject* _args_
);
int prime_isprime(int n);
static PyObject* prime_isprime_wrap(
PyObject* _NOUSE,
PyObject* _args_
);
static PyObject* pfact_maxprid_wrap(
PyObject* _NOUSE
);
static int pfact_setc(pfact* me, PyObject* v, void* closure);
static PyObject* pfact_getc(pfact* me, void* closure);
static PyObject* pfact_maxprime_wrap(
PyTypeObject* cls
);
static PyObject* pfact___iter__(pfact* me);
static PyObject* pfact___next__(pfact* me);
static PyObject* pfact___new__(PyTypeObject* cls, PyObject* args, PyObject* kwds);
int pfact___init__(pfact* me, long int a);
static int pfact___init___wrap(
pfact* me,
PyObject* _args_,
PyObject* _kwds_
);
#define pfact_Check(op) PyObject_TypeCheck(op, &pfactType)
#define pfact_CheckExact(op) (Py_TYPE(op)==&pfactType)
#define pfact_NEW() PyObject_NEW(pfact, &pfactType) //NEW reference!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | #include <Python.h>
#include "structmember.h"
#include "prime.h"
static PyTypeObject pfactType;
/**======== MODULE IMPLEMENTATION ========**/
//@body:begin
/*KMAX=5000 covers 2^31.*/
#define KMAX 5000
static int nap=1;
static int ap[KMAX]={2};
//@body:end
int prime_prime(int k){
if(k<0 || k>=KMAX){
PyErr_SetString(PyExc_ValueError, "Out of range!");
return -1;
}
if (k<nap) return ap[k];
int i, m, n = ap[nap-1];
while (nap <= k){
n ++;
for(m=ap[i=0];
n%m!=0 && n/m>m;
m=ap[++i]);
if (n%m!=0) ap[nap++] = n;
}
return n;
}
static PyObject* prime_prime_wrap(
PyObject* _NOUSE,
PyObject* _args_
){
int k;
//no keywords support
if(!PyArg_ParseTuple(
_args_, "i"
, &k)) return 0;
/*****Argument Exception Handling*****/
int cxpyret = PyErr_Occurred()?(int)0:
prime_prime(k);
/*****Return Value Exception Handling*****/
if(PyErr_Occurred()) return NULL;
return Py_BuildValue("i", cxpyret);
}
int prime_primes(int x){
if (x<2) return 0;
//don't check even numbers
if (x%2==0) --x;
int sum = 1; //as 2 is a prime
for(; x>2; --x, --x)
sum += prime_isprime(x);
return sum;
}
static PyObject* prime_primes_wrap(
PyObject* _NOUSE,
PyObject* _args_
){
int x;
//no keywords support
if(!PyArg_ParseTuple(
_args_, "i"
, &x)) return 0;
/*****Argument Exception Handling*****/
int cxpyret = PyErr_Occurred()?(int)0:
prime_primes(x);
/*****Return Value Exception Handling*****/
if(PyErr_Occurred()) return NULL;
return Py_BuildValue("i", cxpyret);
}
int prime_firstpf(int a, int k){
if(k<0) k=0;
int p = prime_prime(k);
int r = a/p;
while(r>=p){
if(a == p*r) return k;
p = prime_prime(++k);
r = a/p;
};
return -1;
}
static PyObject* prime_firstpf_wrap(
PyObject* _NOUSE,
PyObject* _args_
){
int a;
int k;
//no keywords support
if(!PyArg_ParseTuple(
_args_, "ii"
, &a, &k)) return 0;
/*****Argument Exception Handling*****/
int cxpyret = PyErr_Occurred()?(int)0:
prime_firstpf(a, k);
/*****Return Value Exception Handling*****/
if(PyErr_Occurred()) return NULL;
return Py_BuildValue("i", cxpyret);
}
int prime_isprime(int n){
int k, m, r;
if(n<2) return 0;
k = prime_prime(KMAX-1);
if(n==2 || n==k) return 1;
if(n%k==0) return 0;
for(k=0; ;++k){
m = ap[k];
r = n/m;
if(n==r*m) return 0;
if(r<=m) return 1;
}
}
static PyObject* prime_isprime_wrap(
PyObject* _NOUSE,
PyObject* _args_
){
int n;
//no keywords support
if(!PyArg_ParseTuple(
_args_, "i"
, &n)) return 0;
/*****Argument Exception Handling*****/
int cxpyret = PyErr_Occurred()?(int)0:
prime_isprime(n);
/*****Return Value Exception Handling*****/
if(PyErr_Occurred()) return NULL;
return Py_BuildValue("i", cxpyret);
}
/*--- class pfact ---*/
static PyObject* pfact_maxprid_wrap(
PyObject* _NOUSE
){
/*****Argument Exception Handling*****/
int cxpyret = PyErr_Occurred()?(int)0:
KMAX;
/*****Return Value Exception Handling*****/
if(PyErr_Occurred()) return NULL;
return Py_BuildValue("i", cxpyret);
}
static int pfact_setc(pfact* me, PyObject* v, void* closure){
if(!PyInt_Check(v)){
PyErr_SetString(PyExc_TypeError, "Must be an Integer.");
return 1; //failed
}
me->c = PyInt_AsLong(v);
return me->k = 0; //success
}
static PyObject* pfact_getc(pfact* me, void* closure){
return PyInt_FromLong(me->c);//Py_INCREF
}
static PyObject* pfact_maxprime_wrap(
PyTypeObject* cls
){
/*****Argument Exception Handling*****/
int cxpyret = PyErr_Occurred()?(int)0:
prime_prime(KMAX-1);
/*****Return Value Exception Handling*****/
if(PyErr_Occurred()) return NULL;
return Py_BuildValue("i", cxpyret);
}
static PyObject* pfact___iter__(pfact* me){
me->k = 0;
Py_INCREF(me);
return (PyObject*) me;
}
static PyObject* pfact___next__(pfact* me){
if(me->c <2) return NULL;
me->k = prime_firstpf(me->c, me->k);
int n = me->k<0?me->c:prime_prime(me->k);
me->c /= n;
return PyInt_FromLong(n);//Py_INCREF
}
static PyObject* pfact___new__(PyTypeObject* cls, PyObject* args, PyObject* kwds){
pfact* self;
self = pfact_NEW();//Py_INCREF
if (self == NULL) return NULL;
self->k = 0;
return (PyObject*) self;
}
int pfact___init__(pfact* me, long int a){
me->c = a;
me->k = 0;
return 0; //success
}
static int pfact___init___wrap(
pfact* me,
PyObject* _args_,
PyObject* _kwds_
){
long int a;
static char* _pctrl[] ={"a", 0};
if(!PyArg_ParseTupleAndKeywords(
_args_, _kwds_, "l",
_pctrl, &a)) return 0;
/*****Argument Exception Handling*****/
int cxpyret = PyErr_Occurred()?(int)0:
pfact___init__(me, a);
/*****Return Value Exception Handling*****/
return cxpyret; //don't need PyObject*!
}
static struct PyMethodDef pfactMeths[] = {
{ "maxprid",
(PyCFunction)pfact_maxprid_wrap,
METH_NOARGS | METH_STATIC,
"maximum prime id."
},
{ "maxprime",
(PyCFunction)pfact_maxprime_wrap,
METH_NOARGS | METH_CLASS,
""
},
{NULL} /* sentinel */ };
static PyMemberDef pfactFields[] = {
{"c", T_LONG, offsetof(pfact, c), RO,
"unfactorized part"},
{"k", T_INT, offsetof(pfact, k), RO,
"minimal prime order"},
{NULL} /* Sentinel */
};
/**Type pfact is not numeric.**/
/**Type pfact is not mapping.**/
/**Type pfact is not sequence.**/
static PyGetSetDef pfactProps[] = {
{"rem", (getter)pfact_getc, (setter)pfact_setc,
"remainder for factorization", NULL},
{NULL} /* Sentinel */
};
static PyTypeObject pfactType = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"prime.pfact", /*tp_name*/
sizeof(pfact), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor)0, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash */
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)0,/*tp_getattro*/
(setattrofunc)0,/*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT, /*tp_flags*/
"decompose an integer into prime factors.", /* tp_doc */
(traverseproc)0, /* tp_traverse */
(inquiry)0, /* tp_clear */
(richcmpfunc)0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)pfact___iter__, /* tp_iter */
(iternextfunc)pfact___next__, /* tp_iternext */
pfactMeths, /* tp_methods */
pfactFields, /* tp_members */
pfactProps, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
(descrgetfunc)0, /* tp_descr_get */
(descrsetfunc)0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)pfact___init___wrap, /* tp_init */
0, /* tp_alloc */
pfact___new__, /* tp_new */
};/*http://docs.python.org/c-api/typeobj.html*/
static struct PyMethodDef primeFuncs[] = {
{ "prime",
(PyCFunction)prime_prime_wrap,
METH_VARARGS,
"prime(k) --> the k-th prime number."
},
{ "primes",
(PyCFunction)prime_primes_wrap,
METH_VARARGS,
"primes(x): --> number of primes <= x."
},
{ "firstpf",
(PyCFunction)prime_firstpf_wrap,
METH_VARARGS,
"return the smallest prime factor k'>=k."
},
{ "isprime",
(PyCFunction)prime_isprime_wrap,
METH_VARARGS,
"isprime(n) --> returns:\n"
" -- 1 if n is a prime, \n"
" -- 0 if n is not, "
},
{NULL} /* sentinel */ };
PyMODINIT_FUNC
initprime(void) { //must be: init<module>
PyObject *mod;
//Initialize the type table for "pfact".
if(pfactType.tp_new == 0)
pfactType.tp_new = PyType_GenericNew;
if (PyType_Ready(&pfactType) < 0) return;
mod = Py_InitModule3("prime", primeFuncs,
"A small module to calculate/test primes.");
if (mod==0) return;
Py_INCREF(&pfactType);
PyModule_AddObject(mod, "pfact", (PyObject *)&pfactType);
/* class fields for class pfact: */
PyDict_SetItemString(pfactType.tp_dict, "desc",
Py_BuildValue("s","enumerate prime factors in low to hi order"));
/*End class pfact.*/
/* Custom Exceptions */
/* Global Members */
PyModule_AddObject(mod, "nprimes",
Py_BuildValue("i", 5000));
}
|
For instance fields, the keyword ‘dimension’ can be used to declare a static multi-dimensional array:
class myclass(public):
size = ifield(int, private, dimension='[2]')
the code above would define something like this for the generated C code:
typedef struct {
int[2] size;
...
}myclass;
Of course, you can define multi-dimensions, such as:
dimension='[3][2]'
Usually such a field will be ‘private’, as there ususually not a readily defined way in CPython to access it, but you can define getters/setters for them.
There is another useful keyword in ifield constructor: exposure. Use this field if your extension object has a nested struct and you would like to access it as a member field. Suppose you have a C struct defined as follows:
struct mystruct{ int x, y; };
and you would like to have that struct as a member of your extention object, then expose the field ‘x’ of it. Here is how to make it happend:
class myclass(public):
point = ifield(rawtype('struct mystruct'), private)
x = ifield(int, exposure='point.x')
In this case, the member ‘x’ will be considered already exist in the member ‘point’ as ‘point.x’, so there won’t be another top level ‘x’ member in the C struct of this class, but the field member would be directly exposed to the python object as a a property named ‘x’.
Another use case for ‘exposure’ keyword is to expose it as a different type than what’s claimed, which is often used with keyword ‘dimension’:
class myclass(public):
chars = ifield(char, dimension='[4]', exposure=int)
this time the ‘chars’ is of a nominal type ‘char’, but it can also be seen as an 32 bit integer, because of the ‘dimension’ keyword, so an ‘exposure’ keyword is used to achieve this.
When you write your __init__ method, you can specify your arguments just the same way as you do in writing a python __init__ method, and EXPY will produce a wrapper function to parse the arguments. What’s more, you can also throw exceptions (using the @throws decorator). One thing must remember for __init__: return 0 for success, and -1 for failure.
There are many special methods, such as __iter__, __next__ etc that require returning a python object, and you should increase the reference count. EXPY will check if Py_INCREF is in there, and will give a warning if not.
Through examples I hope it all looks interesting and exciting. Suggestions, volunteer works, bug reports are all welcome, I am available via email: lanyjie at yahoo dot come, and expy also has its own email server, as indicated on the homepage. This concludes our introduction to the major features of expy-cxpy. Thanks for your interests, and I hope you find this a nice tool for developing python extensions. Happy coding!