Implementing Python class instance attributes with C ?

C programming, build, interpreter/VM.
Target audience: MicroPython Developers.
Post Reply
User avatar
Hanski
Posts: 8
Joined: Sat Aug 12, 2017 9:54 am

Implementing Python class instance attributes with C ?

Post by Hanski » Fri Sep 22, 2017 6:04 am

Hi,

I have been porting MicroPython to Pokitto (http://www.pokitto.com) gaming device, which is based on LPC11U68 (NXP ARM Cortex-M0+), and it runs beautifully there :-) I am also porting a small subset of PyGame over MicroPython to be able to use Pokitto's color screen capabilities. In that I have faced the problem, that I cannot define instance attributes to C based Python class. For example, I would like to use pygame.Rect class as follows:

[code]
cx = myRect.x
[/code]

instead of:

[code]
cx = myRect.x()
[/code]

I could not find any example in the existing classes in MicroPython. All seem to use getter and setter methods. The rest of the PyGame Python code uses e.g. the Rect class so it would be very nice to have attributes, so I can keep existing implementation as such.

Below is my current (simplified) implementation of the Rect class:


[code]#include <stdio.h>
#include <string.h>

#include "py/nlr.h"
#include "py/obj.h"
#include "py/runtime.h"

#if 1 //MICROPY_PYGAME

// *** Rect member data
typedef struct _mp_obj_rect_t {
mp_obj_base_t base;
int16_t x;
int16_t y;
int16_t w;
int16_t h;
} mp_obj_rect_t;

// *** Rect class

STATIC mp_obj_t rect_x(size_t n_args, const mp_obj_t *args) {

mp_obj_rect_t *self = MP_OBJ_TO_PTR(args[0]);
return mp_obj_new_int(self->x);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rect_x_obj, 1, 1, rect_x);

STATIC mp_obj_t rect_y(size_t n_args, const mp_obj_t *args) {

mp_obj_rect_t *self = MP_OBJ_TO_PTR(args[0]);
return mp_obj_new_int(self->y);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rect_y_obj, 1, 1, rect_y);

STATIC mp_obj_t rect_width(size_t n_args, const mp_obj_t *args) {

mp_obj_rect_t *self = MP_OBJ_TO_PTR(args[0]);
return mp_obj_new_int(self->w);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rect_width_obj, 1, 1, rect_width);

STATIC mp_obj_t rect_height(size_t n_args, const mp_obj_t *args) {

mp_obj_rect_t *self = MP_OBJ_TO_PTR(args[0]);
return mp_obj_new_int(self->h);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rect_height_obj, 1, 1, rect_height);

STATIC const mp_rom_map_elem_t rect_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_x), MP_ROM_PTR(&rect_x_obj) },
{ MP_ROM_QSTR(MP_QSTR_y), MP_ROM_PTR(&rect_y_obj) },
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&rect_width_obj) },
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&rect_height_obj) },
};
STATIC MP_DEFINE_CONST_DICT(rect_locals_dict, rect_locals_dict_table);

STATIC mp_obj_t rect_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {

// Check params.
mp_arg_check_num(n_args, n_kw, 4, 4, false);

// Create member data struct.
mp_obj_rect_t *o = m_new_obj(mp_obj_rect_t);
o->base.type = type; // class

// Create member data
o->x = mp_obj_get_int(args[0]);
o->y = mp_obj_get_int(args[1]);
o->w = mp_obj_get_int(args[2]);
o->h = mp_obj_get_int(args[3]);

return MP_OBJ_FROM_PTR(o);
}

STATIC const mp_obj_type_t mp_type_rect = {
{ &mp_type_type },
.name = MP_QSTR_Rect,
.make_new = rect_make_new,
.locals_dict = (mp_obj_dict_t*)&rect_locals_dict,
};

// *** upygame module

