mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-19 19:10:17 +00:00
Update 1_5_0.md
This commit is contained in:
parent
e8a76806c7
commit
7828ad2eec
111
docs/1_5_0.md
111
docs/1_5_0.md
@ -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
|
||||
...
|
||||
```
|
Loading…
x
Reference in New Issue
Block a user