mirror of
https://github.com/pocketpy/pocketpy
synced 2025-12-07 10:40:16 +00:00
...
This commit is contained in:
parent
7c81209ce4
commit
ced9662530
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
icon: package
|
icon: package-dependencies
|
||||||
label: box2d
|
label: box2d
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
icon: package
|
icon: package-dependencies
|
||||||
label: os
|
label: os
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -10,12 +10,14 @@ Some python objects have an instance dict, a.k.a, `__dict__` in cpython.
|
|||||||
You can use `obj->attr()` to manipulate the instance dict of an object.
|
You can use `obj->attr()` to manipulate the instance dict of an object.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
VM* vm = new VM();
|
||||||
|
|
||||||
// get the `builtin` module
|
// get the `builtin` module
|
||||||
PyObject* builtins = vm->builtins;
|
PyObject* builtins = vm->builtins;
|
||||||
// get `dict` type
|
// get `dict` type
|
||||||
PyObject* dict = builtins->attr("dict");
|
PyObject* dict = builtins->attr("dict");
|
||||||
// set `pi = 3.14`
|
// set `pi = 3.14`
|
||||||
builtins->attr().set("pi", VAR(3.14));
|
builtins->attr().set("pi", py_var(vm, 3.14));
|
||||||
```
|
```
|
||||||
|
|
||||||
However, you cannot call `attr` on an object which does not have an instance dict.
|
However, you cannot call `attr` on an object which does not have an instance dict.
|
||||||
@ -23,7 +25,7 @@ For example, the `int` object.
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// create a `int` object
|
// create a `int` object
|
||||||
PyObject* obj = VAR(1);
|
PyObject* obj = py_var(vm, 1);
|
||||||
// THIS IS WRONG!! WILL LEAD TO A SEGFAULT!!
|
// THIS IS WRONG!! WILL LEAD TO A SEGFAULT!!
|
||||||
PyObject* add = obj->attr("__add__");
|
PyObject* add = obj->attr("__add__");
|
||||||
```
|
```
|
||||||
@ -33,7 +35,7 @@ To determine whether an object has instance dict or not, you can use this snippe
|
|||||||
```cpp
|
```cpp
|
||||||
// 1. call `is_tagged` to check the object supports `->` operator
|
// 1. call `is_tagged` to check the object supports `->` operator
|
||||||
// 2. call `is_attr_valid` to check the existence of instance dict
|
// 2. call `is_attr_valid` to check the existence of instance dict
|
||||||
PyObject* obj = VAR(1);
|
PyObject* obj = py_var(vm, 1);
|
||||||
bool ok = !is_tagged(obj) && obj->is_attr_valid(); // false
|
bool ok = !is_tagged(obj) && obj->is_attr_valid(); // false
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -51,16 +53,16 @@ or throw an `AttributeError` depending on the value of `throw_err`.
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// create a `int` object
|
// create a `int` object
|
||||||
PyObject* obj = VAR(1);
|
PyObject* obj = py_var(vm, 1);
|
||||||
|
|
||||||
// get its `__add__` method, which is a `bound_method` object
|
// get its `__add__` method, which is a `bound_method` object
|
||||||
PyObject* add = vm->getattr(obj, "__add__");
|
PyObject* add = vm->getattr(obj, "__add__");
|
||||||
|
|
||||||
// call it (equivalent to `1 + 2`)
|
// call it (equivalent to `1 + 2`)
|
||||||
PyObject* ret = vm->call(add, VAR(2));
|
PyObject* ret = vm->call(add, py_var(vm, 2););
|
||||||
|
|
||||||
// get the result
|
// get the result
|
||||||
int result = CAST(int, ret);
|
int result = py_cast<int>(vm, ret);
|
||||||
std::cout << result << std::endl; // 3
|
std::cout << result << std::endl; // 3
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -34,12 +34,14 @@ PyObject* obj = vm->call(tp); // this is a `dict`
|
|||||||
And set a key-value pair,
|
And set a key-value pair,
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
vm->call_method(obj, "__setitem__", VAR("a"), VAR(5));
|
PyObject* _0 = py_var(vm, "a");
|
||||||
|
PyObject* _1 = py_var(vm, 5);
|
||||||
|
vm->call_method(obj, "__setitem__", _0, _1);
|
||||||
```
|
```
|
||||||
|
|
||||||
And get the value,
|
And get the value,
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
PyObject* ret = vm->call_method(obj, "__getitem__", VAR("a"));
|
PyObject* ret = vm->call_method(obj, "__getitem__", _0);
|
||||||
std::cout << CAST(i64, ret) << std::endl;
|
std::cout << py_cast<int>(vm, i64);
|
||||||
```
|
```
|
||||||
@ -11,13 +11,13 @@ In pkpy, any python object is represented by a `PyObject*`.
|
|||||||
|
|
||||||
A set of overloaded function `PyObject* py_var(VM* vm, ...)` were implemented to
|
A set of overloaded function `PyObject* py_var(VM* vm, ...)` were implemented to
|
||||||
create a `PyObject*` from a supported C type.
|
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:
|
Assume we have a `VM* vm` instance.
|
||||||
|
You can create a python `int` object from a C `i64` type:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
i64 i = 2;
|
i64 i = 2;
|
||||||
PyObject* obj = VAR(i);
|
PyObject* obj = py_var(vm, i);
|
||||||
```
|
```
|
||||||
|
|
||||||
Each python type has a corresponding C type, for example, `int` in python is `i64` in C.
|
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.
|
a set of overloaded version including `const char*`, `std::string`, `std::string_view`, `Str`, etc.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
PyObject* obj = VAR("abc"); // create a python str object
|
PyObject* obj = py_var(vm, "abc"); // create a python str object
|
||||||
```
|
```
|
||||||
|
|
||||||
A more complex example is to create a python `list`.
|
A more complex example is to create a python `list`.
|
||||||
@ -35,50 +35,49 @@ In the following code, we create a `list` equals to `[0, 1, 2, 3]`.
|
|||||||
```cpp
|
```cpp
|
||||||
List list;
|
List list;
|
||||||
for (i64 i = 0; i < 4; i++) {
|
for (i64 i = 0; i < 4; i++) {
|
||||||
list.push_back(VAR(i));
|
list.push_back(py_var(vm, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = VAR(std::move(list)); // create a python list object
|
obj = py_var(vm, std::move(list)); // create a python list object
|
||||||
```
|
```
|
||||||
|
|
||||||
Please note that `std::move` is used here to avoid unnecessary copy.
|
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.
|
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 `PyObject*`
|
||||||
|
|
||||||
A set of template function `T py_cast<T>(VM* vm, PyObject* obj)` were implemented
|
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
|
```cpp
|
||||||
i64 i = 2;
|
i64 i = 2;
|
||||||
PyObject* obj = VAR(i);
|
PyObject* obj = py_var(vm, i);
|
||||||
|
|
||||||
// cast a PyObject* to C i64
|
// cast a PyObject* to C i64
|
||||||
i64 j = CAST(i64, obj);
|
i64 j = py_cast<i64>(vm, obj);
|
||||||
```
|
```
|
||||||
|
|
||||||
The `CAST` function will check the type of `obj` before casting.
|
The `py_cast` function will check the type of `obj` before casting.
|
||||||
If the type is not matched, a `TypeError` will be thrown.
|
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`,
|
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.
|
you can use the underscore version `_py_cast` to skip the type check.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// cast a PyObject* to C i64 (unsafe but faster)
|
// cast a PyObject* to C i64 (unsafe but faster)
|
||||||
i64 j = _CAST(i64, obj);
|
i64 j = _py_cast<i64>(vm, obj);
|
||||||
```
|
```
|
||||||
|
|
||||||
For complex objects like `list`, we can use reference cast to avoid unnecessary copy.
|
For complex objects like `list`, we can use reference cast to avoid unnecessary copy.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
PyObject* obj = VAR(List());
|
PyObject* obj = py_var(vm, List());
|
||||||
// reference cast (no copy)
|
// reference cast (no copy)
|
||||||
List& list = CAST(List&, obj);
|
List& list = py_cast<List&>(vm, obj);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Check type of `PyObject*`
|
### Check type of `PyObject*`
|
||||||
|
|
||||||
Each `PyObject*` has a `Type` field to indicate its type.
|
Each `PyObject*` has a `Type type` field to indicate its type.
|
||||||
`Type` is just an integer which is the global index in `vm->_all_types`.
|
`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.
|
`VM` class has a set of predefined `Type` constants for quick access.
|
||||||
@ -86,7 +85,7 @@ They are prefixed by `tp_`. For example, `tp_object`(object),
|
|||||||
`tp_int`(int), `tp_str`(str), `tp_list`(list), etc.
|
`tp_int`(int), `tp_str`(str), `tp_list`(list), etc.
|
||||||
|
|
||||||
Types are divided into **tagged type** and **non-tagged type**.
|
Types are divided into **tagged type** and **non-tagged type**.
|
||||||
+ `int` and `float` are tagged type.
|
+ `int` (small) and `float` are tagged type.
|
||||||
+ Other types are non-tagged type.
|
+ Other types are non-tagged type.
|
||||||
|
|
||||||
To determine whether a `PyObject*` is of a specific type,
|
To determine whether a `PyObject*` is of a specific type,
|
||||||
@ -99,7 +98,7 @@ you can use the following functions:
|
|||||||
+ `bool is_non_tagged_type(PyObject* obj, Type type)`
|
+ `bool is_non_tagged_type(PyObject* obj, Type type)`
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
PyObject* obj = VAR(1);
|
PyObject* obj = py_var(vm, 1);
|
||||||
|
|
||||||
bool ok = is_type(obj, vm->tp_int); // true
|
bool ok = is_type(obj, vm->tp_int); // true
|
||||||
ok = is_int(obj); // true
|
ok = is_int(obj); // true
|
||||||
|
|||||||
@ -28,16 +28,17 @@ A native module is a module written in c++ or mixed c++/python.
|
|||||||
Native modules are always compiled and executed when the VM is created.
|
Native modules are always compiled and executed when the VM is created.
|
||||||
|
|
||||||
To creata a native module,
|
To creata a native module,
|
||||||
use `vm->new_module(...)`.
|
use `vm->new_module(Str name)`.
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
PyObject* mod = vm->new_module("test");
|
PyObject* mod = vm->new_module("test");
|
||||||
mod->attr().set("pi", VAR(3.14));
|
mod->attr().set("pi", py_var(vm, 3.14));
|
||||||
|
|
||||||
vm->bind_func<2>(mod, "add", [](VM* vm, ArgsView args){
|
vm->bind(mod, "add(a: int, b: int)",
|
||||||
i64 a = CAST(i64, args[0]);
|
[](VM* vm, ArgsView args){
|
||||||
i64 b = CAST(i64, args[1]);
|
int a = py_cast<int>(vm, args[0]);
|
||||||
return VAR(a + b);
|
int b = py_cast<int>(vm, args[1]);
|
||||||
|
return py_var(vm, a + b);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user