STATIC const mp_rom_map_elem_t pygame_module_globals_table[] = {

{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_upygame) }, // pygame module name
{ MP_ROM_QSTR(MP_QSTR_Rect), MP_ROM_PTR(&mp_type_rect) }, // Rect class
};

STATIC MP_DEFINE_CONST_DICT(pygame_module_globals, pygame_module_globals_table);

const mp_obj_module_t mp_module_pygame = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&pygame_module_globals,
};

#endif // MICROPY_PYGAME
[/code]
Last edited by Hanski on Tue Sep 26, 2017 9:57 am, edited 2 times in total.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Implementing Python class instance attributes with C ?

Post by dhylands » Fri Sep 22, 2017 4:35 pm

The python object type structure has an attr method which is used for loading/storing/deleting attributes.

One of the few examples in the codebase can be found here:
https://github.com/micropython/micropyt ... pes.c#L645

User avatar
Hanski
Posts: 8
Joined: Sat Aug 12, 2017 9:54 am

Re: Implementing Python class instance attributes with C ?

Post by Hanski » Fri Sep 22, 2017 7:02 pm

Thanks, but it looks a bit too complicated as an example. It would be great if you cared to tell me how should I change my Rect class?

User avatar
Hanski
Posts: 8
Joined: Sat Aug 12, 2017 9:54 am

Re: Implementing Python class instance attributes with C ?

Post by Hanski » Tue Sep 26, 2017 4:48 am

Ok, I figured it out. Here is a simplified example of a Rect class that contains both mehtods and instance attributes. Feel free to suggest any fixes or improvements.

These are the attributes and methods:
Rect.x
Rect.y
Rect.width
Rect.height
Rect.colliderect(Rect)

Code: Select all

#include <stdio.h>
#include <string.h>

#include "py/nlr.h"
#include "py/obj.h"
#include "py/runtime.h"

#if 1 //MICROPY_PYGAME

// *** Rect member data
typedef struct _mp_obj_rect_t {
mp_obj_base_t base;
int16_t x;
int16_t y;
int16_t w;
int16_t h;
} mp_obj_rect_t;

// *** Common functions

STATIC void generic_method_lookup(mp_obj_t obj, qstr attr, mp_obj_t *dest) {
    mp_obj_type_t *type = mp_obj_get_type(obj);
    if (type->locals_dict != NULL) {
         // generic method lookup
         // this is a lookup in the object (ie not class or type)
         assert(type->locals_dict->base.type == &mp_type_dict); // MicroPython restriction, for now
         mp_map_t *locals_map = &type->locals_dict->map;
         mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP);
         if (elem != NULL) {
             mp_convert_member_lookup(obj, type, elem->value, dest);
         }
    }
}

// *** Rect class

STATIC bool do_rects_intersect (mp_obj_rect_t *A, mp_obj_rect_t *B)
{
    return (A->x < B->x + B->w && A->y < B->y + B->h &&
            A->x + A->w > B->x && A->y + A->h > B->y);
}

STATIC mp_obj_t rect_colliderect(size_t n_args, const mp_obj_t *args)
{
    mp_obj_rect_t *self = MP_OBJ_TO_PTR(args[0]);
    mp_obj_rect_t *aRect = MP_OBJ_TO_PTR(args[1]);

    return mp_obj_new_bool(do_rects_intersect (self, aRect));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(rect_colliderect_obj, 2, 2, rect_colliderect);

STATIC mp_obj_t rect_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set_val) {
    mp_obj_rect_t *self = MP_OBJ_TO_PTR(self_in);

    if (set_val == MP_OBJ_NULL) {
        if(attr == MP_QSTR_x)
            return mp_obj_new_int(self->x);
        else if(attr == MP_QSTR_y)
            return mp_obj_new_int(self->y);
        else if(attr == MP_QSTR_width)
            return mp_obj_new_int(self->w);
        else if(attr == MP_QSTR_height)
            return mp_obj_new_int(self->h);
    }
    else {
        if(attr == MP_QSTR_x)
            self->x = (mp_int_t)mp_obj_get_int(set_val);
        else if(attr == MP_QSTR_y)
            self->y = (mp_int_t)mp_obj_get_int(set_val);
        else if(attr == MP_QSTR_width)
            self->w = (mp_int_t)mp_obj_get_int(set_val);
        else if(attr == MP_QSTR_height)
            self->h = (mp_int_t)mp_obj_get_int(set_val);

        return set_val; // just !MP_OBJ_NULL
    }

    // Should be unreachable once all cases are handled
    return MP_OBJ_NULL;
}

