From 7828ad2eecca4153fd8d0ec99acb6d157140441f Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sat, 4 May 2024 23:04:22 +0800 Subject: [PATCH] Update 1_5_0.md --- docs/1_5_0.md | 111 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/docs/1_5_0.md b/docs/1_5_0.md index 0c7c7f31..70fc1830 100644 --- a/docs/1_5_0.md +++ b/docs/1_5_0.md @@ -21,5 +21,114 @@ vm->bind_method(obj, "get", f_get); // new ARGC-based binding vm->bind_func(obj, "add", N, f_add); -vm->bind_func(obj, "get", M+1, f_get); +vm->bind_func(obj, "get", M+1, f_get); // +1 for `self` +``` + +## Class bindings + +Previously, if we want to write bindings for a C++ class, we need to add `PY_CLASS` macro and implement a magic method `_register`. This way was known as an intrusive way. For example: + +```cpp +struct Point{ + PY_CLASS(Point, test, Point) + + int x, y; + + static void _register(VM* vm, PyObject* mod, PyObject* type){ + // do bindings here + } +}; + +int main(){ + VM* vm = new VM(); + PyObject* mod = vm->new_module("test"); + Point::register_class(vm, mod); + return 0; +} +``` + +In `v1.5.0`, the `PY_CLASS` macro was deprecated. We introduced a non-intrusive way to write class bindings via `vm->register_user_class<>`. The example above can be rewritten as follows: + +```cpp +struct Point{ + int x, y; +}; + +int main(){ + VM* vm = new VM(); + PyObject* mod = vm->new_module("test"); + vm->register_user_class(mod, "Point", + [](VM* vm, PyObject* mod, PyObject* type){ + // do bindings here + }); + return 0; +} +``` + +The magic method `_register` is kept for users who still wants to use the intrusive way. +This is achieved by an overloaded version of `vm->register_user_class<>`. For example: + +```cpp +struct Point{ + int x, y; + + static void _register(VM* vm, PyObject* mod, PyObject* type){ + // do bindings here (if you like the intrusive way) + } +}; + +int main(){ + VM* vm = new VM(); + PyObject* mod = vm->new_module("test"); + vm->register_user_class(mod, "Point"); + return 0; +} +``` + +## Optimization of `vm->bind__next__` + +`vm->bind__next__` is a special method that is used to implement the iterator protocol. +Previously, if you want to return multiple values, you need to pack them into a tuple. +For example: + +```cpp +vm->bind__next__(type, [](VM* vm, PyObject* _0){ + // ... + PyObject* a = VAR(1); + PyObject* b = VAR(2); + return VAR(Tuple(a, b)); +}); +``` + +```python +for a, b in my_iter: + # There is tuple creation and destruction for each iteration + ... +``` + +It is not efficient because unnecessary tuple creation and destruction are involved. +In `v1.5.0`, you need to use stack-based style to reimplement the above code: + +```cpp +vm->bind__next__(type, [](VM* vm, PyObject* _0) -> unsigned{ + // ... + PyObject* a = VAR(1); + PyObject* b = VAR(2); + vm->s_data.push(a); // directly push to the stack + vm->s_data.push(b); // directly push to the stack + return 2; // return the number of values +}); +``` + +In this way, the interpreter only creates a tuple when it is necessary. +It can improve ~25% performance when iterating over a large array. + +```python +for a, b in my_iter: + # No tuple creation and destruction + ... + +for t in my_iter: + # Create a tuple lazily + ... ``` \ No newline at end of file