5.6.2 : Wrapper of the tables allocation

To wrap the tables allocation we need, at least, only one function as for the timer function. But this can be useful for you later to have a dedicated function to allocate a numpy array in an other wrapper function.

So we will create two functions :

The header allocTableWrapper.h is really simple :
1
2
3
4
5
6
7
8
9
10
#ifndef __ALLOC_TABLE_WRAPPER_H__
#define __ALLOC_TABLE_WRAPPER_H__

#include <Python.h>
#include "structmember.h"

PyObject * allocTable(long unsigned int nbElement);
PyObject * allocTableWrapper(PyObject *self, PyObject *args);

#endif

Let's write the source file allocTableWrapper.cpp.

Again in this example, since we are using numpy in the module, we have to define the NO_IMPORT_ARRAY to avoid multiple definitions of the same numpy function. And we also have to specify the version of the numpy API to avoid warnings :
1
2
3
4
5
6
#define NO_IMPORT_ARRAY
#ifndef DISABLE_COOL_ARRAY
#define PY_ARRAY_UNIQUE_SYMBOL core_ARRAY_API
#endif

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
Then we include appropriate files. Do not forget to include the asterics_alloc.h file to use our allocation function :
1
2
3
4
5
6
7
#include <Python.h>
#include <numpy/arrayobject.h>
#include <bytearrayobject.h>

#include "asterics_alloc.h"

#include "allocTableWrapper.h"
In this wrapper we will allocate our proper tables, so python does not know how to deallocate them too. For that, we have to define a deallocation function for our tables. This mechanism is called PyCapsule in python, you can find more documentation here.

In this function, we get a PyCapsule. Then, we ge the associated pointer by name and finally we deallocate it :

1
2
3
4
5
6
7
///Free the capsule memory
/**	@param obj : object with contains the capsule
*/
void empty_freeTabArray(PyObject* obj){
	float* ptr = (float*) PyCapsule_GetPointer(obj,"emptyTable");
	free(ptr);
}
Now, we have to implement the function which allocates numpy arrays :
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
///Create a numpy matrix
/**	@param nbElement : number of element of the table
 * 	@return numpy array
*/
PyObject * allocTable(long unsigned int nbElement){
	//Set the size of the numpy array
	npy_intp attr_size[2];
	attr_size[0] = nbElement;
	
	long unsigned int pitch(getPitch(nbElement));
	//Calling allocation
	float* tab = (float*)asterics_malloc((nbElement + pitch)*sizeof(float));
	if(tab == NULL){
		PyObject* objMat = PyArray_EMPTY(2, attr_size, NPY_FLOAT32, 0);
		if(objMat == NULL){
			PyErr_SetString(PyExc_RuntimeError, "allocTableWrapper : Could not allocated memory\n");
			return NULL;
		}
		return objMat;
	}
	memset(tab, 0, (nbElement + pitch)*sizeof(float));
	PyArray_Dims strides = {NULL, 0};
	strides.ptr = PyDimMem_NEW(1);
	strides.len = 1;
	PyArray_Descr *descr = PyArray_DescrFromType(NPY_FLOAT32);
	strides.ptr[0] = (npy_intp)sizeof(float);		     // Last strides is equal to element size
	
	PyObject* objMat = PyArray_NewFromDescr(&PyArray_Type, descr, 1, attr_size, strides.ptr, (void *)tab, NPY_ARRAY_WRITEABLE, NULL);
	
	//Desalocation stuff
	PyObject* memory_capsule = PyCapsule_New(tab, "emptyTable", empty_freeTabArray);
	if(PyArray_SetBaseObject((PyArrayObject*)objMat, memory_capsule) < 0){
		PyErr_SetString(PyExc_RuntimeError, "Fail to create PyCapsule\n");
		return NULL;
	}
	return objMat;
}
To parse static parameters, we have to use the function PyArg_ParseTuple, this function wroks the same as the scanf function from the C standard library.

