Update 1_5_0.md

This commit is contained in:
blueloveTH 2024-05-04 23:04:22 +08:00
parent e8a76806c7
commit 7828ad2eec

View File

@ -21,5 +21,114 @@ vm->bind_method<M>(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<Point>(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<Point>(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
...
```