diff --git a/README.md b/README.md index af2073f2..2860c681 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ int main(){ vm->exec("a = [1, 2, 3]"); // Eval the sum of the list - PyObject* result = vm->eval("sum(a)"); + PyVar result = vm->eval("sum(a)"); std::cout << "Sum of the list: "<< py_cast(vm, result) << std::endl; // 6 // Bindings @@ -117,7 +117,7 @@ int main(){ }); // Call the function - PyObject* f_add = vm->_main->attr("add"); + PyVar f_add = vm->_main->attr("add"); result = vm->call(f_add, py_var(vm, 3), py_var(vm, 7)); std::cout << "Sum of 2 variables: "<< py_cast(vm, result) << std::endl; // 10 diff --git a/docs/1_5_0.md b/docs/1_5_0.md index b7e5e44b..a516c615 100644 --- a/docs/1_5_0.md +++ b/docs/1_5_0.md @@ -6,6 +6,18 @@ 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. +## Alias for `PyObject*` + +We are going to make a very big change in `v2.0` which may break everything! +In order to make this upgrade easier, we are introducing an alias for `PyObject*` in `v1.5.0`. + +```cpp +using PyVar = PyObject*; +``` + +Please replace all `PyObject*` with `PyVar` in your code in order to be compatible with `v2.0` in the future. +We will try our best to keep the compatibility with `v1.x` series. + ## Old 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<>`. @@ -34,14 +46,14 @@ struct Point{ int x, y; - static void _register(VM* vm, PyObject* mod, PyObject* type){ + static void _register(VM* vm, PyVar mod, PyVar type){ // do bindings here } }; int main(){ VM* vm = new VM(); - PyObject* mod = vm->new_module("test"); + PyVar mod = vm->new_module("test"); Point::register_class(vm, mod); return 0; } @@ -56,9 +68,9 @@ struct Point{ int main(){ VM* vm = new VM(); - PyObject* mod = vm->new_module("test"); + PyVar mod = vm->new_module("test"); vm->register_user_class(mod, "Point", - [](VM* vm, PyObject* mod, PyObject* type){ + [](VM* vm, PyVar mod, PyVar type){ // do bindings here }); return 0; @@ -72,14 +84,14 @@ This is achieved by an overloaded version of `vm->register_user_class<>`. For ex struct Point{ int x, y; - static void _register(VM* vm, PyObject* mod, PyObject* type){ + static void _register(VM* vm, PyVar mod, PyVar type){ // do bindings here (if you like the intrusive way) } }; int main(){ VM* vm = new VM(); - PyObject* mod = vm->new_module("test"); + PyVar mod = vm->new_module("test"); vm->register_user_class(mod, "Point"); return 0; } @@ -107,11 +119,11 @@ Previously, if you want to return multiple values, you need to pack them into a For example: ```cpp -vm->bind__next__(type, [](VM* vm, PyObject* _0){ +vm->bind__next__(type, [](VM* vm, PyVar _0){ if(is_end) return vm->StopIteration; // ... - PyObject* a = VAR(1); - PyObject* b = VAR(2); + PyVar a = VAR(1); + PyVar b = VAR(2); return VAR(Tuple(a, b)); }); ``` @@ -126,11 +138,11 @@ It is not efficient because unnecessary tuple creation and destruction are invol 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{ +vm->bind__next__(type, [](VM* vm, PyVar _0) -> unsigned{ if(is_end) return 0; // return 0 indicates StopIteration // ... - PyObject* a = VAR(1); - PyObject* b = VAR(2); + PyVar a = VAR(1); + PyVar 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 @@ -197,7 +209,7 @@ Enabling the profiler has a performance overhead. Only enable it when you need i ## Python Stringify functions -The following functions now return `Str` object instead of `PyObject*`: +The following functions now return `Str` object instead of `PyVar`: + `vm->py_str` + `vm->py_repr` diff --git a/docs/bindings.md b/docs/bindings.md index eefd1dd9..e0fff7c5 100644 --- a/docs/bindings.md +++ b/docs/bindings.md @@ -9,18 +9,18 @@ In order to use a C/C++ library in python, you need to write bindings for it. pkpy uses an universal signature to wrap a function pointer as a python function or method that can be called in python code, i.e `NativeFuncC`. ```cpp -typedef PyObject* (*NativeFuncC)(VM*, ArgsView); +typedef PyVar (*NativeFuncC)(VM*, ArgsView); ``` + The first argument is the pointer of `VM` instance. + The second argument is an array-like object indicates the arguments list. You can use `[]` operator to get the element and call `size()` to get the length of the array. -+ The return value is a `PyObject*`, which should not be `nullptr`. If there is no return value, return `vm->None`. ++ The return value is a `PyVar`, which should not be `nullptr`. If there is no return value, return `vm->None`. ## Bind a function or method Use `vm->bind` to bind a function or method. -+ `PyObject* bind(PyObject*, const char* sig, NativeFuncC)` -+ `PyObject* bind(PyObject*, const char* sig, const char* docstring, NativeFuncC)` ++ `PyVar bind(PyVar, const char* sig, NativeFuncC)` ++ `PyVar bind(PyVar, const char* sig, const char* docstring, NativeFuncC)` ```cpp @@ -56,7 +56,7 @@ vm->bind(obj, "f() -> int", [x](VM* vm, ArgsView args){ I do not encourage you to capture something in a lambda being bound because: 1. Captured lambda runs slower and causes "code-bloat". -2. Captured values are unsafe, especially for `PyObject*` as they could leak by accident. +2. Captured values are unsafe, especially for `PyVar` as they could leak by accident. However, there are 3 ways to capture something when you really need to. The most safe and elegant way is to subclass `VM` and add a member variable. @@ -115,9 +115,9 @@ You can create a `test` module and use `vm->register_user_class<>` to bind the c ```cpp int main(){ VM* vm = new VM(); - PyObject* mod = vm->new_module("test"); + PyVar mod = vm->new_module("test"); vm->register_user_class(mod, "Point", - [](VM* vm, PyObject* mod, PyObject* type){ + [](VM* vm, PyVar mod, PyVar type){ // wrap field x vm->bind_field(type, "x", &Point::x); // wrap field y @@ -145,12 +145,12 @@ int main(){ ### Handle gc for container types -If your custom type stores `PyObject*` in its fields, you need to handle gc for them. +If your custom type stores `PyVar` in its fields, you need to handle gc for them. ```cpp struct Container{ - PyObject* a; - std::vector b; + PyVar a; + std::vector b; // ... } ``` @@ -159,8 +159,8 @@ Add a magic method `_gc_mark() const` to your custom type. ```cpp struct Container{ - PyObject* a; - std::vector b; + PyVar a; + std::vector b; // ... void _gc_mark() const{ @@ -168,7 +168,7 @@ struct Container{ if(a) PK_OBJ_MARK(a); // mark elements in b - for(PyObject* obj : b){ + for(PyVar obj : b){ if(obj) PK_OBJ_MARK(obj); } } @@ -188,7 +188,7 @@ They do not take universal function pointer as argument. You need to provide the detailed `Type` object and the corresponding function pointer. ```cpp -PyObject* f_add(VM* vm, PyObject* lhs, PyObject* rhs){ +PyVar f_add(VM* vm, PyVar lhs, PyVar rhs){ int a = py_cast(vm, lhs); int b = py_cast(vm, rhs); return py_var(vm, a + b); diff --git a/docs/cheatsheet.md b/docs/cheatsheet.md index 28ace536..e52a93eb 100644 --- a/docs/cheatsheet.md +++ b/docs/cheatsheet.md @@ -34,7 +34,7 @@ vm->exec("print('Hello!')"); Evaluate a source string ```cpp -PyObject* obj = vm->eval("123"); +PyVar obj = vm->eval("123"); std::cout << py_cast(vm, obj); // 123 ``` @@ -59,7 +59,7 @@ try{ Create primitive objects ```cpp -PyObject* obj; +PyVar obj; obj = py_var(vm, 1); // create a int obj = py_var(vm, 1.0); // create a float obj = py_var(vm, "123"); // create a string @@ -74,7 +74,7 @@ Tuple t(3); t[0] = py_var(vm, 1); t[1] = py_var(vm, 1.0); t[2] = py_var(vm, "123"); -PyObject* obj = py_var(vm, std::move(t)); +PyVar obj = py_var(vm, std::move(t)); ``` Create a list object @@ -85,7 +85,7 @@ List t; t.push_back(py_var(vm, 1)); t.push_back(py_var(vm, 1.0)); t.push_back(py_var(vm, "123")); -PyObject* obj = py_var(vm, std::move(t)); +PyVar obj = py_var(vm, std::move(t)); ``` Create a dict object @@ -95,13 +95,13 @@ Create a dict object Dict d(vm); d.set(py_var(vm, "x"), py_var(vm, 1)); d.set(py_var(vm, "y"), py_var(vm, "123")); -PyObject* obj = py_var(vm, std::move(d)); +PyVar obj = py_var(vm, std::move(d)); ``` Get native types from python objects ```cpp -PyObject* obj; +PyVar obj; i64 a = py_cast(vm, obj); f64 b = py_cast(vm, obj); Str& c = py_cast(vm, obj); // reference cast @@ -130,11 +130,11 @@ Tuple& c_ = PK_OBJ_GET(Tuple, obj); Access built-in python types ```cpp -PyObject* int_t = vm->_t(VM::tp_int); -PyObject* float_t = vm->_t(VM::tp_float); -PyObject* object_t = vm->_t(VM::tp_object); -PyObject* tuple_t = vm->_t(VM::tp_tuple); -PyObject* list_t = vm->_t(VM::tp_list); +PyVar int_t = vm->_t(VM::tp_int); +PyVar float_t = vm->_t(VM::tp_float); +PyVar object_t = vm->_t(VM::tp_object); +PyVar tuple_t = vm->_t(VM::tp_tuple); +PyVar list_t = vm->_t(VM::tp_list); ``` Access user registered types @@ -146,22 +146,22 @@ Type voidp_t = vm->_tp_user(); Check if an object is a python type ```cpp -PyObject* obj; +PyVar obj; bool ok = is_type(obj, VM::tp_int); // check if obj is an int ``` Get the type of a python object ```cpp -PyObject* obj = py_var(vm, 1); -PyObject* t = vm->_t(obj); // +PyVar obj = py_var(vm, 1); +PyVar t = vm->_t(obj); // Type type = vm->_tp(obj); // VM::tp_int ``` Convert a type object into a type index ```cpp -PyObject* int_t = vm->_t(VM::tp_int); +PyVar int_t = vm->_t(VM::tp_int); Type t = PK_OBJ_GET(Type, int_t); // t == VM::tp_int ``` @@ -171,7 +171,7 @@ Type t = PK_OBJ_GET(Type, int_t); Check an object supports attribute access ```cpp -PyObject* obj; +PyVar obj; bool ok = !is_tagged(obj) && obj->is_attr_valid(); ``` @@ -188,8 +188,8 @@ class MyClass: Get and set attributes ```cpp -PyObject* obj = vm->exec("MyClass(1, 2)"); -PyObject* x = vm->getattr(obj, "x"); // obj.x +PyVar obj = vm->exec("MyClass(1, 2)"); +PyVar x = vm->getattr(obj, "x"); // obj.x vm->setattr(obj, "x", py_var(vm, 3)); // obj.x = 3 ``` @@ -203,16 +203,16 @@ def add(a, b): Call a function ```cpp -PyObject* f_add = vm->eval("add"); -PyObject* ret = vm->call(f_add, py_var(vm, 1), py_var(vm, 2)); +PyVar f_add = vm->eval("add"); +PyVar ret = vm->call(f_add, py_var(vm, 1), py_var(vm, 2)); std::cout << py_cast(vm, ret); // 3 ``` Call a method ```cpp -PyObject* obj = vm->exec("MyClass(1, 2)"); -PyObject* ret = vm->call_method(obj, "sum"); +PyVar obj = vm->exec("MyClass(1, 2)"); +PyVar ret = vm->call_method(obj, "sum"); std::cout << CAST(i64, ret); // 3 ``` @@ -221,7 +221,7 @@ Cache the name of a function or method to avoid string-based lookup ```cpp // cache the name "add" to avoid string-based lookup const static StrName m_sum("sum"); -PyObject* ret = vm->call_method(obj, m_sum); +PyVar ret = vm->call_method(obj, m_sum); ``` ## Special operations @@ -229,50 +229,50 @@ PyObject* ret = vm->call_method(obj, m_sum); Compare two python objects ```cpp -PyObject* obj1 = py_var(vm, 1); -PyObject* obj2 = py_var(vm, 2); +PyVar obj1 = py_var(vm, 1); +PyVar obj2 = py_var(vm, 2); bool ok = vm->py_eq(obj1, obj2); ``` Convert a python object to string ```cpp -PyObject* obj = py_var(vm, 123); +PyVar obj = py_var(vm, 123); std::cout << vm->py_str(obj); // 123 ``` Get the string representation of a python object ```cpp -PyObject* obj = py_var(vm, "123"); +PyVar obj = py_var(vm, "123"); std::cout << vm->py_repr(obj); // "'123'" ``` Get the JSON representation of a python object ```cpp -PyObject* obj = py_var(vm, 123); +PyVar obj = py_var(vm, 123); std::cout << vm->py_json(obj); // "123" ``` Get the hash value of a python object ```cpp -PyObject* obj = py_var(vm, 1); +PyVar obj = py_var(vm, 1); i64 h = vm->py_hash(obj); // 1 ``` Get the iterator of a python object ```cpp -PyObject* obj = vm->eval("range(3)"); -PyObject* iter = vm->py_iter(obj); +PyVar obj = vm->eval("range(3)"); +PyVar iter = vm->py_iter(obj); ``` Get the next item of an iterator ```cpp -PyObject* obj = vm->py_next(iter); +PyVar obj = vm->py_next(iter); if(obj == vm->StopIteration){ // end of iteration }else{ @@ -283,7 +283,7 @@ if(obj == vm->StopIteration){ Convert a python iterable to a list ```cpp -PyObject* obj = vm->eval("range(3)"); +PyVar obj = vm->eval("range(3)"); List list = vm->py_list(obj); ``` @@ -328,6 +328,6 @@ vm->_lazy_modules["test"] = "pi = 3.14"; Create a native module ```cpp -PyObject* mod = vm->new_module("test"); +PyVar mod = vm->new_module("test"); vm->setattr(mod, "pi", py_var(vm, 3.14)); ``` diff --git a/docs/modules/os.md b/docs/modules/os.md index e40464f7..b5504a55 100644 --- a/docs/modules/os.md +++ b/docs/modules/os.md @@ -62,7 +62,7 @@ You can add other functions to `os` module via normal binding if you need them. For example, add `os.system`: ```cpp -PyObject* mod = vm->_modules["os"]; +PyVar mod = vm->_modules["os"]; vm->bind(mod, "system(cmd: str) -> int", [](VM* vm, ArgsView args){ const char* cmd = py_cast(vm, args[0]); diff --git a/docs/quick-start/attr.md b/docs/quick-start/attr.md index 202edf3d..eccac8c7 100644 --- a/docs/quick-start/attr.md +++ b/docs/quick-start/attr.md @@ -13,9 +13,9 @@ You can use `obj->attr()` to manipulate the instance dict of an object. VM* vm = new VM(); // get the `builtin` module -PyObject* builtins = vm->builtins; +PyVar builtins = vm->builtins; // get `dict` type -PyObject* dict = builtins->attr("dict"); +PyVar dict = builtins->attr("dict"); // set `pi = 3.14` builtins->attr().set("pi", py_var(vm, 3.14)); ``` @@ -25,9 +25,9 @@ For example, the `int` object. ```cpp // create a `int` object -PyObject* obj = py_var(vm, 1); +PyVar obj = py_var(vm, 1); // THIS IS WRONG!! WILL LEAD TO A SEGFAULT!! -PyObject* add = obj->attr("__add__"); +PyVar add = obj->attr("__add__"); ``` To determine whether an object has instance dict or not, you can use this snippet. @@ -35,7 +35,7 @@ To determine whether an object has instance dict or not, you can use this snippe ```cpp // 1. call `is_tagged` to check the object supports `->` operator // 2. call `is_attr_valid` to check the existence of instance dict -PyObject* obj = py_var(vm, 1); +PyVar obj = py_var(vm, 1); bool ok = !is_tagged(obj) && obj->is_attr_valid(); // false ``` @@ -45,7 +45,7 @@ As you can see, direct access does not take care of derived attributes or method In most cases, what you need is `getattr` and `setattr`. These two methods handle all possible cases. -#### `PyObject* getattr(PyObject* obj, StrName name, bool throw_err=true)` +#### `PyVar getattr(PyVar obj, StrName name, bool throw_err=true)` This method is equivalent to `getattr` in python. If the attribute is not found, it will return `nullptr` @@ -53,20 +53,20 @@ or throw an `AttributeError` depending on the value of `throw_err`. ```cpp // create a `int` object -PyObject* obj = py_var(vm, 1); +PyVar obj = py_var(vm, 1); // get its `__add__` method, which is a `bound_method` object -PyObject* add = vm->getattr(obj, "__add__"); +PyVar add = vm->getattr(obj, "__add__"); // call it (equivalent to `1 + 2`) -PyObject* ret = vm->call(add, py_var(vm, 2);); +PyVar ret = vm->call(add, py_var(vm, 2);); // get the result int result = py_cast(vm, ret); std::cout << result << std::endl; // 3 ``` -#### `void setattr(PyObject*, StrName, PyObject*)` +#### `void setattr(PyVar, StrName, PyVar)` This method is equivalent to `setattr` in python. It raises `TypeError` if the object does not support attribute assignment. diff --git a/docs/quick-start/call.md b/docs/quick-start/call.md index 2baae0d4..5ce38393 100644 --- a/docs/quick-start/call.md +++ b/docs/quick-start/call.md @@ -10,8 +10,8 @@ You can use `call` to invoke any python callable object, including functions, methods, classes, etc. For methods, `call_method` can be used. -+ `PyObject* call(PyObject* obj, ...)` -+ `PyObject* call_method(PyObject* obj, StrName name, ...)` ++ `PyVar call(PyVar obj, ...)` ++ `PyVar call_method(PyVar obj, StrName name, ...)` ### Example @@ -27,22 +27,22 @@ print(obj["a"]) # print the value First, create an empty dict object, ```cpp -PyObject* tp = vm->builtins->attr("dict"); -PyObject* obj = vm->call(tp); // this is a `dict` +PyVar tp = vm->builtins->attr("dict"); +PyVar obj = vm->call(tp); // this is a `dict` ``` And set a key-value pair, ```cpp -PyObject* _0 = py_var(vm, "a"); -PyObject* _1 = py_var(vm, 5); +PyVar _0 = py_var(vm, "a"); +PyVar _1 = py_var(vm, 5); vm->call_method(obj, "__setitem__", _0, _1); ``` And get the value, ```cpp -PyObject* ret = vm->call_method(obj, "__getitem__", _0); +PyVar ret = vm->call_method(obj, "__getitem__", _0); std::cout << py_cast(vm, i64); ``` @@ -55,17 +55,17 @@ you should use `vm->vectorcall`. This is a low-level, stack-based API. 4. Call `vm->vectorcall` with the number of arguments. ```cpp -PyObject* f_sum = vm->builtins->attr("sum"); +PyVar f_sum = vm->builtins->attr("sum"); List args(N); // a list of N arguments vm->s_data.push_back(f_sum); vm->s_data.push_back(PY_NULL); // self -for(PyObject* arg : args) { +for(PyVar arg : args) { vm->s_data.push_back(arg); } -PyObject* ret = vm->vectorcall(args.size()); +PyVar ret = vm->vectorcall(args.size()); ``` diff --git a/docs/quick-start/exec.md b/docs/quick-start/exec.md index 2af6a713..f0e74abd 100644 --- a/docs/quick-start/exec.md +++ b/docs/quick-start/exec.md @@ -8,20 +8,20 @@ order: 93 Once you have a `VM` instance, you can execute python code by calling `exec` method. -#### `PyObject* exec(Str source, Str filename, CompileMode mode, PyObject* _module=nullptr)` +#### `PyVar exec(Str source, Str filename, CompileMode mode, PyVar _module=nullptr)` + `source`, the python source code to be executed + `filename`, the filename of the source code. This is used for error reporting + `mode`, the compile mode. See below for details + `module`, the module where the code will be executed. If `nullptr`, the code will be executed in the `__main__` module -`exec` handles possible exceptions and returns a `PyObject*`. +`exec` handles possible exceptions and returns a `PyVar`. If the execution is not successful, e.g. a syntax error or a runtime exception, the return value will be `nullptr`. There are also overloaded versions of `exec` and `eval`, which is useful for simple execution: -+ `PyObject* exec(Str source)` -+ `PyObject* eval(Str source)` ++ `PyVar exec(Str source)` ++ `PyVar eval(Str source)` ### Compile mode @@ -39,7 +39,7 @@ In some cases, you may want to execute python code in a more fine-grained way. These two methods are provided for this purpose: + `CodeObject_ compile(Str source, Str filename, CompileMode mode, bool unknown_global_scope)` -+ `PyObject* _exec(CodeObject_ co, PyObject* _module)` ++ `PyVar _exec(CodeObject_ co, PyVar _module)` 1. `compile` compiles the source code into a `CodeObject_` instance. Leave `unknown_global_scope` to `false` if you don't know what it means. 2. `_exec` executes the `CodeObject_` instance. diff --git a/docs/quick-start/installation.md b/docs/quick-start/installation.md index 5ec74473..d65125ce 100644 --- a/docs/quick-start/installation.md +++ b/docs/quick-start/installation.md @@ -93,7 +93,7 @@ int main(){ vm->exec("a = [1, 2, 3]"); // Eval the sum of the list - PyObject* result = vm->eval("sum(a)"); + PyVar result = vm->eval("sum(a)"); std::cout << "Sum of the list: "<< py_cast(vm, result) << std::endl; // 6 // Bindings @@ -105,7 +105,7 @@ int main(){ }); // Call the function - PyObject* f_add = vm->_main->attr("add"); + PyVar f_add = vm->_main->attr("add"); result = vm->call(f_add, py_var(vm, 3), py_var(vm, 7)); std::cout << "Sum of 2 variables: "<< py_cast(vm, result) << std::endl; // 10 diff --git a/docs/quick-start/interop.md b/docs/quick-start/interop.md index c4deeba6..ea1f4c59 100644 --- a/docs/quick-start/interop.md +++ b/docs/quick-start/interop.md @@ -4,20 +4,20 @@ label: 'Interop with PyObject' order: 90 --- -In pkpy, any python object is represented by a `PyObject*`. +In pkpy, any python object is represented by a `PyVar`. -### Create `PyObject*` from C type +### Create `PyVar` from C type -A set of overloaded function `PyObject* py_var(VM* vm, ...)` were implemented to -create a `PyObject*` from a supported C type. +A set of overloaded function `PyVar py_var(VM* vm, ...)` were implemented to +create a `PyVar` from a supported C type. Assume we have a `VM* vm` instance. You can create a python `int` object from a C `i64` type: ```cpp i64 i = 2; -PyObject* obj = py_var(vm, i); +PyVar obj = py_var(vm, i); ``` Each python type has a corresponding C type, for example, `int` in python is `i64` in C. @@ -26,7 +26,7 @@ For strings, we have defined a set of overloaded version including `const char*`, `std::string`, `std::string_view`, `Str`, etc. ```cpp -PyObject* obj = py_var(vm, "abc"); // create a python str object +PyVar obj = py_var(vm, "abc"); // create a python str object ``` A more complex example is to create a python `list`. @@ -44,15 +44,15 @@ obj = py_var(vm, std::move(list)); // create a python list object Please note that `std::move` is used here to avoid unnecessary copy. Most types have both a rvalue and a lvalue version of `py_var` function. -### Access internal C type of `PyObject*` +### Access internal C type of `PyVar` -A set of template function `T py_cast(VM* vm, PyObject* obj)` were implemented. +A set of template function `T py_cast(VM* vm, PyVar obj)` were implemented. ```cpp i64 i = 2; -PyObject* obj = py_var(vm, i); +PyVar obj = py_var(vm, i); -// cast a PyObject* to C i64 +// cast a PyVar to C i64 i64 j = py_cast(vm, obj); ``` @@ -63,21 +63,21 @@ However, this type check has a cost. If you are sure about the type of `obj`, you can use the underscore version `_py_cast` to skip the type check. ```cpp -// cast a PyObject* to C i64 (unsafe but faster) +// cast a PyVar to C i64 (unsafe but faster) i64 j = _py_cast(vm, obj); ``` For complex objects like `list`, we can use reference cast to avoid unnecessary copy. ```cpp -PyObject* obj = py_var(vm, List()); +PyVar obj = py_var(vm, List()); // reference cast (no copy) List& list = py_cast(vm, obj); ``` -### Check type of `PyObject*` +### Check type of `PyVar` -Each `PyObject*` has a `Type type` field to indicate its type. +Each `PyVar` has a `Type type` field to indicate its type. `Type` is just an integer which is the global index in `vm->_all_types`. `VM` class has a set of predefined `Type` constants for quick access. @@ -88,16 +88,16 @@ Types are divided into **tagged type** and **non-tagged type**. + small `int` objects are tagged. + Other types are non-tagged type. -To determine whether a `PyObject*` is of a specific type, +To determine whether a `PyVar` is of a specific type, you can use the following functions: -+ `bool is_type(PyObject* obj, Type type)` -+ `bool is_int(PyObject* obj)` -+ `bool is_float(PyObject* obj)` -+ `bool is_tagged(PyObject* obj)` ++ `bool is_type(PyVar obj, Type type)` ++ `bool is_int(PyVar obj)` ++ `bool is_float(PyVar obj)` ++ `bool is_tagged(PyVar obj)` ```cpp -PyObject* obj = py_var(vm, 1); +PyVar obj = py_var(vm, 1); bool ok = is_type(obj, vm->tp_int); // true ok = is_int(obj); // true @@ -110,8 +110,8 @@ ok = is_float(obj); // false Simply put, `is_type` is the most general function and can check any types. Other variants are designed for specific types and are faster. -You can also use `check_` prefix functions assert the type of a `PyObject*`, +You can also use `check_` prefix functions assert the type of a `PyVar`, which will throw `TypeError` on failure. -+ `void check_type(PyObject* obj, Type type)` ++ `void check_type(PyVar obj, Type type)` diff --git a/docs/quick-start/misc.md b/docs/quick-start/misc.md index 62fe444f..8806feb3 100644 --- a/docs/quick-start/misc.md +++ b/docs/quick-start/misc.md @@ -18,7 +18,7 @@ For example, you create a temporary object on the stack and then call `vm->py_ne ```cpp void some_func(VM* vm){ - PyObject* obj = VAR(List(5)); + PyVar obj = VAR(List(5)); // unsafe PyObject iter = vm->py_iter(obj); PyObject next = vm->py_next(iter); @@ -32,7 +32,7 @@ The scope lock prevents this from happening. ```cpp void some_func(VM* vm){ - PyObject* obj = VAR(List(5)); + PyVar obj = VAR(List(5)); // safe auto _lock = vm->heap.gc_scope_lock(); PyObject iter = vm->py_iter(obj); diff --git a/docs/quick-start/modules.md b/docs/quick-start/modules.md index 241c0a47..c622d6aa 100644 --- a/docs/quick-start/modules.md +++ b/docs/quick-start/modules.md @@ -30,7 +30,7 @@ Native modules are always compiled and executed when the VM is created. To creata a native module, use `vm->new_module(Str name)`. ```cpp -PyObject* mod = vm->new_module("test"); +PyVar mod = vm->new_module("test"); mod->attr().set("pi", py_var(vm, 3.14)); vm->bind(mod, "add(a: int, b: int)",