Skip to content

Commit a67989a

Browse files
laurensvalkdpgeorge
authored andcommitted
examples/usercmodule: Add example of a native C class.
This shows how ports can add their own custom types/classes. It is part of the unix coverage build, so we can use it for tests too. Signed-off-by: Laurens Valk <[email protected]>
1 parent 1d27c7d commit a67989a

File tree

4 files changed

+95
-3
lines changed

4 files changed

+95
-3
lines changed

docs/develop/cmodules.rst

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,12 @@ A MicroPython user C module is a directory with the following files:
9595
Basic example
9696
-------------
9797

98-
This simple module named ``cexample`` provides a single function
99-
``cexample.add_ints(a, b)`` which adds the two integer args together and returns
100-
the result. It can be found in the MicroPython source tree
98+
The ``cexample`` module provides examples for a function and a class. The
99+
``cexample.add_ints(a, b)`` function adds two integer args together and returns
100+
the result. The ``cexample.Timer()`` type creates timers that can be used to
101+
measure the elapsed time since the object is instantiated.
102+
103+
The module can be found in the MicroPython source tree
101104
`in the examples directory <https://github.com/micropython/micropython/tree/master/examples/usercmodule/cexample>`_
102105
and has a source file and a Makefile fragment with content as described above::
103106

@@ -272,3 +275,13 @@ can now be accessed in Python just like any other builtin module, e.g.
272275
import cexample
273276
print(cexample.add_ints(1, 3))
274277
# should display 4
278+
279+
.. code-block:: python
280+
281+
from cexample import Timer
282+
from time import sleep_ms
283+
284+
watch = Timer()
285+
sleep_ms(1000)
286+
print(watch.time())
287+
# should display approximately 1000

examples/usercmodule/cexample/examplemodule.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Include MicroPython API.
22
#include "py/runtime.h"
33

4+
// Used to get the time in the Timer class example.
5+
#include "py/mphal.h"
6+
47
// This is the function which will be called from Python as cexample.add_ints(a, b).
58
STATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {
69
// Extract the ints from the micropython input objects.
@@ -13,6 +16,59 @@ STATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) {
1316
// Define a Python reference to the function above.
1417
STATIC MP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints);
1518

19+
// This structure represents Timer instance objects.
20+
typedef struct _example_Timer_obj_t {
21+
// All objects start with the base.
22+
mp_obj_base_t base;
23+
// Everything below can be thought of as instance attributes, but they
24+
// cannot be accessed by MicroPython code directly. In this example we
25+
// store the time at which the object was created.
26+
mp_uint_t start_time;
27+
} example_Timer_obj_t;
28+
29+
// This is the Timer.time() method. After creating a Timer object, this
30+
// can be called to get the time elapsed since creating the Timer.
31+
STATIC mp_obj_t example_Timer_time(mp_obj_t self_in) {
32+
// The first argument is self. It is cast to the *example_Timer_obj_t
33+
// type so we can read its attributes.
34+
example_Timer_obj_t *self = MP_OBJ_TO_PTR(self_in);
35+
36+
// Get the elapsed time and return it as a MicroPython integer.
37+
mp_uint_t elapsed = mp_hal_ticks_ms() - self->start_time;
38+
return mp_obj_new_int_from_uint(elapsed);
39+
}
40+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(example_Timer_time_obj, example_Timer_time);
41+
42+
// This represents Timer.__new__ and Timer.__init__, which is called when
43+
// the user instantiates a Timer object.
44+
STATIC mp_obj_t example_Timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
45+
// Allocates the new object and sets the type.
46+
example_Timer_obj_t *self = m_new_obj(example_Timer_obj_t);
47+
self->base.type = (mp_obj_type_t *)type;
48+
49+
// Initializes the time for this Timer instance.
50+
self->start_time = mp_hal_ticks_ms();
51+
52+
// The make_new function always returns self.
53+
return MP_OBJ_FROM_PTR(self);
54+
}
55+
56+
// This collects all methods and other static class attributes of the Timer.
57+
// The table structure is similar to the module table, as detailed below.
58+
STATIC const mp_rom_map_elem_t example_Timer_locals_dict_table[] = {
59+
{ MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&example_Timer_time_obj) },
60+
};
61+
STATIC MP_DEFINE_CONST_DICT(example_Timer_locals_dict, example_Timer_locals_dict_table);
62+
63+
// This defines the type(Timer) object.
64+
MP_DEFINE_CONST_OBJ_TYPE(
65+
example_type_Timer,
66+
MP_QSTR_Timer,
67+
MP_TYPE_FLAG_NONE,
68+
make_new, example_Timer_make_new,
69+
locals_dict, &example_Timer_locals_dict
70+
);
71+
1672
// Define all properties of the module.
1773
// Table entries are key/value pairs of the attribute name (a string)
1874
// and the MicroPython object reference.
@@ -21,6 +77,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints);
2177
STATIC const mp_rom_map_elem_t example_module_globals_table[] = {
2278
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cexample) },
2379
{ MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) },
80+
{ MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&example_type_Timer) },
2481
};
2582
STATIC MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table);
2683

tests/misc/cexample_class.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# test custom native class
2+
3+
try:
4+
import cexample
5+
import time
6+
except ImportError:
7+
print("SKIP")
8+
raise SystemExit
9+
10+
t = cexample.Timer()
11+
12+
print(t)
13+
print(t.time() <= 1)
14+
15+
time.sleep_ms(100)
16+
17+
elapsed = t.time()
18+
19+
if not (99 <= elapsed <= 110):
20+
print("Elapsed time should be approx. 100ms but it is", elapsed)

tests/misc/cexample_class.py.exp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<Timer>
2+
True

0 commit comments

Comments
 (0)