blueloveTH %!s(int64=2) %!d(string=hai) anos
pai
achega
196616a917
Modificáronse 2 ficheiros con 93 adicións e 27 borrados
  1. 3 2
      docs/quick-start/attr.md
  2. 90 25
      docs/quick-start/interop.md

+ 3 - 2
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)`

+ 90 - 25
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
+i64 i = 2;
+PyObject* obj = VAR(i);
+```
+
+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.
+
+```cpp
+PyObject* obj = VAR("abc");		// create a python str object
+```
+
+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<T>(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
-PyObject* x = VAR(12);		// cast a C int to PyObject*
-int y = CAST(int, x);		// cast a PyObject* to C int
+// 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.
 
-PyObject* i = VAR("abc");
-std::cout << CAST(Str, i);	// abc
+```cpp
+PyObject* obj = VAR(List());
+// reference cast (no copy)
+List& list = CAST(List&, obj);
 ```
 
-### Types
+### 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`.
 
-| 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` |                        |
-| ...          | ...              | ...                    |
+`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.
 
-### Type check
+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
+
+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)`