Once we have the desired size of the table (in number of elements), we call the allocTable function :

1
2
3
4
5
6
7
8
9
10
11
12
13
///Allocate an aligned matrix of float with a pitch
/**	@param self : pointer to the parent object if it exist
 * 	@param args : arguments passed to the program
 * 	@return allocated numpy array
*/
PyObject * allocTableWrapper(PyObject *self, PyObject *args){
	long unsigned int nbElement(0lu);
	if(!PyArg_ParseTuple(args, "k", &nbElement)){
		PyErr_SetString(PyExc_RuntimeError, "allocTableWrapper : wrong set of arguments. Expects one argument table size\n");
		return NULL;
	}
	return allocTable(nbElement);
}
The full allocTableWrapper.cpp 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
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
/***************************************
	Auteur : Pierre Aubert
	Mail : aubertp7@gmail.com
	Licence : CeCILL-C
****************************************/

#define NO_IMPORT_ARRAY
#ifndef DISABLE_COOL_ARRAY
#define PY_ARRAY_UNIQUE_SYMBOL core_ARRAY_API
#endif

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION

#include <Python.h>
#include <numpy/arrayobject.h>
#include <bytearrayobject.h>

#include "asterics_alloc.h"

#include "allocTableWrapper.h"

///Free the capsule memory
/**	@param obj : object with contains the capsule
*/
void empty_freeTabArray(PyObject* obj){
	float* ptr = (float*) PyCapsule_GetPointer(obj,"emptyTable");
	free(ptr);
}

///Create a numpy matrix
/**	@param nbElement : number of element of the table
 * 	@return numpy array
*/
PyObject * allocTable(long unsigned int nbElement){
	//Set the size of the numpy array
	npy_intp attr_size[2];
	attr_size[0] = nbElement;
	
	long unsigned int pitch(getPitch(nbElement));
	//Calling allocation
	float* tab = (float*)asterics_malloc((nbElement + pitch)*sizeof(float));
	if(tab == NULL){
		PyObject* objMat = PyArray_EMPTY(2, attr_size, NPY_FLOAT32, 0);
		if(objMat == NULL){
			PyErr_SetString(PyExc_RuntimeError, "allocTableWrapper : Could not allocated memory\n");
			return NULL;
		}
		return objMat;
	}
	memset(tab, 0, (nbElement + pitch)*sizeof(float));
	PyArray_Dims strides = {NULL, 0};
	strides.ptr = PyDimMem_NEW(1);
	strides.len = 1;
	PyArray_Descr *descr = PyArray_DescrFromType(NPY_FLOAT32);
	strides.ptr[0] = (npy_intp)sizeof(float);		     // Last strides is equal to element size
	
	PyObject* objMat = PyArray_NewFromDescr(&PyArray_Type, descr, 1, attr_size, strides.ptr, (void *)tab, NPY_ARRAY_WRITEABLE, NULL);
	
	//Desalocation stuff
	PyObject* memory_capsule = PyCapsule_New(tab, "emptyTable", empty_freeTabArray);
	if(PyArray_SetBaseObject((PyArrayObject*)objMat, memory_capsule) < 0){
		PyErr_SetString(PyExc_RuntimeError, "Fail to create PyCapsule\n");
		return NULL;
	}
	return objMat;
}

///Allocate an aligned matrix of float with a pitch
/**	@param self : pointer to the parent object if it exist
 * 	@param args : arguments passed to the program
 * 	@return allocated numpy array
*/
PyObject * allocTableWrapper(PyObject *self, PyObject *args){
	long unsigned int nbElement(0lu);
	if(!PyArg_ParseTuple(args, "k", &nbElement)){
		PyErr_SetString(PyExc_RuntimeError, "allocTableWrapper : wrong set of arguments. Expects one argument table size\n");
		return NULL;
	}
	return allocTable(nbElement);
}
You can download it here.