blueloveTH пре 1 година
родитељ
комит
7828ad2eec
1 измењених фајлова са 110 додато и 1 уклоњено
  1. 110 1
      docs/1_5_0.md

+ 110 - 1
docs/1_5_0.md

@@ -21,5 +21,114 @@ vm->bind_method<M>(obj, "get", f_get);
 
 
 // new ARGC-based binding
 // new ARGC-based binding
 vm->bind_func(obj, "add", N, f_add);
 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
+    ...
 ```
 ```