Skip to content

Contribution Notes: colours.c

Bhavye Mathur edited this page Dec 1, 2020 · 4 revisions

This page details everything you must take into consideration before making changes to colours.c. This will help reduce the number of bugs created by changes and make it easier to keep a track of what needs changing. Before making changes, make sure to read the PEP 7 Style Guide

Creating a New Colour Type

You may want to create a new colour type or another custom type to use in the C class. These next steps detail the process to do so with an example of the ColourHSV type.

Checklist

  1. Create type struct and define what the object will contain
  2. Create the function which creates a new instance of the type
  3. Create the function which initializes a new instance
  4. Define the tp_slots of the type
  5. Add the class build code to the PyMODINIT_FUNC function

Detailed Steps

1. Create the custom type struct, ie what the object will contain. Reference.

typedef struct {
    PyObject_HEAD
    int red, green, blue;
    char colour[7];
} ColourHSV;

PyObject_HEAD is mandatory at the start of each object struct

The red, green, blue variables are defined for all colour objects while the 7-length string colour defines the actual colour itself in hex format. (Eg. "#ffffff" for white). Do not initialize any values for variables here, this is just what an instance of the object contains.

The code ends with the name of the class, ColourHSV.

2. Create the type's instance creation function. Reference.

static PyObject* ColourHSV_new(PyTypeObject *type, PyObject *args, PyObject*kwds) {
    ColourHSV *self;

    self = (ColourHSV *) type->tp_alloc(type, 0);

    if (self != NULL) {
        strncpy(self->colour, "#000000", 7);

        self->red = 0;
        self->green = 0;
        self->blue = 0;
    }
    return (PyObject *) self;
}

This function will be called whenever someone creates a new instance of the ColourHSV type. It starts out with the line of the arguments required for the function PyTypeObject *type, PyObject *args, PyObject*kwds. This is standard for colour types.

Then, we allocate the object some memory,

self = (ColourHSV *) type->tp_alloc(type, 0);

and set the variable self to its value. Checking if (self != NULL) ensures that no error occurred after which we define the default values for the variables of the class. Finally, the code returns self, essentially giving back the object.

3. Create the type's initialization creation function. Reference.

static int ColourHSV_init(ColourHSV *self, PyObject *args, PyObject *kwds) {
    static char *kwlist[] = {"h", "s", "v", NULL};

    int h = 0, s = 0, v = 0;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwlist, &h, &s, &v))
        return -1;

    int red = 0, green = 0, blue = 0;

    /* Code to define the values of red, green, & blue. It is the conversion from HSV to RGB here.*/

    self->red = red;
    self->green = green;
    self->blue = blue;

    sprintf(self->colour, "#%x%x%x", red, green, blue);

    return 0;
}

This code is incredibly important so let's step through it carefully. It is called whenever someone creates a new ColourHSV instance.

static char *kwlist[] = {"h", "s", "v", NULL};

    int h = 0, s = 0, v = 0;

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iii", kwlist, &h, &s, &v))
        return -1;

This part starts by defining what arguments the class is going to receive ({"h", "s", "v", NULL} the NULL is to terminate the array). Here it is the HSV values. Then, we parse the values and by saying "|iii" it tells the API that the values for h, s, & v are integers. If there is an error in this step, the program returns -1.

Then it proceeds to convert the HSV values into RGB and stores them in 3 variables. The sprintf() allows for the conversion of the RGB values into a hex string and the code returns a 0.

4. Define the type object. Reference.

This step defines what all fields are required for the colour object. Here is an example:

static PyTypeObject ColourHSVType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "goopylib.ColourHSV",
    .tp_doc = "An object to represent goopylib colours using HSV values",
    .tp_basicsize = sizeof(ColourHSV),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT,
    .tp_new = PyType_GenericNew,
    .tp_dealloc = (destructor) Colour_dealloc,
    .tp_new = ColourHSV_new,
    .tp_init = (initproc) ColourHSV_init,
    .tp_members = Colour_members,
};
  • .tp_name is the name of the type
  • .tp_doc is the docstring describing the type
  • .tp_basicsize just defines the size of the object, this is sizeof(ColourHSV), the previously defined struct
  • .tp_itemsize is set to 0 to specify that instances of the object have a fixed length
  • .tp_flags is set to the generic, default flag
  • .tp_new points to the function we created which defined how to create a new instance
  • .tp_init points to the initialization function of the type previously created by us too
  • .tp_members this defines the attributes of the type, it is defined as:
static PyMemberDef Colour_members[] = {
    {"red", T_INT, offsetof(Colour, red), 0,
     "RGB red value of Colour"},

    {"green", T_INT, offsetof(Colour, green), 0,
     "RGB green value of Colour"},

    {"blue", T_INT, offsetof(Colour, blue), 0,
     "RGB blue value of Colour"},

    {"colour", T_STRING, offsetof(Colour, colour), 0,
     "Hexadecimal colour value of Colour"},

    {NULL}
};

Every attribute here contains 5 elements: name, datatype, variable name, access flag, and its docstring. The {NULL} at the end marks the end of all attributes.

5. Add Module Initialization Code

The next step is to allow Python to build the extension module and the class along with it. Add this code to PyMODINIT_FUNC:

PyObject *m;

if (PyType_Ready(&ColourHSVType) < 0)
        return NULL;

Py_INCREF(&ColourHSVType);

m = PyModule_Create(&CColoursModule);
    if (m == NULL)
        return NULL;

if (PyModule_AddObject(m, "ColourHSV", (PyObject *) &ColourHSVType) < 0) {
        Py_DECREF(&ColourHSVType);
        Py_DECREF(m);
        return NULL;}

Look at fields for more fields you can specify here.

Important References:

Clone this wiki locally