--- icon: log title: 'Upgrade to v1.5.0' order: 25 --- We are applying a major API refactoring in this release. The main goal is to make the API more consistent and easier to use. We are also adding new features and improvements. This release is not backward compatible with the previous versions. Please read the following guide to upgrade your project. ## New style bindings We introduced the new style bindings `vm->bind` in [`v1.1.3`](https://github.com/pocketpy/pocketpy/releases/tag/v1.1.3) and deprecated the old style bindings `vm->bind_func<>` and `vm->bind_method<>`. In this release, we added an ARGC-based binding `vm->bind_func` (without template parameter) to replace the old style bindings. If you are still using `vm->bind_func<>` and `vm->bind_method<>`, please update your code to use `vm->bind_func` instead. ```cpp // old style (removed) vm->bind_func(obj, "add", f_add); 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); // +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 ... ```