micropython用c添加接口——添加type
前面讲的如何向micropython添加一个module并在module下面添加function。可以看出来很多的操作步骤都是有类比性的。这一篇就重点讲如何添加type,以及给type 类添加function功能。聪明的小伙伴已经可以猜到和之前添加module差不多。但是除了差不多还是有一些区别的,这也是这篇文章的重点。
先看下之前介绍框架的时候的一张图:
你首先要把这个框架图映在脑子里,这样等一会儿你看代码的时候就不会晕头转向。抓住核心再深入细节才是完美的思路。添加type相对来说会比添加module复杂一些,所以代码量也会多一些。好,下面正式进入主题:
第一步:从最简单开始,先添加一个空的type类型到我们之前的modtest中。
在ports/esp32下面新建一个文件:modtest_math.c
打开文件,添加如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include "stdint.h" #include "stdio.h" #include "py/obj.h" #include "py/runtime.h" //定义type的locals_dict_type STATIC const mp_rom_map_elem_t math_locals_dict_table[] = { }; //这个定义字典的宏定义 STATIC MP_DEFINE_CONST_DICT(math_locals_dict, math_locals_dict_table); //定义一个mp_obj_type_t 类型的结构体。注意这里和定义module使用的类型是不一样的 const mp_obj_type_t modtest_math_type = { .base={ &mp_type_type }, .name = MP_QSTR_math, //type 类的name属性是放在这里定义的,而不是放在DICT中 .locals_dict = (mp_obj_dict_t*)&math_locals_dict, //注册math_locals_dict }; |
看上面的代码结构和定义一个module是不是类似的,只是需要注意的地方就是,这里定义type就要用mp_obj_type_t 该类型,modtest_math_type 就是用来向module注册的接口。再mp_obj_type_t 中的成员和mp_obj_module_t是不一样的。在 math_locals_dict_table里面我们什么都没添加,后面我们会添加function就是添加到这里。我们一步步来,先确保添加type类型是OK的。
第二步:我们新建的c文件那肯定是要添加到makefile文件里面,要参与编译啊
打开Makefile文件,找到我们之前添加modtest.c的地方,排上队把新建的modtest_math.c按照一样的格式写在后面:
1 2 3 4 5 6 |
SRC_C = \ main.c \ ………… modtest.c\ modtest_math.c\ $(SRC_MOD) |
第三步:把定义的modtest_math_type 注册到modtest里,打开modtest.c添加如下代码:
1 2 3 4 5 6 7 |
extern const mp_obj_type_t modtest_math_type; //引用外部定义 STATIC const mp_rom_map_elem_t modtest_globals_table[] = { {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_modtest)}, {MP_OBJ_NEW_QSTR(MP_QSTR_test0), MP_ROM_PTR(&modtest_obj_test0)}, {MP_OBJ_NEW_QSTR(MP_QSTR_test1), MP_ROM_PTR(&modtest_obj_test1)}, {MP_OBJ_NEW_QSTR(MP_QSTR_math), MP_ROM_PTR(&modtest_math_type)}, //这个是我们新添加的,把modtest_math_type 注册进来 }; |
添加完毕以后,编译烧录再测试,结果如下就证明添加好了:
从测试结果看我们名为math的type已经添加成功了,用type() 检查下类型,没错,是class type类的。
第四步:在type中添加无参数的function
直接上代码:
1 2 3 4 5 6 7 8 9 10 11 12 |
//定义无参数函数 STATIC mp_obj_t math_nothing() { printf("This is a function in class type and no parameter\n"); return mp_const_none; } //使用函数参数使用对应宏定义 STATIC MP_DEFINE_CONST_FUN_OBJ_0(math_nothing_obj,math_nothing); STATIC const mp_rom_map_elem_t math_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_nothing), MP_ROM_PTR(&math_nothing_obj) }, //添加的DICT里面去 }; |
这个步骤没啥可解释的,和module添加无参数的函数是一样的:
编译执行验证结果如下:
第五步:在type中添加有一个参数的function(注意,关键点来了)
惯性思维让我以为在type中添加带有一个参数的函数会和module一样,折腾了两个小时候才发现这里和module的区别还是很大。你可以自行尝试,我这里只提供正确的思路。
type类型在python中是类,而要操作带参数的函数就需要实例化一个对象出来:比如 mymath=modtest.math() 那么对应到c语言肯定要有一个对应的分配空间创建对象的函数,还有表示对象的结构体。
在modtest_math.c中添加一个math_obj_t 的结构体,用来表示一个math对象的结构:
1 2 3 4 5 6 |
typedef struct _math_obj_t { mp_obj_base_t base; //定义的对象结构体要包含该成员 uint16_t value1; //下面的成员,根据需要自己添加 uint16_t value2; }math_obj_t; |
接下来为modtest_matg_type 添加.make_new 属性,以及对应的make new函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//make new 穿件一个实例对象的函数 STATIC mp_obj_t modtest_math_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 0, 0, true); //检查参数个数 math_obj_t *self=m_new_obj(math_obj_t); //创建对象,分配空间 self->base.type=&modtest_math_type; //定义对象的类型 return MP_OBJ_FROM_PTR(self); //返回对象的指针 } const mp_obj_type_t modtest_math_type = { .base={ &mp_type_type }, .name = MP_QSTR_math, .make_new=modtest_math_make_new, //这个是我们新添加的make new属性 .locals_dict = (mp_obj_dict_t*)&math_locals_dict, }; |
好了,现在我们的math就可以进行实例化了。看到这里,你是不是以为和“添加有一个参数的function”的标题跑偏了?no,no!前面的实现了,我们才能实现下面的功能。
添加如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//定义math_add函数 mp_obj_t math_add(mp_obj_t self_in,mp_obj_t data) { math_obj_t *self=MP_OBJ_TO_PTR(self_in); //从第一个参数里面取出对象的指针 self->value1=100; self->value2=mp_obj_get_int(data); //从第二个参数里面取出整型数值 printf("100+%d=\n",self->value2); return mp_obj_new_int(self->value1+self->value2); //返回计算的结果 } //注意两点,我这里使用的是OBJ_2而不是OBJ_1 STATIC MP_DEFINE_CONST_FUN_OBJ_2(math_add_obj, math_add); STATIC const mp_rom_map_elem_t math_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_nothing), MP_ROM_PTR(&math_nothing_obj) }, { MP_ROM_QSTR(MP_QSTR_add), MP_ROM_PTR(&math_add_obj) }, //把我们定义的function对象添加到DICT中 }; |
先看运行结果:
在python中我们先定义实例化一个对象m=modtest.math(),然后就可以使用传递参数的方法了:m.add(200) 计算结果也正确
那么有没有感觉很奇怪,我们这里m.add(200)只是传递了一个参数进去,但是在代码中我们却使用了MP_DEFINE_CONST_FUN_OBJ_2两个参数的宏。这里也是和定义module的function区别大的地方。在type型class 中传递参数的时候,默认第一个并不是我们在python层面填进去的参数,而是一个实例化对象的指针,第二个参数才是我们传递进去的。
可能有点晕,自己动手写代码操作操作就明白了!!
如还有其他疑问,欢迎加QQ:849664628 探讨