step_00_c_api.cΒΆ

  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
#include <math.h>
#include <Python.h>

// Porting to HPy, Step 0: Original Python C API version
//
// An example of porting a C extension that implements a Point type
// with a couple of simple methods (a norm and a dot product). It
// illustrates the steps needed to port types that contain additional
// C attributes (in this case, x and y).
//
// This file contains the original C API version that needs to be ported.
//
// HPy supports porting C extensions piece by piece.
//
// point_hpy_legacy_1.c illustrates a possible first step where all
// methods still receive PyObject arguments and may still cast them to
// PyPointObject if they are instances of Point.
//
// point_hpy_legacy_2.c shows how to transition some methods to HPy methods
// that receive HPy handles as arguments while still supporting legacy
// methods that receive PyObject arguments.
//
// point_hpy_final.c shows the completed port to HPy where all methods receive
// HPy handles and PyObject_HEAD has been removed.

typedef struct {
    PyObject_HEAD
    double x;
    double y;
    PyObject *obj;
} PyPointObject;

int Point_traverse(PyObject *self, visitproc visit, void *arg)
{
    Py_VISIT(((PyPointObject*)self)->obj);
    Py_VISIT(Py_TYPE(self));
    return 0;
}

void Point_dealloc(PyObject *self)
{
    Py_CLEAR(((PyPointObject*)self)->obj);
    PyTypeObject *tp = Py_TYPE(self);
    tp->tp_free(self);
    Py_DECREF(tp);
}

// this is a method for creating a Point
int Point_init(PyObject *self, PyObject *args, PyObject *kw)
{
    static char *kwlist[] = {"x", "y", "obj", NULL};
    PyPointObject *p = (PyPointObject *)self;
    p->x = 0.0;
    p->y = 0.0;
    p->obj = NULL;
    if (!PyArg_ParseTupleAndKeywords(args, kw, "|ddO", kwlist,
                                     &p->x, &p->y, &p->obj))
        return -1;
    if (p->obj == NULL)
        p->obj = Py_None;
    Py_INCREF(p->obj);
    return 0;
}

// this is a method of Point
PyObject* Point_norm(PyObject *self)
{
    PyPointObject *p = (PyPointObject *)self;
    double norm;
    PyObject *result;
    norm = sqrt(p->x * p->x + p->y * p->y);
    result = PyFloat_FromDouble(norm);
    return result;
}

// this is the getter for the associated object
PyObject* Point_obj_get(PyObject *self, void *context)
{
    PyPointObject *p = (PyPointObject *)self;
    Py_INCREF(p->obj);
    return p->obj;
}

// this is an unrelated function which happens to cast a PyObject* into a
// PyPointObject*
PyObject* dot(PyObject *self, PyObject *args)
{
    PyObject *point1, *point2;
    if (!PyArg_ParseTuple(args, "OO",  &point1, &point2))
        return NULL;

    PyPointObject *p1 = (PyPointObject *)point1;
    PyPointObject *p2 = (PyPointObject *)point2;

    double dp;
    PyObject *result;
    dp = p1->x * p2->x + p1->y * p2->y;
    result = PyFloat_FromDouble(dp);
    return result;
}


// Method, type and module definitions. These will be updated to add HPy
// module support in point_hpy_legacy_1.c.

static PyMethodDef PointMethods[] = {
    {"norm", (PyCFunction)Point_norm, METH_NOARGS, "Distance from origin."},
    {NULL, NULL, 0, NULL}
};

static PyGetSetDef PointGetSets[] = {
    {"obj", (getter)Point_obj_get, NULL, "Associated object.", NULL},
    {NULL, NULL, 0, NULL}
};

static PyType_Slot Point_slots[] = {
    {Py_tp_doc, "Point (Step 0; C API implementation)"},
    {Py_tp_init, Point_init},
    {Py_tp_methods, PointMethods},
    {Py_tp_getset, PointGetSets},
    {Py_tp_traverse, Point_traverse},
    {Py_tp_dealloc, Point_dealloc},
    {0, 0}
};

static PyType_Spec Point_Type_spec = {
    .name = "point_capi.Point",
    .basicsize = sizeof(PyPointObject),
    .itemsize = 0,
    .flags = Py_TPFLAGS_DEFAULT,
    .slots = Point_slots
};

static PyMethodDef PointModuleMethods[] = {
    {"dot", (PyCFunction)dot, METH_VARARGS, "Dot product."},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef moduledef = {
    PyModuleDef_HEAD_INIT,
    "step_00_c_api",
    "Point module (Step 0; C API implementation)",
    -1,
    PointModuleMethods,
    NULL,
    NULL,
    NULL,
    NULL,
};

PyMODINIT_FUNC
PyInit_step_00_c_api(void)
{
    PyObject* m;
    m = PyModule_Create(&moduledef);
    if (m == NULL)
        return NULL;

    PyObject *point_type = PyType_FromSpec(&Point_Type_spec);
    if (point_type == NULL)
        return NULL;
    PyModule_AddObject(m, "Point", point_type);

    return m;
}