From 196616a91701289e3f9957cbb7085cbec6eb14ec Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Sun, 21 May 2023 16:56:19 +0800 Subject: [PATCH] ... --- docs/quick-start/attr.md | 5 +- docs/quick-start/interop.md | 117 ++++++++++++++++++++++++++++-------- 2 files changed, 94 insertions(+), 28 deletions(-) diff --git a/docs/quick-start/attr.md b/docs/quick-start/attr.md index 0c7e865d..5318736e 100644 --- a/docs/quick-start/attr.md +++ b/docs/quick-start/attr.md @@ -7,7 +7,7 @@ order: 80 ### Direct access Some python objects have an instance dict, a.k.a, `__dict__` in cpython. -You can use `PyObject::attr()` to manipulate the instance dict of an object. +You can use `obj->attr()` to manipulate the instance dict of an object. ```cpp // get the `builtin` module @@ -39,7 +39,8 @@ bool ok = !is_tagged(obj) && obj->is_attr_valid(); // false ### General access -As you can see, direct access does not take care of derived attributes or methods. In most cases, what you need is `getattr` and `setattr`. +As you can see, direct access does not take care of derived attributes or methods. +In most cases, what you need is `getattr` and `setattr`. These two methods handle all possible cases. #### `PyObject* VM::getattr(PyObject* obj, StrName name, bool throw_err=true)` diff --git a/docs/quick-start/interop.md b/docs/quick-start/interop.md index 346dabf6..118d0428 100644 --- a/docs/quick-start/interop.md +++ b/docs/quick-start/interop.md @@ -5,39 +5,104 @@ order: 90 --- In pkpy, any python object is represented by a `PyObject*`. -There are 3 macros for you to do convert. -+ `VAR(...)`, -create a `PyObject*` from a C type -+ `CAST(T, ...)`, -cast a `PyObject*` to a C type -+ `_CAST(T, ...)`, -cast a `PyObject*` to a C type, without type check + +### Create `PyObject*` from C type + +A set of overloaded function `PyObject* py_var(VM* vm, ...)` were implemented to +create a `PyObject*` from a supported C type. +In order to make it less verbose, we usually use macro `VAR(...)`, which is just a wrapper of `py_var`. + +For example, create a python `int` object from a C `i64` type: ```cpp -PyObject* x = VAR(12); // cast a C int to PyObject* -int y = CAST(int, x); // cast a PyObject* to C int - -PyObject* i = VAR("abc"); -std::cout << CAST(Str, i); // abc +i64 i = 2; +PyObject* obj = VAR(i); ``` -### Types +Each python type has a corresponding C type, for example, `int` in python is `i64` in C. +python's `list` corresponds to `List`, `str` corresponds to `Str`, etc. +For strings, we have defined +a set of overloaded version including `const char*`, `std::string`, `std::string_view`, `Str`, etc. -| python type | C type | note | -| ------------ | ---------------- | ---------------------- | -| `int` | `i64` | 62 bits integer | -| `float` | `f64` | 62 bits floating point | -| `str` | `pkpy::Str` | | -| `bool` | `bool` | | -| `list` | `pkpy::List` | | -| `tuple` | `pkpy::Tuple` | | -| `function` | `pkpy::Function` | | -| ... | ... | ... | +```cpp +PyObject* obj = VAR("abc"); // create a python str object +``` -### Type check +A more complex example is to create a python `list`. +In the following code, we create a `list` equals to `[0, 1, 2, 3]`. + +```cpp +List list; +for (i64 i = 0; i < 4; i++) { + list.push_back(VAR(i)); +} + +obj = VAR(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 `VAR` function. + +### Access internal C type of `PyObject*` + +A set of template function `T py_cast(VM* vm, PyObject* obj)` were implemented +for each supported C type. We usually use macro `CAST(T, ...)` to make it less verbose. + +```cpp +i64 i = 2; +PyObject* obj = VAR(i); + +// cast a PyObject* to C i64 +i64 j = CAST(i64, obj); +``` + +The `CAST` function will check the type of `obj` before casting. +If the type is not matched, a `TypeError` will be thrown. + +However, this type check has a cost. If you are sure about the type of `obj`, +you can use the underscore version `_CAST` to skip the type check. + +```cpp +// cast a PyObject* to C i64 (unsafe but faster) +i64 j = _CAST(i64, obj); +``` + +For complex objects like `list`, we can use reference cast to avoid unnecessary copy. + +```cpp +PyObject* obj = VAR(List()); +// reference cast (no copy) +List& list = CAST(List&, obj); +``` + +### Check type of `PyObject*` + +Each `PyObject*` has a `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. +They are prefixed by `tp_`. For example, `tp_object`(object), +`tp_int`(int), `tp_str`(str), `tp_list`(list), etc. + +Types are divided into **tagged type** and **non-tagged type**. ++ `int` and `float` are tagged type. ++ Other types are non-tagged type. + +To determine whether a `PyObject*` 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_non_tagged_type(PyObject* obj, Type type)` -+ `void VM::check_type(PyObject* obj, Type type)` throws `TypeError` on failure -+ `void VM::check_non_tagged_type(PyObject* obj, Type type)` throws `TypeError` on failure \ No newline at end of file + +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*`, +which will throw `TypeError` on failure. + ++ `void VM::check_type(PyObject* obj, Type type)` ++ `void VM::check_non_tagged_type(PyObject* obj, Type type)` \ No newline at end of file