Page 1 of 1

registering python special methods in C class

Posted: Wed Jun 26, 2019 6:38 pm
by v923z
Hi all,

I am trying to implement a class in C, and would like to implement the special method __add__(). I was hoping that

Code: Select all

a+b 
would be evaluated, if

Code: Select all

a = testObj()
b = testObj()
However, I get the following traceback:

Code: Select all

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported types for __add__: 'testObj', 'testObj'
Curiously,

Code: Select all

a.__add__(b)
does work as expected. Here is my implementation of __add__():

Code: Select all

STATIC mp_obj_t test_add(mp_obj_t self_in, mp_obj_t other_in) {
	test_obj_t *self = MP_OBJ_TO_PTR(self_in);
	printf("here 1\n");
	test_obj_t *other = MP_OBJ_TO_PTR(other_in);
	printf("here 2\n");
	self->n += 1;
	printf("here 3\n");
	other->n += 1;
	printf("here 4\n");
	return mp_const_none;
}

MP_DEFINE_CONST_FUN_OBJ_2(test_add_obj, test_add);
and then the function is added to the local dictionary as

Code: Select all

	{ MP_ROM_QSTR(MP_QSTR___add__), MP_ROM_PTR(&test_add_obj) },	
What I don't quite understand is why the expression

Code: Select all

a+b
doesn't even call the method .__add__(). If it did, then I should see the four printouts, which is not the case.

The class structure is defined as

Code: Select all

typedef struct _test_obj_t {
	mp_obj_base_t base;
	uint8_t n;
} test_obj_t;
On the other hand, if I define the class in python as

Code: Select all

class T:

	def __init__(self):
		self.n = 1
		
	def __add__(self, other):
		return self.n + other.n
		
then

Code: Select all

a, b = T(), T()
a+b
returns 2, as expected. What the heck is going on here? Have I got to register test_add/test_add_obj in a different way?

I have also looked at the micropython code, but it seems to me that the only place, where __add__ appears is objtype.c, and I am not any wiser by reading the code there.

Well, if anyone could shed some light on this problem, I would appreciate it. I would even go as far as offering a free beer, if we bump into each other somewhere ;)

Thanks,

Zoltán

Re: registering python special methods in C class

Posted: Sat Jun 29, 2019 9:21 am
by v923z
Hi Zoltán,

If you still haven't figured out what is wrong, here is a hint: special methods have to be registered in your class definition, where you have the .name, the .make_new, .print methods, and the .locals_dict. In that very structure you should also have .unary_op for things like __len__, or .binary_op for addition, multiplication, and similar. So, your structure should look like

Code: Select all

const mp_obj_type_t someobject_type = {
	{ &mp_type_type },
	.name = MP_QSTR_objectname,
	.print = object_print,
	.make_new = object_make_new,
	.unary_op = object_unary_op,
	.binary_op = object_binary_op,
	.locals_dict = (mp_obj_dict_t*)&module_locals_dict,
};
Now, in the object_unary_op function you have to parse the type of the unary operation. The function signature should be

Code: Select all

STATIC mp_obj_t object_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
	test_obj_t *self = MP_OBJ_TO_PTR(self_in);
    switch (op) {
        case MP_UNARY_OP_BOOL: return  1; /* return whatever Boolean you want */
        case MP_UNARY_OP_LEN: return MP_OBJ_NEW_SMALL_INT(1); /* return whatever you want to signify the length of your object
        default: return MP_OBJ_NULL; // op not supported
    }
}
The case of binary operations is similar, except, you have now two operands, i.e., three arguments for the function.

I hope this helps!

Cheers,

Zoltán

PS: don't forget the beer that you promised in your original post ;)

Re: registering python special methods in C class

Posted: Sat Jun 29, 2019 9:50 am
by rpr
Hopefully Zoltan can find Zoltan's fridge easily. :D