STATIC void rect_attr(mp_obj_t obj, qstr attr, mp_obj_t *dest) {

    if(attr == MP_QSTR_x || attr == MP_QSTR_y || attr == MP_QSTR_width || attr == MP_QSTR_height ) {

        if (dest[0] == MP_OBJ_NULL) {
            // load attribute
            mp_obj_t val = rect_attr_op(obj, attr, MP_OBJ_NULL);
            dest[0] = val;
        } else {
            // delete/store attribute
            if (rect_attr_op(obj, attr, dest[1]) != MP_OBJ_NULL)
                dest[0] = MP_OBJ_NULL; // indicate success
        }
    }
    else {
        // A method call
        generic_method_lookup(obj, attr, dest);
    }
}

STATIC const mp_rom_map_elem_t rect_locals_dict_table[] = {

    // Instance attributes
    { MP_ROM_QSTR(MP_QSTR_x), MP_ROM_INT(0) },
    { MP_ROM_QSTR(MP_QSTR_y), MP_ROM_INT(0) },
    { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_INT(0) },
    { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_INT(0) },

    // Methods
    { MP_ROM_QSTR(MP_QSTR_colliderect), MP_ROM_PTR(&rect_colliderect_obj) },
};
STATIC MP_DEFINE_CONST_DICT(rect_locals_dict, rect_locals_dict_table);

STATIC mp_obj_t rect_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {

    // Check params.
	mp_arg_check_num(n_args, n_kw, 1, 4, false);

    // Create member data struct.
    mp_obj_rect_t *o = m_new_obj(mp_obj_rect_t);
    o->base.type = type;  // class

    // Init from x,y,w,h

    // Create member data
    o->x = mp_obj_get_int(args[0]);
    o->y = mp_obj_get_int(args[1]);
    o->w = mp_obj_get_int(args[2]);
    o->h = mp_obj_get_int(args[3]);
    
    return MP_OBJ_FROM_PTR(o);
 }

STATIC const mp_obj_type_t mp_type_rect = {
    { &mp_type_type },
    .name = MP_QSTR_Rect,
    .make_new = rect_make_new,
    .locals_dict = (mp_obj_dict_t*)&rect_locals_dict,
    .attr = rect_attr,
};

// *** upygame module

STATIC const mp_rom_map_elem_t pygame_module_globals_table[] = {

{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_upygame) }, // pygame module name
{ MP_ROM_QSTR(MP_QSTR_Rect), MP_ROM_PTR(&mp_type_rect) },	// Rect class 
};

STATIC MP_DEFINE_CONST_DICT(pygame_module_globals, pygame_module_globals_table);

const mp_obj_module_t mp_module_pygame = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&pygame_module_globals,
};

#endif // MICROPY_PYGAME

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Implementing Python class instance attributes with C ?

Post by dhylands » Tue Sep 26, 2017 5:03 pm

You'd be better off use:

Code: Select all

return (mp_obj_t)&mp_const_none_obj;
rather than

Code: Select all

return MP_OBJ_NULL;
Returning MP_OBJ_NULL will cause things to crash if it ever hits that case.

User avatar
Hanski
Posts: 8
Joined: Sat Aug 12, 2017 9:54 am

Re: Implementing Python class instance attributes with C ?

Post by Hanski » Tue Sep 26, 2017 6:14 pm

Ok, thanks.

Post Reply