mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-19 19:10:17 +00:00
Compare commits
16 Commits
fece79b169
...
4ba0b7d2e2
Author | SHA1 | Date | |
---|---|---|---|
|
4ba0b7d2e2 | ||
|
2d6def4bbd | ||
|
a5a3c75b83 | ||
|
8ffcacf99f | ||
|
a2cb91b125 | ||
|
86006d0b61 | ||
|
a08c5af6e9 | ||
|
475be8ce80 | ||
|
12d748ba7d | ||
|
0c1bd532f8 | ||
|
d52cbec80c | ||
|
e16aa501ef | ||
|
59bbecdbee | ||
|
f5c356a047 | ||
|
68f8cd1159 | ||
|
259394d7ad |
37
README.md
37
README.md
@ -25,7 +25,8 @@ pkpy is extremely easy to embed via a single header file `pocketpy.h`, without e
|
||||
|
||||
Please see https://pocketpy.dev for details and try the following resources.
|
||||
+ [Live Python Demo](https://pocketpy.dev/static/web/): Python REPL of the latest version
|
||||
+ [Live C++ Examples](https://pocketpy.github.io/examples/): Common usage of pkpy in C++
|
||||
|
||||
> We are moving to v2.0 branch, which is a complete refactor of this project in C11. See [Migration Guide](https://pocketpy.dev/migrate/) for more details.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
@ -90,39 +91,33 @@ python scripts/run_tests.py
|
||||
### Example
|
||||
|
||||
```cpp
|
||||
#include "pocketpy.h"
|
||||
#include <pybind11/pybind11.h>
|
||||
|
||||
using namespace pkpy;
|
||||
namespace py = pybind11;
|
||||
|
||||
int main(){
|
||||
// Create a virtual machine
|
||||
VM* vm = new VM();
|
||||
int main() {
|
||||
// Start the interpreter
|
||||
py::scoped_interpreter guard{};
|
||||
|
||||
// Hello world!
|
||||
vm->exec("print('Hello world!')");
|
||||
py::exec("print('Hello world!')");
|
||||
|
||||
// Create a list
|
||||
vm->exec("a = [1, 2, 3]");
|
||||
py::exec("a = [1, 2, 3]");
|
||||
|
||||
// Eval the sum of the list
|
||||
PyVar result = vm->eval("sum(a)");
|
||||
std::cout << "Sum of the list: "<< py_cast<int>(vm, result) << std::endl; // 6
|
||||
auto result = py::eval("sum(a)");
|
||||
std::cout << "Sum of the list: " << result.cast<int>() << std::endl; // 6
|
||||
|
||||
// Bindings
|
||||
vm->bind(vm->_main, "add(a: int, b: int)",
|
||||
[](VM* vm, ArgsView args){
|
||||
int a = py_cast<int>(vm, args[0]);
|
||||
int b = py_cast<int>(vm, args[1]);
|
||||
return py_var(vm, a + b);
|
||||
});
|
||||
auto m = py::module_::__main__();
|
||||
m.def("add", [](int a, int b) {
|
||||
return a + b;
|
||||
});
|
||||
|
||||
// Call the function
|
||||
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<int>(vm, result) << std::endl; // 10
|
||||
std::cout << "Sum of 2 variables: " << m.attr("add")(1, 2).cast<int>() << std::endl; // 10
|
||||
|
||||
// Dispose the virtual machine
|
||||
delete vm;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
@ -1,333 +0,0 @@
|
||||
---
|
||||
icon: log
|
||||
title: 'Cheatsheet'
|
||||
order: 22
|
||||
---
|
||||
|
||||
## Basics
|
||||
|
||||
Setup pocketpy
|
||||
|
||||
```cpp
|
||||
#include "pocketpy.h"
|
||||
using namespace pkpy;
|
||||
```
|
||||
|
||||
Create a python virtual machine
|
||||
|
||||
```cpp
|
||||
VM* vm = new VM();
|
||||
```
|
||||
|
||||
Dispose a python virtual machine
|
||||
|
||||
```cpp
|
||||
delete vm;
|
||||
```
|
||||
|
||||
Execute a source string
|
||||
|
||||
```cpp
|
||||
vm->exec("print('Hello!')");
|
||||
```
|
||||
|
||||
Evaluate a source string
|
||||
|
||||
```cpp
|
||||
PyVar obj = vm->eval("123");
|
||||
std::cout << py_cast<int>(vm, obj); // 123
|
||||
```
|
||||
|
||||
Compile a source string into a code object
|
||||
|
||||
```cpp
|
||||
CodeObject_ co = vm->compile("print('Hello!')", "main.py", EXEC_MODE);
|
||||
```
|
||||
|
||||
Execute a compiled code object
|
||||
|
||||
```cpp
|
||||
try{
|
||||
vm->_exec(co); // may throw
|
||||
}catch(Exception& e){
|
||||
std::cerr << e.summary() << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
## Interop with native types
|
||||
|
||||
Create primitive objects
|
||||
|
||||
```cpp
|
||||
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
|
||||
obj = py_var(vm, true); // create a bool
|
||||
```
|
||||
|
||||
Create a tuple object
|
||||
|
||||
```cpp
|
||||
// obj = (1, 1.0, '123')
|
||||
Tuple t(3);
|
||||
t[0] = py_var(vm, 1);
|
||||
t[1] = py_var(vm, 1.0);
|
||||
t[2] = py_var(vm, "123");
|
||||
PyVar obj = py_var(vm, std::move(t));
|
||||
```
|
||||
|
||||
Create a list object
|
||||
|
||||
```cpp
|
||||
// obj = [1, 1.0, '123']
|
||||
List t;
|
||||
t.push_back(py_var(vm, 1));
|
||||
t.push_back(py_var(vm, 1.0));
|
||||
t.push_back(py_var(vm, "123"));
|
||||
PyVar obj = py_var(vm, std::move(t));
|
||||
```
|
||||
|
||||
Create a dict object
|
||||
|
||||
```cpp
|
||||
// obj = {'x': 1, 'y': '123'}
|
||||
Dict d(vm);
|
||||
d.set(py_var(vm, "x"), py_var(vm, 1));
|
||||
d.set(py_var(vm, "y"), py_var(vm, "123"));
|
||||
PyVar obj = py_var(vm, std::move(d));
|
||||
```
|
||||
|
||||
Get native types from python objects
|
||||
|
||||
```cpp
|
||||
PyVar obj;
|
||||
i64 a = py_cast<i64>(vm, obj);
|
||||
f64 b = py_cast<f64>(vm, obj);
|
||||
Str& c = py_cast<Str&>(vm, obj); // reference cast
|
||||
bool d = py_cast<bool>(vm, obj);
|
||||
|
||||
Tuple& e = py_cast<Tuple&>(vm, obj); // reference cast
|
||||
List& f = py_cast<List&>(vm, obj); // reference cast
|
||||
Dict& g = py_cast<Dict&>(vm, obj); // reference cast
|
||||
```
|
||||
|
||||
Get native types without type checking
|
||||
|
||||
```cpp
|
||||
// unsafe version 1 (for int object you must use `_py_cast`)
|
||||
i64 a = _py_cast<i64>(vm, obj);
|
||||
f64 b = _py_cast<f64>(vm, obj);
|
||||
Tuple& c = _py_cast<Tuple&>(vm, obj);
|
||||
// unsafe version 2 (for others, you can also use `PK_OBJ_GET` macro)
|
||||
Str& a_ = PK_OBJ_GET(Str, obj);
|
||||
List& b_ = PK_OBJ_GET(List, obj);
|
||||
Tuple& c_ = PK_OBJ_GET(Tuple, obj);
|
||||
```
|
||||
|
||||
## Access python types
|
||||
|
||||
Access built-in python types
|
||||
|
||||
```cpp
|
||||
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
|
||||
|
||||
```cpp
|
||||
Type voidp_t = vm->_tp_user<VoidP>();
|
||||
```
|
||||
|
||||
Check if an object is a python type
|
||||
|
||||
```cpp
|
||||
PyVar obj;
|
||||
bool ok = is_type(obj, VM::tp_int); // check if obj is an int
|
||||
```
|
||||
|
||||
Get the type of a python object
|
||||
|
||||
```cpp
|
||||
PyVar obj = py_var(vm, 1);
|
||||
PyVar t = vm->_t(obj); // <class 'int'>
|
||||
Type type = vm->_tp(obj); // VM::tp_int
|
||||
```
|
||||
|
||||
Convert a type object into a type index
|
||||
|
||||
```cpp
|
||||
PyVar int_t = vm->_t(VM::tp_int);
|
||||
Type t = PK_OBJ_GET(Type, int_t);
|
||||
// t == VM::tp_int
|
||||
```
|
||||
|
||||
## Access attributes
|
||||
|
||||
Check an object supports attribute access
|
||||
|
||||
```cpp
|
||||
PyVar obj;
|
||||
bool ok = !is_tagged(obj) && obj->is_attr_valid();
|
||||
```
|
||||
|
||||
```python
|
||||
class MyClass:
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def sum(self):
|
||||
return self.x + self.y
|
||||
```
|
||||
|
||||
Get and set attributes
|
||||
|
||||
```cpp
|
||||
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
|
||||
```
|
||||
|
||||
## Call python functions
|
||||
|
||||
```python
|
||||
def add(a, b):
|
||||
return a + b
|
||||
```
|
||||
|
||||
Call a function
|
||||
|
||||
```cpp
|
||||
PyVar f_add = vm->eval("add");
|
||||
PyVar ret = vm->call(f_add, py_var(vm, 1), py_var(vm, 2));
|
||||
std::cout << py_cast<int>(vm, ret); // 3
|
||||
```
|
||||
|
||||
Call a method
|
||||
|
||||
```cpp
|
||||
PyVar obj = vm->exec("MyClass(1, 2)");
|
||||
PyVar ret = vm->call_method(obj, "sum");
|
||||
std::cout << CAST(i64, ret); // 3
|
||||
```
|
||||
|
||||
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");
|
||||
PyVar ret = vm->call_method(obj, m_sum);
|
||||
```
|
||||
|
||||
## Special operations
|
||||
|
||||
Compare two python objects
|
||||
|
||||
```cpp
|
||||
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
|
||||
PyVar obj = py_var(vm, 123);
|
||||
std::cout << vm->py_str(obj); // 123
|
||||
```
|
||||
|
||||
Get the string representation of a python object
|
||||
|
||||
```cpp
|
||||
PyVar obj = py_var(vm, "123");
|
||||
std::cout << vm->py_repr(obj); // "'123'"
|
||||
```
|
||||
|
||||
Get the JSON representation of a python object
|
||||
|
||||
```cpp
|
||||
PyVar obj = py_var(vm, 123);
|
||||
std::cout << vm->py_json(obj); // "123"
|
||||
```
|
||||
|
||||
Get the hash value of a python object
|
||||
|
||||
```cpp
|
||||
PyVar obj = py_var(vm, 1);
|
||||
i64 h = vm->py_hash(obj); // 1
|
||||
```
|
||||
|
||||
Get the iterator of a python object
|
||||
|
||||
```cpp
|
||||
PyVar obj = vm->eval("range(3)");
|
||||
PyVar iter = vm->py_iter(obj);
|
||||
```
|
||||
|
||||
Get the next item of an iterator
|
||||
|
||||
```cpp
|
||||
PyVar obj = vm->py_next(iter);
|
||||
if(obj == vm->StopIteration){
|
||||
// end of iteration
|
||||
}else{
|
||||
// process obj
|
||||
}
|
||||
```
|
||||
|
||||
Convert a python iterable to a list
|
||||
|
||||
```cpp
|
||||
PyVar obj = vm->eval("range(3)");
|
||||
List list = vm->py_list(obj);
|
||||
```
|
||||
|
||||
## Bindings
|
||||
|
||||
Bind a native function
|
||||
|
||||
```cpp
|
||||
vm->bind(obj, "add(a: int, b: int) -> int", [](VM* vm, ArgsView args){
|
||||
int a = py_cast<int>(vm, args[0]);
|
||||
int b = py_cast<int>(vm, args[1]);
|
||||
return py_var(vm, a + b);
|
||||
});
|
||||
```
|
||||
|
||||
Bind a property
|
||||
|
||||
```cpp
|
||||
// getter and setter of property `x`
|
||||
vm->bind_property(type, "x: int",
|
||||
[](VM* vm, ArgsView args){
|
||||
Point& self = PK_OBJ_GET(Point, args[0]);
|
||||
return VAR(self.x);
|
||||
},
|
||||
[](VM* vm, ArgsView args){
|
||||
Point& self = PK_OBJ_GET(Point, args[0]);
|
||||
self.x = py_cast<int>(vm, args[1]);
|
||||
return vm->None;
|
||||
});
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
Create a source module
|
||||
|
||||
```cpp
|
||||
vm->_lazy_modules["test"] = "pi = 3.14";
|
||||
// import test
|
||||
// print(test.pi) # 3.14
|
||||
```
|
||||
|
||||
Create a native module
|
||||
|
||||
```cpp
|
||||
PyVar mod = vm->new_module("test");
|
||||
vm->setattr(mod, "pi", py_var(vm, 3.14));
|
||||
```
|
@ -5,6 +5,12 @@ label: Welcome
|
||||
|
||||
# Welcome to pocketpy
|
||||
|
||||
!!!
|
||||
We are moving to v2.0 branch, which is a complete refactor of this project in C11.
|
||||
|
||||
See [Migration Guide](migrate.md) for more details.
|
||||
!!!
|
||||
|
||||
pkpy is a lightweight(~15K LOC) Python interpreter for game scripting, built on C++17 with STL.
|
||||
|
||||
It aims to be an alternative to lua for game scripting, with elegant syntax, powerful features and competitive performance.
|
||||
|
32
docs/migrate.md
Normal file
32
docs/migrate.md
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
icon: log
|
||||
title: 'Migration Guide'
|
||||
order: 22
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
v2.0 branch is a complete refactor of pocketpy in C11,
|
||||
which enables users to run pocketpy on platforms that do not support C++.
|
||||
Also we redesign the core interpreter to be more efficient and maintainable
|
||||
by using modern C11 language features.
|
||||
|
||||
> v2.0 will be released on 2024/08.
|
||||
|
||||
## API compatibility
|
||||
|
||||
| name | v1.x | v2.0 |
|
||||
| --- | --- | --- |
|
||||
| legacy C++ API (v1.x only) | ✅ | ❌ |
|
||||
| legacy C API (v1.x only) | ✅ | ❌ |
|
||||
| C11 API (v2.0 only) | ❌ | ✅ (work-in-progress) |
|
||||
| pybind11 API (both) | ✅ | ✅ (work-in-progress) |
|
||||
|
||||
## Suggestions
|
||||
|
||||
- If you are a C++ user
|
||||
- Use **pybind11 API** if you want to upgrade to v2.0 in the future
|
||||
- Use **legacy C++ API** if you want to stay in v1.x
|
||||
- If you are a C user
|
||||
- Use **v2.0's C11 API** (will be available soon)
|
||||
- Use **legacy C API** if you want to stay in v1.x
|
291
docs/pybind11.md
Normal file
291
docs/pybind11.md
Normal file
@ -0,0 +1,291 @@
|
||||
---
|
||||
icon: log
|
||||
title: 'pybind11 User Guide'
|
||||
order: 21
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
pkpy provides a [pybind11](https://pybind11.readthedocs.io/en/stable/) compatible layer which allows users to do convenient bindings.
|
||||
|
||||
To begin with, use `py::scoped_interpreter guard{}` to start the interpreter before using any Python objects.
|
||||
Or explicitly call `py::interpreter::initialize()` and `py::interpreter::finalize()`.
|
||||
|
||||
### module
|
||||
|
||||
```cpp
|
||||
#include <pybind11/pybind11.h>
|
||||
namespace py = pybind11;
|
||||
|
||||
PYBIND11_MODULE(example, m) {
|
||||
m.def("add", [](int a, int b) {
|
||||
return a + b;
|
||||
});
|
||||
|
||||
auto math = m.def_submodule("math");
|
||||
}
|
||||
```
|
||||
|
||||
### function
|
||||
|
||||
```cpp
|
||||
int add(int a, int b) { return a + b; }
|
||||
|
||||
int add(int a, int b, int c) { return a + b + c; }
|
||||
|
||||
void register_function(py::module_& m)
|
||||
{
|
||||
m.def("add", py::overload_cast<int, int>(&add));
|
||||
|
||||
// support function overload
|
||||
m.def("add", py::overload_cast<int, int, int>(&add));
|
||||
|
||||
// bind with default arguments
|
||||
m.def("sub", [](int a, int b) {
|
||||
return a - b;
|
||||
}, py::arg("a") = 1, py::arg("b") = 2);
|
||||
|
||||
// bind *args
|
||||
m.def("add", [](py::args args) {
|
||||
int sum = 0;
|
||||
for (auto& arg : args) {
|
||||
sum += arg.cast<int>();
|
||||
}
|
||||
return sum;
|
||||
});
|
||||
|
||||
// bind **kwargs
|
||||
m.def("add", [](py::kwargs kwargs) {
|
||||
int sum = 0;
|
||||
for (auto item : kwargs) {
|
||||
sum += item.second.cast<int>();
|
||||
}
|
||||
return sum;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### class
|
||||
|
||||
```cpp
|
||||
struct Point
|
||||
{
|
||||
const int x;
|
||||
int y;
|
||||
|
||||
public:
|
||||
Point() : x(0), y(0) {}
|
||||
|
||||
Point(int x, int y) : x(x), y(y) {}
|
||||
|
||||
Point(const Point& p) : x(p.x), y(p.y) {}
|
||||
|
||||
std::string stringfy() const {
|
||||
return "(" + std::to_string(x) + ", " + std::to_string(y) + ")";
|
||||
}
|
||||
};
|
||||
|
||||
struct Point3D : Point
|
||||
{
|
||||
private:
|
||||
int z;
|
||||
|
||||
public:
|
||||
Point3D(int x, int y, int z) : Point(x, y), z(z) {}
|
||||
|
||||
int get_z() const { return z; }
|
||||
|
||||
void set_z(int z) { this->z = z; }
|
||||
};
|
||||
|
||||
void bind_class(py::module_& m)
|
||||
{
|
||||
py::class_<Point>(m, "Point")
|
||||
.def(py::init<>())
|
||||
.def(py::init<int, int>())
|
||||
.def(py::init<const Point&>())
|
||||
.def_readonly("x", &Point::x)
|
||||
.def_readwrite("y", &Point::y)
|
||||
.def("__str__", &Point::stringfy);
|
||||
|
||||
// only support single inheritance
|
||||
py::class_<Point3D, Point>(m, "Point3D", py::dynamic_attr())
|
||||
.def(py::init<int, int, int>())
|
||||
.def_property("z", &Point3D::get_z, &Point3D::set_z);
|
||||
|
||||
// dynamic_attr will enable the dict of bound class
|
||||
}
|
||||
```
|
||||
|
||||
### operators
|
||||
|
||||
```cpp
|
||||
#include <pybind11/operators.h>
|
||||
namespace py = pybind11;
|
||||
|
||||
struct Int {
|
||||
int value;
|
||||
|
||||
Int(int value) : value(value) {}
|
||||
|
||||
Int operator+(const Int& other) const {
|
||||
return Int(value + other.value);
|
||||
}
|
||||
|
||||
Int operator-(const Int& other) const {
|
||||
return Int(value - other.value);
|
||||
}
|
||||
|
||||
bool operator==(const Int& other) const {
|
||||
return value == other.value;
|
||||
}
|
||||
|
||||
bool operator!=(const Int& other) const {
|
||||
return value != other.value;
|
||||
}
|
||||
};
|
||||
|
||||
void bind_operators(py::module_& m)
|
||||
{
|
||||
py::class_<Int>(m, "Int")
|
||||
.def(py::init<int>())
|
||||
.def(py::self + py::self)
|
||||
.def(py::self - py::self)
|
||||
.def(py::self == py::self)
|
||||
.def(py::self != py::self);
|
||||
// other operators are similar
|
||||
}
|
||||
```
|
||||
|
||||
### py::object
|
||||
|
||||
`py::object` is just simple wrapper around `PyVar`. It supports some convenient methods to interact with Python objects.
|
||||
|
||||
|
||||
here are some common methods:
|
||||
|
||||
```cpp
|
||||
obj.attr("x"); // access attribute
|
||||
obj[1]; // access item
|
||||
|
||||
obj.is_none(); // same as obj is None in Python
|
||||
obj.is(obj2); // same as obj is obj2 in Python
|
||||
|
||||
// operators
|
||||
obj + obj2; // same as obj + obj2 in Python
|
||||
// ...
|
||||
obj == obj2; // same as obj == obj2 in Python
|
||||
// ...
|
||||
|
||||
obj(...); // same as obj.__call__(...)
|
||||
|
||||
py::cast(obj); // cast to Python object
|
||||
obj.cast<T>; // cast to C++ type
|
||||
|
||||
py::type::of(obj); // get type of obj
|
||||
py::type::of<T>(); // get type of T, if T is registered
|
||||
```
|
||||
|
||||
you can also create some builtin objects with their according wrappers:
|
||||
|
||||
```cpp
|
||||
py::bool_ b = {true};
|
||||
py::int_ i = {1};
|
||||
py::float_ f = {1.0};
|
||||
py::str s = {"hello"};
|
||||
py::list l = {1, 2, 3};
|
||||
py::tuple t = {1, 2, 3};
|
||||
// ...
|
||||
```
|
||||
|
||||
|
||||
|
||||
## More Examples
|
||||
|
||||
More examples please see the test [folder](https://github.com/pocketpy/gsoc-2024-dev/tree/main/pybind11/tests) in the GSoC repository. All tested features are supported.
|
||||
|
||||
## Limits and Comparison
|
||||
|
||||
This is a feature list of pybind11 for pocketpy. It lists all completed and pending features. It also lists the features that cannot be implemented in the current version of pocketpy.
|
||||
|
||||
### [Function](https://pybind11.readthedocs.io/en/stable/advanced/functions.html)
|
||||
|
||||
- [x] Function overloading
|
||||
- [x] Return value policy
|
||||
- [x] is_prepend
|
||||
- [x] `*args` and `**kwargs`
|
||||
- [ ] Keep-alive
|
||||
- [ ] Call Guard
|
||||
- [x] Default arguments
|
||||
- [ ] Keyword-Only arguments
|
||||
- [ ] Positional-Only arguments
|
||||
- [ ] Allow/Prohibiting None arguments
|
||||
|
||||
### [Class](https://pybind11.readthedocs.io/en/stable/classes.html)
|
||||
|
||||
- [x] Creating bindings for a custom type
|
||||
- [x] Binding lambda functions
|
||||
- [x] Dynamic attributes
|
||||
- [x] Inheritance and automatic downcasting
|
||||
- [x] Enumerations and internal types
|
||||
- [ ] Instance and static fields
|
||||
|
||||
> Binding static fields may never be implemented in pocketpy because it requires a metaclass, which is a heavy and infrequently used feature.
|
||||
|
||||
### [Exceptions](https://pybind11.readthedocs.io/en/stable/advanced/exceptions.html)
|
||||
|
||||
Need further discussion.
|
||||
|
||||
### [Smart pointers](https://pybind11.readthedocs.io/en/stable/advanced/smart_ptrs.html)
|
||||
|
||||
- [ ] std::shared_ptr
|
||||
- [ ] std::unique_ptr
|
||||
- [ ] Custom smart pointers
|
||||
|
||||
### [Type conversions](https://pybind11.readthedocs.io/en/stable/advanced/cast/index.html)
|
||||
|
||||
- [x] Python built-in types
|
||||
- [x] STL Containers
|
||||
- [ ] Functional
|
||||
- [ ] Chrono
|
||||
|
||||
### [Python C++ interface](https://pybind11.readthedocs.io/en/stable/advanced/pycpp/object.html)
|
||||
|
||||
Need further discussion.
|
||||
|
||||
- [x] `object`
|
||||
- [x] `none`
|
||||
- [x] `type`
|
||||
- [x] `bool_`
|
||||
- [x] `int_`
|
||||
- [x] `float_`
|
||||
- [x] `str`
|
||||
- [ ] `bytes`
|
||||
- [ ] `bytearray`
|
||||
- [x] `tuple`
|
||||
- [x] `list`
|
||||
- [ ] `set`
|
||||
- [x] `dict`
|
||||
- [ ] `slice`
|
||||
- [x] `iterable`
|
||||
- [x] `iterator`
|
||||
- [ ] `function`
|
||||
- [ ] `buffer`
|
||||
- [ ] `memoryview`
|
||||
- [x] `capsule`
|
||||
|
||||
### [Miscellaneous](https://pybind11.readthedocs.io/en/stable/advanced/misc.html)
|
||||
|
||||
- [ ] Global Interpreter Lock (GIL)
|
||||
- [ ] Binding sequence data types, iterators, the slicing protocol, etc.
|
||||
- [x] Convenient operators binding
|
||||
|
||||
### Differences between CPython and pocketpy
|
||||
|
||||
- only `add`, `sub` and `mul` have corresponding right versions in pocketpy. So if you bind `int() >> py::self`, it will has no effect in pocketpy.
|
||||
|
||||
- `__new__` and `__del__` are not supported in pocketpy.
|
||||
|
||||
- in-place operators, such as `+=`, `-=`, `*=`, etc., are not supported in pocketpy.
|
||||
|
||||
- thre return value of `globals` is immutable in pocketpy.
|
@ -3,7 +3,7 @@ output: .retype
|
||||
url: https://pocketpy.dev
|
||||
branding:
|
||||
title: pocketpy
|
||||
label: v1.5.0
|
||||
label: v1.4.6
|
||||
logo: "./static/logo.png"
|
||||
favicon: "./static/logo.png"
|
||||
meta:
|
||||
@ -16,10 +16,10 @@ links:
|
||||
icon: play
|
||||
link: "https://pocketpy.dev/static/web/"
|
||||
target: blank
|
||||
- text: "Live Examples"
|
||||
icon: play
|
||||
link: "https://pocketpy.github.io/examples/"
|
||||
target: blank
|
||||
# - text: "Live Examples"
|
||||
# icon: play
|
||||
# link: "https://pocketpy.github.io/examples/"
|
||||
# target: blank
|
||||
- text: "Github"
|
||||
icon: mark-github
|
||||
link: https://github.com/blueloveth/pocketpy
|
||||
|
@ -20,10 +20,10 @@
|
||||
#include <typeindex>
|
||||
#include <initializer_list>
|
||||
|
||||
#define PK_VERSION "1.5.0"
|
||||
#define PK_VERSION "1.4.6"
|
||||
#define PK_VERSION_MAJOR 1
|
||||
#define PK_VERSION_MINOR 5
|
||||
#define PK_VERSION_PATCH 0
|
||||
#define PK_VERSION_MINOR 4
|
||||
#define PK_VERSION_PATCH 6
|
||||
|
||||
#include "config.h"
|
||||
#include "export.h"
|
||||
|
@ -81,9 +81,13 @@ inline bool isinstance(const handle& obj, const handle& type) {
|
||||
|
||||
inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); }
|
||||
|
||||
namespace impl {
|
||||
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct type_caster;
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
handle cast(T&& value, return_value_policy policy, handle parent) {
|
||||
// decay_t can resolve c-array type, but remove_cv_ref_t can't.
|
||||
@ -107,7 +111,7 @@ handle cast(T&& value, return_value_policy policy, handle parent) {
|
||||
: return_value_policy::move;
|
||||
}
|
||||
|
||||
return type_caster<underlying_type>::cast(std::forward<T>(value), policy, parent);
|
||||
return impl::type_caster<underlying_type>::cast(std::forward<T>(value), policy, parent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,7 +119,7 @@ template <typename T>
|
||||
T cast(const handle& obj, bool convert) {
|
||||
assert(obj.ptr() != nullptr);
|
||||
|
||||
type_caster<T> caster = {};
|
||||
impl::type_caster<T> caster = {};
|
||||
|
||||
if(caster.load(obj, convert)) {
|
||||
return caster.value;
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "instance.h"
|
||||
#include "accessor.h"
|
||||
|
||||
namespace pybind11 {
|
||||
namespace pybind11::impl {
|
||||
|
||||
using pkpy::is_floating_point_v;
|
||||
using pkpy::is_integral_v;
|
||||
|
@ -169,15 +169,15 @@ public:
|
||||
}
|
||||
|
||||
enum_& value(const char* name, T value) {
|
||||
handle var = type_caster<T>::cast(value, return_value_policy::copy);
|
||||
this->m_ptr->attr().set(name, var.ptr());
|
||||
handle var = pybind11::cast(value, return_value_policy::copy);
|
||||
setattr(*this, name, var);
|
||||
m_values.emplace_back(name, var);
|
||||
return *this;
|
||||
}
|
||||
|
||||
enum_& export_values() {
|
||||
for(auto& [name, value]: m_values) {
|
||||
Base::m_scope.ptr()->attr().set(name, value.ptr());
|
||||
setattr(Base::m_scope, name, value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ public:
|
||||
template <typename Fn, std::size_t... Is, typename... Args>
|
||||
handle invoke(Fn&& fn,
|
||||
std::index_sequence<Is...>,
|
||||
std::tuple<type_caster<Args>...>& casters,
|
||||
std::tuple<impl::type_caster<Args>...>& casters,
|
||||
return_value_policy policy,
|
||||
handle parent) {
|
||||
using underlying_type = std::decay_t<Fn>;
|
||||
@ -361,10 +361,10 @@ struct template_parser<Callable, std::tuple<Extras...>, std::tuple<Args...>, std
|
||||
|
||||
// resolve keyword arguments
|
||||
const auto n = vm->s_data._sp - view.end();
|
||||
std::size_t index = 0;
|
||||
int index = 0;
|
||||
|
||||
if constexpr(named_argc > 0) {
|
||||
std::size_t arg_index = 0;
|
||||
int arg_index = 0;
|
||||
auto& arguments = *record.arguments;
|
||||
|
||||
while(arg_index < named_argc && index < n) {
|
||||
@ -403,7 +403,7 @@ struct template_parser<Callable, std::tuple<Extras...>, std::tuple<Args...>, std
|
||||
}
|
||||
|
||||
// ok, all the arguments are valid, call the function
|
||||
std::tuple<type_caster<Args>...> casters;
|
||||
std::tuple<impl::type_caster<Args>...> casters;
|
||||
|
||||
// check type compatibility
|
||||
if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
|
||||
@ -489,14 +489,14 @@ pkpy::PyVar setter_wrapper(pkpy::VM* vm, pkpy::ArgsView view) {
|
||||
|
||||
if constexpr(std::is_member_object_pointer_v<Setter>) {
|
||||
// specialize for pointer to data member
|
||||
type_caster<member_type_t<Setter>> caster;
|
||||
impl::type_caster<member_type_t<Setter>> caster;
|
||||
if(caster.load(view[1], true)) {
|
||||
self.*setter = caster.value;
|
||||
return vm->None;
|
||||
}
|
||||
} else {
|
||||
// specialize for pointer to member function
|
||||
type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
|
||||
impl::type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
|
||||
if(caster.load(view[1], true)) {
|
||||
(self.*setter)(caster.value);
|
||||
return vm->None;
|
||||
@ -507,7 +507,7 @@ pkpy::PyVar setter_wrapper(pkpy::VM* vm, pkpy::ArgsView view) {
|
||||
using Self = remove_cvref_t<std::tuple_element_t<0, callable_args_t<Setter>>>;
|
||||
auto& self = handle(view[0])._as<instance>()._as<Self>();
|
||||
|
||||
type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
|
||||
impl::type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
|
||||
if(caster.load(view[1], true)) {
|
||||
setter(self, caster.value);
|
||||
return vm->None;
|
||||
|
@ -37,7 +37,7 @@ class handle;
|
||||
class object;
|
||||
class iterator;
|
||||
class str;
|
||||
class arg;
|
||||
struct arg;
|
||||
struct args_proxy;
|
||||
struct kwargs_proxy;
|
||||
|
||||
@ -210,12 +210,7 @@ constexpr inline bool is_pyobject_v = std::is_base_of_v<object, T>;
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
using error_already_set = pkpy::TopLevelException;
|
||||
#else
|
||||
class error_already_set : std::exception {
|
||||
public:
|
||||
error_already_set() = default;
|
||||
|
||||
const char* what() const noexcept override { return "An error occurred while calling a Python function."; }
|
||||
};
|
||||
using error_already_set = pkpy::Exception;
|
||||
#endif
|
||||
|
||||
inline void setattr(const handle& obj, const handle& name, const handle& value);
|
||||
|
@ -19,8 +19,10 @@ public:
|
||||
}
|
||||
|
||||
module_ def_submodule(const char* name, const char* doc = nullptr) {
|
||||
auto package = this->package()._as<pkpy::Str>() + "." + this->name()._as<pkpy::Str>();
|
||||
auto m = vm->new_module(name, package);
|
||||
// TODO: resolve package
|
||||
//auto package = this->package()._as<pkpy::Str>() + "." + this->name()._as<pkpy::Str>();
|
||||
auto fname = this->name()._as<pkpy::Str>() + "." + name;
|
||||
auto m = vm->new_module(fname, "");
|
||||
setattr(*this, name, m);
|
||||
return m;
|
||||
}
|
||||
|
@ -69,15 +69,19 @@ public:
|
||||
template <typename T>
|
||||
decltype(auto) _as() const {
|
||||
static_assert(!std::is_reference_v<T>, "T must not be a reference type.");
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
if constexpr(pkpy::is_sso_v<T>) {
|
||||
return pkpy::_py_cast<T>(vm, ptr());
|
||||
if constexpr(std::is_same_v<T, empty>) {
|
||||
return empty();
|
||||
} else {
|
||||
return ptr().template obj_get<T>();
|
||||
}
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
if constexpr(pkpy::is_sso_v<T>) {
|
||||
return pkpy::_py_cast<T>(vm, ptr());
|
||||
} else {
|
||||
return ptr().template obj_get<T>();
|
||||
}
|
||||
#else
|
||||
return (((pkpy::Py_<T>*)ptr())->_value);
|
||||
return (((pkpy::Py_<T>*)ptr())->_value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -25,9 +25,9 @@ struct function_traits {
|
||||
static_assert(dependent_false<Fn>, "unsupported function type");
|
||||
};
|
||||
|
||||
#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(qualifiers) \
|
||||
#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(...) \
|
||||
template <typename R, typename... Args> \
|
||||
struct function_traits<R(Args...) qualifiers> { \
|
||||
struct function_traits<R(Args...) __VA_ARGS__> { \
|
||||
using return_type = R; \
|
||||
using args_type = std::tuple<Args...>; \
|
||||
constexpr static std::size_t args_count = sizeof...(Args); \
|
||||
@ -167,4 +167,32 @@ constexpr auto type_name() {
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
struct overload_cast_t {
|
||||
template <typename Return>
|
||||
constexpr auto operator() (Return (*pf)(Args...)) const noexcept -> decltype(pf) {
|
||||
return pf;
|
||||
}
|
||||
|
||||
template <typename Return, typename Class>
|
||||
constexpr auto operator() (Return (Class::*pmf)(Args...), std::false_type = {}) const noexcept -> decltype(pmf) {
|
||||
return pmf;
|
||||
}
|
||||
|
||||
template <typename Return, typename Class>
|
||||
constexpr auto operator() (Return (Class::*pmf)(Args...) const, std::true_type) const noexcept -> decltype(pmf) {
|
||||
return pmf;
|
||||
}
|
||||
};
|
||||
|
||||
/// Syntax sugar for resolving overloaded function pointers:
|
||||
/// - regular: static_cast<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
|
||||
/// - sweet: overload_cast<Arg0, Arg1, Arg2>(&Class::func)
|
||||
template <typename... Args>
|
||||
constexpr inline overload_cast_t<Args...> overload_cast;
|
||||
|
||||
/// Const member function selector for overload_cast
|
||||
/// - regular: static_cast<Return (Class::*)(Arg) const>(&Class::func)
|
||||
/// - sweet: overload_cast<Arg>(&Class::func, const_)
|
||||
constexpr static auto const_ = std::true_type{};
|
||||
} // namespace pybind11
|
||||
|
@ -49,6 +49,11 @@ public:
|
||||
return type_visitor::type<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static type of() {
|
||||
return type_visitor::type<T>();
|
||||
}
|
||||
|
||||
static type of(const handle& obj) { return type(vm->_t(obj.ptr())); }
|
||||
};
|
||||
|
||||
@ -385,11 +390,11 @@ public:
|
||||
};
|
||||
|
||||
class args : public tuple {
|
||||
PYBIND11_TYPE_IMPLEMENT(tuple, struct empty, vm->tp_tuple);
|
||||
PYBIND11_TYPE_IMPLEMENT(tuple, pybind11::empty, vm->tp_tuple);
|
||||
};
|
||||
|
||||
class kwargs : public dict {
|
||||
PYBIND11_TYPE_IMPLEMENT(dict, struct empty, vm->tp_dict);
|
||||
PYBIND11_TYPE_IMPLEMENT(dict, pybind11::empty, vm->tp_dict);
|
||||
};
|
||||
|
||||
} // namespace pybind11
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace pybind11 {
|
||||
namespace pybind11::impl {
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct type_caster<std::array<T, N>> {
|
||||
@ -140,5 +140,5 @@ struct type_caster<T, std::enable_if_t<is_py_map_like_v<T>>> {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pybind11
|
||||
} // namespace pybind11::impl
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user