mirror of
https://github.com/pocketpy/pocketpy
synced 2025-12-11 12:40:18 +00:00
Compare commits
42 Commits
76c251fb7f
...
981e6e6e88
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
981e6e6e88 | ||
|
|
066554f241 | ||
|
|
9bb19f4c9c | ||
|
|
6134175df4 | ||
|
|
51ce483c48 | ||
|
|
f803a7eca6 | ||
|
|
8d0e304ecd | ||
|
|
901cf714a2 | ||
|
|
37c22653f1 | ||
|
|
f162cd308a | ||
|
|
4ba0b7d2e2 | ||
|
|
2d6def4bbd | ||
|
|
a5a3c75b83 | ||
|
|
8ffcacf99f | ||
|
|
a2cb91b125 | ||
|
|
86006d0b61 | ||
|
|
a08c5af6e9 | ||
|
|
475be8ce80 | ||
|
|
12d748ba7d | ||
|
|
0c1bd532f8 | ||
|
|
d52cbec80c | ||
|
|
e16aa501ef | ||
|
|
59bbecdbee | ||
|
|
f5c356a047 | ||
|
|
68f8cd1159 | ||
|
|
259394d7ad | ||
|
|
fece79b169 | ||
|
|
95553438e1 | ||
|
|
a9d296dedc | ||
|
|
21d886f1a4 | ||
|
|
435ded95b5 | ||
|
|
f937fc1da8 | ||
|
|
71432a4054 | ||
|
|
cc4dd34c86 | ||
|
|
6736fa7f50 | ||
|
|
0406dfab78 | ||
|
|
f9c00fd706 | ||
|
|
56369bfa6f | ||
|
|
02b922a93e | ||
|
|
68c8f72ba5 | ||
|
|
845c9153e8 | ||
|
|
77e3d2e88f |
1
.github/workflows/website.yml
vendored
1
.github/workflows/website.yml
vendored
@ -26,7 +26,6 @@ jobs:
|
|||||||
- name: Setup emsdk
|
- name: Setup emsdk
|
||||||
uses: mymindstorm/setup-emsdk@v12
|
uses: mymindstorm/setup-emsdk@v12
|
||||||
with:
|
with:
|
||||||
version: 3.1.25
|
|
||||||
actions-cache-folder: 'emsdk-cache'
|
actions-cache-folder: 'emsdk-cache'
|
||||||
- name: Compile
|
- name: Compile
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,6 +4,7 @@ __pycache__/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.coverage
|
.coverage
|
||||||
.idea
|
.idea
|
||||||
|
.cache
|
||||||
|
|
||||||
gmon.out
|
gmon.out
|
||||||
gprof.txt
|
gprof.txt
|
||||||
@ -29,4 +30,4 @@ pocketpy.dSYM
|
|||||||
libpocketpy.dylib.dSYM/
|
libpocketpy.dylib.dSYM/
|
||||||
main.dSYM/
|
main.dSYM/
|
||||||
|
|
||||||
docs/references.md
|
docs/references.md
|
||||||
|
|||||||
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.
|
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 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
|
## Supported Platforms
|
||||||
|
|
||||||
@ -90,39 +91,33 @@ python scripts/run_tests.py
|
|||||||
### Example
|
### Example
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include "pocketpy.h"
|
#include <pybind11/pybind11.h>
|
||||||
|
|
||||||
using namespace pkpy;
|
namespace py = pybind11;
|
||||||
|
|
||||||
int main(){
|
int main() {
|
||||||
// Create a virtual machine
|
// Start the interpreter
|
||||||
VM* vm = new VM();
|
py::scoped_interpreter guard{};
|
||||||
|
|
||||||
// Hello world!
|
// Hello world!
|
||||||
vm->exec("print('Hello world!')");
|
py::exec("print('Hello world!')");
|
||||||
|
|
||||||
// Create a list
|
// Create a list
|
||||||
vm->exec("a = [1, 2, 3]");
|
py::exec("a = [1, 2, 3]");
|
||||||
|
|
||||||
// Eval the sum of the list
|
// Eval the sum of the list
|
||||||
PyVar result = vm->eval("sum(a)");
|
auto result = py::eval("sum(a)");
|
||||||
std::cout << "Sum of the list: "<< py_cast<int>(vm, result) << std::endl; // 6
|
std::cout << "Sum of the list: " << result.cast<int>() << std::endl; // 6
|
||||||
|
|
||||||
// Bindings
|
// Bindings
|
||||||
vm->bind(vm->_main, "add(a: int, b: int)",
|
auto m = py::module_::__main__();
|
||||||
[](VM* vm, ArgsView args){
|
m.def("add", [](int a, int b) {
|
||||||
int a = py_cast<int>(vm, args[0]);
|
return a + b;
|
||||||
int b = py_cast<int>(vm, args[1]);
|
});
|
||||||
return py_var(vm, a + b);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Call the function
|
// Call the function
|
||||||
PyVar f_add = vm->_main->attr("add");
|
std::cout << "Sum of 2 variables: " << m.attr("add")(1, 2).cast<int>() << std::endl; // 10
|
||||||
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
|
|
||||||
|
|
||||||
// Dispose the virtual machine
|
|
||||||
delete vm;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
23
benchmarks/vec.py
Normal file
23
benchmarks/vec.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
is_cpython = hasattr(sys, 'getrefcount')
|
||||||
|
|
||||||
|
if is_cpython:
|
||||||
|
class vec2:
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
return vec2(self.x + other.x, self.y + other.y)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.x == other.x and self.y == other.y
|
||||||
|
else:
|
||||||
|
from linalg import vec2
|
||||||
|
|
||||||
|
x = vec2(0, 0)
|
||||||
|
for i in range(10000000):
|
||||||
|
x += vec2(1, 1)
|
||||||
|
|
||||||
|
assert x == vec2(10000000, 10000000)
|
||||||
218
docs/1_5_0.md
218
docs/1_5_0.md
@ -1,218 +0,0 @@
|
|||||||
---
|
|
||||||
icon: log
|
|
||||||
title: 'Upgrade to v1.5.0'
|
|
||||||
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<>`.
|
|
||||||
|
|
||||||
In this release, we added an ARGC-based binding `vm->bind_func` (without template parameter)
|
|
||||||
to replace the old style bindings. If you are still using `vm->bind_func<>` and `vm->bind_method<>`,
|
|
||||||
please update your code to use `vm->bind_func` instead.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// old style (removed)
|
|
||||||
vm->bind_func<N>(obj, "add", f_add);
|
|
||||||
vm->bind_method<M>(obj, "get", f_get);
|
|
||||||
|
|
||||||
// new ARGC-based binding
|
|
||||||
vm->bind_func(obj, "add", N, f_add);
|
|
||||||
vm->bind_func(obj, "get", M+1, f_get); // +1 for `self`
|
|
||||||
```
|
|
||||||
|
|
||||||
## Class bindings
|
|
||||||
|
|
||||||
Previously, if we want to write bindings for a C++ class, we need to add `PY_CLASS` macro and implement a magic method `_register`. This way was known as an intrusive way. For example:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct Point{
|
|
||||||
PY_CLASS(Point, test, Point)
|
|
||||||
|
|
||||||
int x, y;
|
|
||||||
|
|
||||||
static void _register(VM* vm, PyVar mod, PyVar type){
|
|
||||||
// do bindings here
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(){
|
|
||||||
VM* vm = new VM();
|
|
||||||
PyVar mod = vm->new_module("test");
|
|
||||||
Point::register_class(vm, mod);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In `v1.5.0`, the `PY_CLASS` macro was deprecated. We introduced a non-intrusive way to write class bindings via `vm->register_user_class<>`. The example above can be rewritten as follows:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct Point{
|
|
||||||
int x, y;
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(){
|
|
||||||
VM* vm = new VM();
|
|
||||||
PyVar mod = vm->new_module("test");
|
|
||||||
vm->register_user_class<Point>(mod, "Point",
|
|
||||||
[](VM* vm, PyVar mod, PyVar type){
|
|
||||||
// do bindings here
|
|
||||||
});
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The magic method `_register` is kept for users who still wants to use the intrusive way.
|
|
||||||
This is achieved by an overloaded version of `vm->register_user_class<>`. For example:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct Point{
|
|
||||||
int x, y;
|
|
||||||
|
|
||||||
static void _register(VM* vm, PyVar mod, PyVar type){
|
|
||||||
// do bindings here (if you like the intrusive way)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(){
|
|
||||||
VM* vm = new VM();
|
|
||||||
PyVar mod = vm->new_module("test");
|
|
||||||
vm->register_user_class<Point>(mod, "Point");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Signature of `_import_handler`
|
|
||||||
|
|
||||||
The signature of `_import_handler` was changed from:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
unsigned char* (*)(const char* name_p, int name_size, int* out_size);
|
|
||||||
```
|
|
||||||
|
|
||||||
to:
|
|
||||||
```cpp
|
|
||||||
unsigned char* (*)(const char* name, int* out_size);
|
|
||||||
```
|
|
||||||
|
|
||||||
This is because `str` object was ensured to be null-terminated after `v1.4.1`.
|
|
||||||
|
|
||||||
## Signature of `bind__next__`
|
|
||||||
|
|
||||||
`vm->bind__next__` is a special method that is used to implement the iterator protocol.
|
|
||||||
Previously, if you want to return multiple values, you need to pack them into a tuple.
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
vm->bind__next__(type, [](VM* vm, PyVar _0){
|
|
||||||
if(is_end) return vm->StopIteration;
|
|
||||||
// ...
|
|
||||||
PyVar a = VAR(1);
|
|
||||||
PyVar b = VAR(2);
|
|
||||||
return VAR(Tuple(a, b));
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
|
||||||
for a, b in my_iter:
|
|
||||||
# There is tuple creation and destruction for each iteration
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
It is not efficient because unnecessary tuple creation and destruction are involved.
|
|
||||||
In `v1.5.0`, you need to use stack-based style to reimplement the above code:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
vm->bind__next__(type, [](VM* vm, PyVar _0) -> unsigned{
|
|
||||||
if(is_end) return 0; // return 0 indicates StopIteration
|
|
||||||
// ...
|
|
||||||
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
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
In this way, the interpreter only creates a tuple when it is necessary.
|
|
||||||
It can improve ~25% performance when iterating over a large array.
|
|
||||||
|
|
||||||
```python
|
|
||||||
for a, b in my_iter:
|
|
||||||
# No tuple creation and destruction
|
|
||||||
...
|
|
||||||
|
|
||||||
for t in my_iter:
|
|
||||||
# Create a tuple lazily
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Builtin function `next()`
|
|
||||||
|
|
||||||
Previously, `next()` returns `StopIteration` when the iterator is exhausted.
|
|
||||||
In `v1.5.0`, it raises `StopIteration` exception instead, which is consistent with CPython.
|
|
||||||
If you like the old way, you can use the following snippet to import the old `next()` function:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from __builtins import next
|
|
||||||
```
|
|
||||||
|
|
||||||
Related C++ APIs do not change. They still return `vm->StopIteration` to indicate the end of iteration.
|
|
||||||
|
|
||||||
## User config support
|
|
||||||
|
|
||||||
We used to read `user_config.h` file to override the default configurations.
|
|
||||||
In `v1.5.0`, this is no longer supported.
|
|
||||||
Please use config macros before `#include "pocketpy.h"` directly.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define PK_ENABLE_OS 1
|
|
||||||
#define PK_ENABLE_THREAD 1
|
|
||||||
#define PK_ENABLE_PROFILER 1
|
|
||||||
// for all config macros, please refer to `include/pocketpy/config.h`
|
|
||||||
#include "pocketpy.h"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Debugger and profiler
|
|
||||||
|
|
||||||
We added a macro `PK_ENABLE_PROFILER` (default is 0) to control the availability of the builtin debugger and profiler.
|
|
||||||
If you want to use them, for example, you want to call `breakpoint()` or `import line_profiler`, you need to set `PK_ENABLE_PROFILER` to 1 before `#include "pocketpy.h"`.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define PK_ENABLE_PROFILER 1
|
|
||||||
#include "pocketpy.h"
|
|
||||||
```
|
|
||||||
|
|
||||||
!!!
|
|
||||||
Enabling the profiler has a performance overhead. Only enable it when you need it.
|
|
||||||
!!!
|
|
||||||
|
|
||||||
## Type checking functions
|
|
||||||
|
|
||||||
+ `vm->is_non_tagged_type` was removed. Use `vm->is_type` instead.
|
|
||||||
+ `vm->check_non_tagged_type` was removed. Use `vm->check_type` instead.
|
|
||||||
|
|
||||||
## Python Stringify functions
|
|
||||||
|
|
||||||
The following functions now return `Str` object instead of `PyVar`:
|
|
||||||
|
|
||||||
+ `vm->py_str`
|
|
||||||
+ `vm->py_repr`
|
|
||||||
+ `vm->py_json`
|
|
||||||
|
|
||||||
Also, `vm->bind__str__` and `vm->bind__repr__` were changed to return `Str` object.
|
|
||||||
@ -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
|
# 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.
|
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.
|
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_EMBEDDED_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
|
url: https://pocketpy.dev
|
||||||
branding:
|
branding:
|
||||||
title: pocketpy
|
title: pocketpy
|
||||||
label: v1.5.0
|
label: v1.4.6
|
||||||
logo: "./static/logo.png"
|
logo: "./static/logo.png"
|
||||||
favicon: "./static/logo.png"
|
favicon: "./static/logo.png"
|
||||||
meta:
|
meta:
|
||||||
@ -16,10 +16,10 @@ links:
|
|||||||
icon: play
|
icon: play
|
||||||
link: "https://pocketpy.dev/static/web/"
|
link: "https://pocketpy.dev/static/web/"
|
||||||
target: blank
|
target: blank
|
||||||
- text: "Live Examples"
|
# - text: "Live Examples"
|
||||||
icon: play
|
# icon: play
|
||||||
link: "https://pocketpy.github.io/examples/"
|
# link: "https://pocketpy.github.io/examples/"
|
||||||
target: blank
|
# target: blank
|
||||||
- text: "Github"
|
- text: "Github"
|
||||||
icon: mark-github
|
icon: mark-github
|
||||||
link: https://github.com/blueloveth/pocketpy
|
link: https://github.com/blueloveth/pocketpy
|
||||||
|
|||||||
@ -20,7 +20,10 @@
|
|||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
|
||||||
#define PK_VERSION "1.5.0"
|
#define PK_VERSION "1.4.6"
|
||||||
|
#define PK_VERSION_MAJOR 1
|
||||||
|
#define PK_VERSION_MINOR 4
|
||||||
|
#define PK_VERSION_PATCH 6
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "export.h"
|
#include "export.h"
|
||||||
|
|||||||
@ -73,6 +73,7 @@
|
|||||||
#pragma warning (disable:4100)
|
#pragma warning (disable:4100)
|
||||||
#pragma warning (disable:4244)
|
#pragma warning (disable:4244)
|
||||||
#pragma warning (disable:4996)
|
#pragma warning (disable:4996)
|
||||||
|
#pragma warning (disable:4018)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
|||||||
@ -224,6 +224,7 @@ extern const StrName __package__;
|
|||||||
extern const StrName __path__;
|
extern const StrName __path__;
|
||||||
extern const StrName __class__;
|
extern const StrName __class__;
|
||||||
extern const StrName __missing__;
|
extern const StrName __missing__;
|
||||||
|
extern const StrName __abs__;
|
||||||
|
|
||||||
extern const StrName pk_id_add;
|
extern const StrName pk_id_add;
|
||||||
extern const StrName pk_id_set;
|
extern const StrName pk_id_set;
|
||||||
|
|||||||
@ -290,7 +290,7 @@ namespace pkpy
|
|||||||
const auto size = other.size();
|
const auto size = other.size();
|
||||||
const auto capacity = other.capacity();
|
const auto capacity = other.capacity();
|
||||||
m_begin = reinterpret_cast<T*>(other.is_small() ? m_buffer : std::malloc(sizeof(T) * capacity));
|
m_begin = reinterpret_cast<T*>(other.is_small() ? m_buffer : std::malloc(sizeof(T) * capacity));
|
||||||
uninitialized_copy_n(other.begin, size, this->m_begin);
|
uninitialized_copy_n(other.m_begin, size, this->m_begin);
|
||||||
m_end = m_begin + size;
|
m_end = m_begin + size;
|
||||||
m_max = m_begin + capacity;
|
m_max = m_begin + capacity;
|
||||||
}
|
}
|
||||||
|
|||||||
2
include/pybind11/embed.h
Normal file
2
include/pybind11/embed.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "pybind11.h"
|
||||||
2
include/pybind11/functional.h
Normal file
2
include/pybind11/functional.h
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "pybind11.h"
|
||||||
173
include/pybind11/internal/accessor.h
Normal file
173
include/pybind11/internal/accessor.h
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "builtins.h"
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
|
||||||
|
// implement iterator methods for interface
|
||||||
|
template <typename Derived>
|
||||||
|
inline iterator interface<Derived>::begin() const {
|
||||||
|
return handle(vm->py_iter(this->ptr()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline iterator interface<Derived>::end() const {
|
||||||
|
return iterator::sentinel();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline str interface<Derived>::package() const {
|
||||||
|
return handle(this->attr(pkpy::__package__));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline str interface<Derived>::name() const {
|
||||||
|
return handle(this->attr(pkpy::__name__));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline str interface<Derived>::repr() const {
|
||||||
|
return handle(str(vm->py_repr(this->ptr())));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename policy>
|
||||||
|
class accessor : public interface<accessor<policy>> {
|
||||||
|
|
||||||
|
using key_type = typename policy::key_type;
|
||||||
|
|
||||||
|
handle m_obj;
|
||||||
|
mutable handle m_value;
|
||||||
|
key_type m_key;
|
||||||
|
|
||||||
|
friend interface<handle>;
|
||||||
|
friend interface<accessor<policy>>;
|
||||||
|
friend tuple;
|
||||||
|
friend list;
|
||||||
|
friend dict;
|
||||||
|
|
||||||
|
accessor(const handle& obj, key_type key) : m_obj(obj), m_value(), m_key(key) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
pkpy::PyVar ptr() const {
|
||||||
|
if(!m_value) { m_value = policy::get(m_obj, m_key); }
|
||||||
|
return m_value.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Value>
|
||||||
|
accessor& operator= (Value&& value) && {
|
||||||
|
policy::set(m_obj, m_key, std::forward<Value>(value));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Value>
|
||||||
|
accessor& operator= (Value&& value) & {
|
||||||
|
m_value = std::forward<Value>(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T cast() const {
|
||||||
|
return operator handle ().template cast<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
operator handle () const { return ptr(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace policy {
|
||||||
|
struct attr {
|
||||||
|
using key_type = pkpy::StrName;
|
||||||
|
|
||||||
|
static handle get(const handle& obj, pkpy::StrName key) { return vm->getattr(obj.ptr(), key); }
|
||||||
|
|
||||||
|
static void set(const handle& obj, pkpy::StrName key, const handle& value) {
|
||||||
|
vm->setattr(obj.ptr(), key, value.ptr());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct item {
|
||||||
|
using key_type = handle;
|
||||||
|
|
||||||
|
static handle get(const handle& obj, const handle& key) {
|
||||||
|
return vm->call(vm->py_op("getitem"), obj.ptr(), key.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(const handle& obj, const handle& key, const handle& value) {
|
||||||
|
vm->call(vm->py_op("setitem"), obj.ptr(), key.ptr(), value.ptr());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tuple {
|
||||||
|
using key_type = int;
|
||||||
|
|
||||||
|
static handle get(const handle& obj, int key) { return obj._as<pkpy::Tuple>()[key]; }
|
||||||
|
|
||||||
|
static void set(const handle& obj, size_t key, const handle& value) { obj._as<pkpy::Tuple>()[key] = value.ptr(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct list {
|
||||||
|
using key_type = int;
|
||||||
|
|
||||||
|
static handle get(const handle& obj, size_t key) { return obj._as<pkpy::List>()[key]; }
|
||||||
|
|
||||||
|
static void set(const handle& obj, size_t key, const handle& value) { obj._as<pkpy::List>()[key] = value.ptr(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dict {
|
||||||
|
using key_type = handle;
|
||||||
|
|
||||||
|
static handle get(const handle& obj, const handle& key) { return obj.cast<pybind11::dict>().getitem(key); }
|
||||||
|
|
||||||
|
static void set(const handle& obj, const handle& key, const handle& value) {
|
||||||
|
obj.cast<pybind11::dict>().setitem(key, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace policy
|
||||||
|
|
||||||
|
// implement other methods of interface
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline attr_accessor interface<Derived>::attr(pkpy::StrName key) const {
|
||||||
|
return attr_accessor(this->ptr(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline attr_accessor interface<Derived>::attr(const char* key) const {
|
||||||
|
return attr_accessor(this->ptr(), pkpy::StrName(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline attr_accessor interface<Derived>::attr(const handle& key) const {
|
||||||
|
return attr_accessor(this->ptr(), pkpy::StrName(key._as<pkpy::Str>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline attr_accessor interface<Derived>::doc() const {
|
||||||
|
return attr_accessor(this->ptr(), pkpy::StrName("__doc__"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline item_accessor interface<Derived>::operator[] (int index) const {
|
||||||
|
return item_accessor(this->ptr(), int_(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline item_accessor interface<Derived>::operator[] (const char* key) const {
|
||||||
|
return item_accessor(this->ptr(), str(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline item_accessor interface<Derived>::operator[] (const handle& key) const {
|
||||||
|
return item_accessor(this->ptr(), key);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline tuple_accessor tuple::operator[] (int i) const { return tuple_accessor(this->ptr(), i); }
|
||||||
|
|
||||||
|
inline list_accessor list::operator[] (int i) const { return list_accessor(this->ptr(), i); }
|
||||||
|
|
||||||
|
inline dict_accessor dict::operator[] (int index) const { return dict_accessor(this->ptr(), int_(index)); }
|
||||||
|
|
||||||
|
inline dict_accessor dict::operator[] (std::string_view key) const { return dict_accessor(this->ptr(), str(key)); }
|
||||||
|
|
||||||
|
inline dict_accessor dict::operator[] (const handle& key) const { return dict_accessor(this->ptr(), key); }
|
||||||
|
|
||||||
|
} // namespace pybind11
|
||||||
226
include/pybind11/internal/builtins.h
Normal file
226
include/pybind11/internal/builtins.h
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
#include "type_traits.h"
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
|
||||||
|
inline object eval(std::string_view code, const handle& global = none{}, handle local = none{}) {
|
||||||
|
return vm->py_eval(code, global.ptr(), local.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void exec(std::string_view code, const handle& global = none{}, handle local = none{}) {
|
||||||
|
vm->py_exec(code, global.ptr(), local.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// globas() in pkpy is immutable, your changes will not be reflected in the Python interpreter
|
||||||
|
inline dict globals() {
|
||||||
|
auto& proxy = eval("globals()")._as<pkpy::MappingProxy>().attr();
|
||||||
|
dict result;
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
proxy.apply(
|
||||||
|
[](pkpy::StrName key, pkpy::PyVar value, void* data) {
|
||||||
|
auto& dict = static_cast<pybind11::dict*>(data)->_as<pkpy::Dict>();
|
||||||
|
auto key_ = pybind11::str(key.sv()).ptr();
|
||||||
|
dict.set(vm, key_, value);
|
||||||
|
},
|
||||||
|
&result);
|
||||||
|
#else
|
||||||
|
proxy.apply([&](pkpy::StrName key, pkpy::PyVar value) {
|
||||||
|
result.setitem(str(key.sv()), value);
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapper for builtin functions in Python
|
||||||
|
inline bool hasattr(const handle& obj, const handle& name) {
|
||||||
|
return vm->getattr(obj.ptr(), name._as<pkpy::Str>(), false) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool hasattr(const handle& obj, const char* name) { return vm->getattr(obj.ptr(), name, false) != nullptr; }
|
||||||
|
|
||||||
|
inline void delattr(const handle& obj, const handle& name) { vm->delattr(obj.ptr(), name._as<pkpy::Str>()); }
|
||||||
|
|
||||||
|
inline void delattr(const handle& obj, const char* name) { vm->delattr(obj.ptr(), name); }
|
||||||
|
|
||||||
|
inline object getattr(const handle& obj, const handle& name) { return vm->getattr(obj.ptr(), name._as<pkpy::Str>()); }
|
||||||
|
|
||||||
|
inline object getattr(const handle& obj, const char* name) { return vm->getattr(obj.ptr(), name); }
|
||||||
|
|
||||||
|
inline object getattr(const handle& obj, const handle& name, const handle& default_) {
|
||||||
|
auto attr = vm->getattr(obj.ptr(), name._as<pkpy::Str>(), false);
|
||||||
|
if(attr) { return attr; }
|
||||||
|
return default_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline object getattr(const handle& obj, const char* name, const handle& default_) {
|
||||||
|
auto attr = vm->getattr(obj.ptr(), name, false);
|
||||||
|
if(attr) { return attr; }
|
||||||
|
return default_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setattr(const handle& obj, const handle& name, const handle& value) {
|
||||||
|
vm->setattr(obj.ptr(), name._as<pkpy::Str>(), value.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setattr(const handle& obj, const char* name, const handle& value) {
|
||||||
|
vm->setattr(obj.ptr(), name, value.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline bool isinstance(const handle& obj) {
|
||||||
|
return type_visitor::check<T>(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool isinstance<handle>(const handle&) = delete;
|
||||||
|
|
||||||
|
inline bool isinstance(const handle& obj, const handle& type) {
|
||||||
|
return vm->isinstance(obj.ptr(), type._as<pkpy::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.
|
||||||
|
using underlying_type = std::decay_t<T>;
|
||||||
|
|
||||||
|
if constexpr(std::is_convertible_v<underlying_type, handle>) {
|
||||||
|
return std::forward<T>(value);
|
||||||
|
} else {
|
||||||
|
static_assert(!is_multiple_pointer_v<underlying_type>, "multiple pointer is not supported.");
|
||||||
|
static_assert(!std::is_void_v<std::remove_pointer_t<underlying_type>>,
|
||||||
|
"void* is not supported, consider using py::capsule.");
|
||||||
|
|
||||||
|
// resolve for automatic policy.
|
||||||
|
if(policy == return_value_policy::automatic) {
|
||||||
|
policy = std::is_pointer_v<underlying_type> ? return_value_policy::take_ownership
|
||||||
|
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
|
||||||
|
: return_value_policy::move;
|
||||||
|
} else if(policy == return_value_policy::automatic_reference) {
|
||||||
|
policy = std::is_pointer_v<underlying_type> ? return_value_policy::reference
|
||||||
|
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
|
||||||
|
: return_value_policy::move;
|
||||||
|
}
|
||||||
|
|
||||||
|
return impl::type_caster<underlying_type>::cast(std::forward<T>(value), policy, parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T cast(const handle& obj, bool convert) {
|
||||||
|
assert(obj.ptr() != nullptr);
|
||||||
|
|
||||||
|
impl::type_caster<T> caster = {};
|
||||||
|
|
||||||
|
if(caster.load(obj, convert)) {
|
||||||
|
return caster.value;
|
||||||
|
} else {
|
||||||
|
std::string msg = "cast python instance to c++ failed, ";
|
||||||
|
msg += "obj type is: {";
|
||||||
|
msg += type::of(obj).name();
|
||||||
|
msg += "}, target type is: {";
|
||||||
|
msg += type_name<T>();
|
||||||
|
msg += "}.";
|
||||||
|
vm->TypeError(msg);
|
||||||
|
PK_UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kwargs_proxy {
|
||||||
|
handle value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct args_proxy {
|
||||||
|
handle value;
|
||||||
|
|
||||||
|
kwargs_proxy operator* () { return kwargs_proxy{value}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
args_proxy interface<Derived>::operator* () const {
|
||||||
|
return args_proxy{ptr()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
handle interpreter::vectorcall(const handle& callable, const handle& self, const Args&... args) {
|
||||||
|
vm->s_data.push(callable.ptr());
|
||||||
|
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
vm->s_data.push(self ? self.ptr() : PY_NULL);
|
||||||
|
#else
|
||||||
|
vm->s_data.push(self ? self.ptr() : pkpy::PY_NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int argc = 0;
|
||||||
|
int kwargsc = 0;
|
||||||
|
|
||||||
|
auto push_arg = [&](const handle& value) {
|
||||||
|
assert(value);
|
||||||
|
vm->s_data.push(value.ptr());
|
||||||
|
argc++;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto push_named_arg = [&](std::string_view name, const handle& value) {
|
||||||
|
assert(value);
|
||||||
|
vm->s_data.push(int_(pkpy::StrName(name).index).ptr());
|
||||||
|
vm->s_data.push(value.ptr());
|
||||||
|
kwargsc++;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto foreach_ = [&](const auto& arg) {
|
||||||
|
using T = std::decay_t<decltype(arg)>;
|
||||||
|
if constexpr(std::is_convertible_v<T, handle>) {
|
||||||
|
push_arg(arg);
|
||||||
|
} else if constexpr(std::is_same_v<T, args_proxy>) {
|
||||||
|
pybind11::tuple args = arg.value.template cast<pybind11::tuple>();
|
||||||
|
for(auto item: args) {
|
||||||
|
push_arg(item);
|
||||||
|
}
|
||||||
|
} else if constexpr(std::is_same_v<T, pybind11::arg>) {
|
||||||
|
push_named_arg(arg.name, arg.default_);
|
||||||
|
} else if constexpr(std::is_same_v<T, kwargs_proxy>) {
|
||||||
|
pybind11::dict kwargs = arg.value.template cast<pybind11::dict>();
|
||||||
|
for(auto item: kwargs) {
|
||||||
|
str name = item.first.template cast<str>();
|
||||||
|
push_named_arg(name, item.second);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
static_assert(dependent_false<T>, "unsupported type");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(foreach_(args), ...);
|
||||||
|
|
||||||
|
return vm->vectorcall(argc, kwargsc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
template <return_value_policy policy, typename... Args>
|
||||||
|
inline object interface<Derived>::operator() (Args&&... args) const {
|
||||||
|
auto _cast = [&](auto&& arg) {
|
||||||
|
using T = std::decay_t<decltype(arg)>;
|
||||||
|
if constexpr(std::is_same_v<T, pybind11::arg> || std::is_same_v<T, kwargs_proxy> ||
|
||||||
|
std::is_same_v<T, args_proxy> || std::is_convertible_v<T, handle>) {
|
||||||
|
return arg;
|
||||||
|
} else {
|
||||||
|
return pybind11::cast(std::forward<decltype(arg)>(arg), policy);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return interpreter::vectorcall(ptr(), handle(), _cast(std::forward<Args>(args))...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
void print(Args&&... args) {
|
||||||
|
handle print = getattr(vm->builtins, "print");
|
||||||
|
print(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pybind11
|
||||||
174
include/pybind11/internal/cast.h
Normal file
174
include/pybind11/internal/cast.h
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "instance.h"
|
||||||
|
#include "accessor.h"
|
||||||
|
|
||||||
|
namespace pybind11::impl {
|
||||||
|
|
||||||
|
using pkpy::is_floating_point_v;
|
||||||
|
using pkpy::is_integral_v;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline bool is_string_v =
|
||||||
|
std::is_same_v<T, const char*> || std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool is_pointer_v = std::is_pointer_v<T> && !std::is_same_v<T, char*> && !std::is_same_v<T, const char*>;
|
||||||
|
|
||||||
|
template <typename T, typename>
|
||||||
|
struct type_caster;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct type_caster<bool> {
|
||||||
|
bool value;
|
||||||
|
|
||||||
|
bool load(const handle& src, bool) {
|
||||||
|
if(isinstance<pybind11::bool_>(src)) {
|
||||||
|
value = pkpy::_py_cast<bool>(vm, src.ptr());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(bool src, return_value_policy, handle) { return src ? vm->True : vm->False; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct type_caster<T, std::enable_if_t<is_integral_v<T>>> {
|
||||||
|
T value;
|
||||||
|
|
||||||
|
bool load(const handle& src, bool convert) {
|
||||||
|
if(isinstance<pybind11::int_>(src)) {
|
||||||
|
value = pkpy::_py_cast<T>(vm, src.ptr());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct type_caster<T, std::enable_if_t<is_floating_point_v<T>>> {
|
||||||
|
T value;
|
||||||
|
|
||||||
|
bool load(const handle& src, bool convert) {
|
||||||
|
if(isinstance<pybind11::float_>(src)) {
|
||||||
|
value = pkpy::_py_cast<T>(vm, src.ptr());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(convert && isinstance<pybind11::int_>(src)) {
|
||||||
|
value = pkpy::_py_cast<int64_t>(vm, src.ptr());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct type_caster<T, std::enable_if_t<is_string_v<T>>> {
|
||||||
|
T value;
|
||||||
|
|
||||||
|
bool load(const handle& src, bool) {
|
||||||
|
if(isinstance<pybind11::str>(src)) {
|
||||||
|
auto& str = src._as<pkpy::Str>();
|
||||||
|
if constexpr(std::is_same_v<T, std::string>) {
|
||||||
|
value = str;
|
||||||
|
} else if constexpr(std::is_same_v<T, std::string_view>) {
|
||||||
|
value = str;
|
||||||
|
} else if constexpr(std::is_same_v<T, const char*>) {
|
||||||
|
value = str.c_str();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static handle cast(U&& src, return_value_policy, handle) {
|
||||||
|
return str(std::forward<U>(src));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct type_caster<T, std::enable_if_t<is_pyobject_v<T>>> {
|
||||||
|
T value;
|
||||||
|
|
||||||
|
bool load(const handle& src, bool) {
|
||||||
|
if(isinstance<T>(src)) {
|
||||||
|
value = src;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle cast(const handle& src, return_value_policy, handle) { return src; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename>
|
||||||
|
struct type_caster {
|
||||||
|
struct value_wrapper {
|
||||||
|
T* pointer;
|
||||||
|
|
||||||
|
operator T& () { return *pointer; }
|
||||||
|
};
|
||||||
|
|
||||||
|
value_wrapper value;
|
||||||
|
|
||||||
|
using underlying_type = std::remove_pointer_t<decltype(value.pointer)>;
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if(isinstance<underlying_type>(src)) {
|
||||||
|
auto& i = src._as<instance>();
|
||||||
|
value.pointer = &i._as<underlying_type>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static handle cast(U&& value, return_value_policy policy, const handle& parent = handle()) {
|
||||||
|
const auto& info = typeid(underlying_type);
|
||||||
|
auto type = type_visitor::type<underlying_type>();
|
||||||
|
return instance::create(std::forward<U>(value), type, policy, parent.ptr());
|
||||||
|
// TODO: support implicit cast
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct type_caster<T, std::enable_if_t<is_pointer_v<T> || std::is_reference_v<T>>> {
|
||||||
|
using underlying =
|
||||||
|
std::remove_cv_t<std::conditional_t<is_pointer_v<T>, std::remove_pointer_t<T>, std::remove_reference_t<T>>>;
|
||||||
|
|
||||||
|
struct wrapper {
|
||||||
|
type_caster<underlying> caster;
|
||||||
|
|
||||||
|
operator T () {
|
||||||
|
if constexpr(std::is_pointer_v<T>) {
|
||||||
|
return caster.value.pointer;
|
||||||
|
} else {
|
||||||
|
return caster.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapper value;
|
||||||
|
|
||||||
|
bool load(const handle& src, bool convert) { return value.caster.load(src, convert); }
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static handle cast(U&& value, return_value_policy policy, const handle& parent) {
|
||||||
|
return type_caster<underlying>::cast(std::forward<U>(value), policy, parent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pybind11
|
||||||
|
|
||||||
207
include/pybind11/internal/class.h
Normal file
207
include/pybind11/internal/class.h
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "module.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
|
||||||
|
struct dynamic_attr {};
|
||||||
|
|
||||||
|
template <typename T, typename Base = void>
|
||||||
|
class class_ : public type {
|
||||||
|
protected:
|
||||||
|
handle m_scope;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using type::type;
|
||||||
|
using underlying_type = T;
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
class_(const handle& scope, const char* name, const Args&... args) :
|
||||||
|
m_scope(scope), type(type_visitor::create<T, Base>(scope, name)) {
|
||||||
|
auto& info = type_info::of<T>();
|
||||||
|
info.name = name;
|
||||||
|
|
||||||
|
// bind __new__
|
||||||
|
interpreter::bind_func(m_ptr, pkpy::__new__, -1, [](pkpy::VM* vm, pkpy::ArgsView args) {
|
||||||
|
auto cls = handle(args[0])._as<pkpy::Type>();
|
||||||
|
|
||||||
|
// check if the class has constructor, if not, raise error
|
||||||
|
if(vm->find_name_in_mro(cls, pkpy::__init__) == nullptr) {
|
||||||
|
vm->RuntimeError("if you want to create instance of bound class, you must bind constructor for it");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto var = instance::create(cls, &type_info::of<T>());
|
||||||
|
|
||||||
|
if constexpr(types_count_v<dynamic_attr, Args...> != 0) {
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
var.get()->_attr = new pkpy::NameDict();
|
||||||
|
#else
|
||||||
|
var->_enable_instance_dict();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return var;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// bind constructor
|
||||||
|
template <typename... Args, typename... Extra>
|
||||||
|
class_& def(impl::constructor<Args...>, const Extra&... extra) {
|
||||||
|
if constexpr(!std::is_constructible_v<T, Args...>) {
|
||||||
|
static_assert(std::is_constructible_v<T, Args...>, "Invalid constructor arguments");
|
||||||
|
} else {
|
||||||
|
impl::bind_function<true>(
|
||||||
|
*this,
|
||||||
|
"__init__",
|
||||||
|
[](T* self, Args... args) {
|
||||||
|
new (self) T(args...);
|
||||||
|
},
|
||||||
|
pkpy::BindType::DEFAULT,
|
||||||
|
extra...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Fn, typename... Extra>
|
||||||
|
class_& def(impl::factory<Fn> factory, const Extra&... extra) {
|
||||||
|
using ret = callable_return_t<Fn>;
|
||||||
|
|
||||||
|
if constexpr(!std::is_same_v<T, ret>) {
|
||||||
|
static_assert(std::is_same_v<T, ret>, "Factory function must return the class type");
|
||||||
|
} else {
|
||||||
|
impl::bind_function<true>(*this, "__init__", factory.make(), pkpy::BindType::DEFAULT, extra...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// bind member function
|
||||||
|
template <typename Fn, typename... Extra>
|
||||||
|
class_& def(const char* name, Fn&& f, const Extra&... extra) {
|
||||||
|
using first = remove_cvref_t<std::tuple_element_t<0, callable_args_t<remove_cvref_t<Fn>>>>;
|
||||||
|
constexpr bool is_first_base_of_v = std::is_base_of_v<first, T> || std::is_same_v<first, T>;
|
||||||
|
|
||||||
|
if constexpr(!is_first_base_of_v) {
|
||||||
|
static_assert(is_first_base_of_v,
|
||||||
|
"If you want to bind member function, the first argument must be the base class");
|
||||||
|
} else {
|
||||||
|
impl::bind_function<true>(*this, name, std::forward<Fn>(f), pkpy::BindType::DEFAULT, extra...);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// bind operators
|
||||||
|
template <typename Operator, typename... Extras>
|
||||||
|
class_& def(Operator op, const Extras&... extras) {
|
||||||
|
op.execute(*this, extras...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: factory function
|
||||||
|
|
||||||
|
/// bind static function
|
||||||
|
template <typename Fn, typename... Extra>
|
||||||
|
class_& def_static(const char* name, Fn&& f, const Extra&... extra) {
|
||||||
|
impl::bind_function<false>(*this, name, std::forward<Fn>(f), pkpy::BindType::STATICMETHOD, extra...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename MP, typename... Extras>
|
||||||
|
class_& def_readwrite(const char* name, MP mp, const Extras&... extras) {
|
||||||
|
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
||||||
|
static_assert(std::is_member_object_pointer_v<MP>, "def_readwrite only supports pointer to data member");
|
||||||
|
} else {
|
||||||
|
impl::bind_property(*this, name, mp, mp, extras...);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename MP, typename... Extras>
|
||||||
|
class_& def_readonly(const char* name, MP mp, const Extras&... extras) {
|
||||||
|
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
||||||
|
static_assert(std::is_member_object_pointer_v<MP>, "def_readonly only supports pointer to data member");
|
||||||
|
} else {
|
||||||
|
impl::bind_property(*this, name, mp, nullptr, extras...);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Getter, typename Setter, typename... Extras>
|
||||||
|
class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
||||||
|
impl::bind_property(*this, name, std::forward<Getter>(g), std::forward<Setter>(s), extras...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Getter, typename... Extras>
|
||||||
|
class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) {
|
||||||
|
impl::bind_property(*this, name, std::forward<Getter>(mp), nullptr, extras...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Var, typename... Extras>
|
||||||
|
class_& def_readwrite_static(const char* name, Var& mp, const Extras&... extras) {
|
||||||
|
static_assert(
|
||||||
|
dependent_false<Var>,
|
||||||
|
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Var, typename... Extras>
|
||||||
|
class_& def_readonly_static(const char* name, Var& mp, const Extras&... extras) {
|
||||||
|
static_assert(
|
||||||
|
dependent_false<Var>,
|
||||||
|
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Getter, typename Setter, typename... Extras>
|
||||||
|
class_& def_property_static(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
||||||
|
static_assert(
|
||||||
|
dependent_false<Getter>,
|
||||||
|
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename... Others>
|
||||||
|
class enum_ : public class_<T, Others...> {
|
||||||
|
std::vector<std::pair<const char*, handle>> m_values;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Base = class_<T, Others...>;
|
||||||
|
using class_<T, Others...>::class_;
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
enum_(const handle& scope, const char* name, Args&&... args) :
|
||||||
|
class_<T, Others...>(scope, name, std::forward<Args>(args)...) {
|
||||||
|
|
||||||
|
Base::def(init([](int value) {
|
||||||
|
return static_cast<T>(value);
|
||||||
|
}));
|
||||||
|
|
||||||
|
Base::def("__eq__", [](T& self, T& other) {
|
||||||
|
return self == other;
|
||||||
|
});
|
||||||
|
|
||||||
|
Base::def_property_readonly("value", [](T& self) {
|
||||||
|
return int_(static_cast<std::underlying_type_t<T>>(self));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
enum_& value(const char* name, T value) {
|
||||||
|
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) {
|
||||||
|
setattr(Base::m_scope, name, value);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace pybind11
|
||||||
|
|
||||||
589
include/pybind11/internal/cpp_function.h
Normal file
589
include/pybind11/internal/cpp_function.h
Normal file
@ -0,0 +1,589 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "cast.h"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
|
||||||
|
// append the overload to the beginning of the overload list
|
||||||
|
struct prepend {};
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
struct constructor {};
|
||||||
|
|
||||||
|
template <typename Fn, typename Args = callable_args_t<Fn>>
|
||||||
|
struct factory;
|
||||||
|
|
||||||
|
template <typename Fn, typename... Args>
|
||||||
|
struct factory<Fn, std::tuple<Args...>> {
|
||||||
|
Fn fn;
|
||||||
|
|
||||||
|
auto make() {
|
||||||
|
using Self = callable_return_t<Fn>;
|
||||||
|
return [fn = std::move(fn)](Self* self, Args... args) {
|
||||||
|
new (self) Self(fn(args...));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
impl::constructor<Args...> init() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Fn>
|
||||||
|
impl::factory<Fn> init(Fn&& fn) {
|
||||||
|
return {std::forward<Fn>(fn)};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: support more customized tags
|
||||||
|
//
|
||||||
|
// template <std::size_t Nurse, std::size_t... Patients>
|
||||||
|
// struct keep_alive {};
|
||||||
|
//
|
||||||
|
// template <typename T>
|
||||||
|
// struct call_guard {
|
||||||
|
// static_assert(std::is_default_constructible_v<T>, "call_guard must be default constructible");
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// struct kw_only {};
|
||||||
|
//
|
||||||
|
// struct pos_only {};
|
||||||
|
|
||||||
|
class cpp_function : public function {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(function, pkpy::NativeFunc, vm->tp_native_func);
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename Fn, typename... Extras>
|
||||||
|
cpp_function(Fn&& f, const Extras&... extras) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
decltype(auto) get_userdata_as() {
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
return self()._userdata.as<T>();
|
||||||
|
#else
|
||||||
|
return self()._userdata._cast<T>();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void set_userdata(T&& value) {
|
||||||
|
self()._userdata = std::forward<T>(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pybind11
|
||||||
|
|
||||||
|
namespace pybind11::impl {
|
||||||
|
|
||||||
|
template <typename Callable,
|
||||||
|
typename Extra,
|
||||||
|
typename Args = callable_args_t<Callable>,
|
||||||
|
typename IndexSequence = std::make_index_sequence<std::tuple_size_v<Args>>>
|
||||||
|
struct template_parser;
|
||||||
|
|
||||||
|
class function_record {
|
||||||
|
private:
|
||||||
|
template <typename C, typename E, typename A, typename I>
|
||||||
|
friend struct template_parser;
|
||||||
|
|
||||||
|
struct arguments_t {
|
||||||
|
std::vector<pkpy::StrName> names;
|
||||||
|
std::vector<handle> defaults;
|
||||||
|
};
|
||||||
|
|
||||||
|
using destructor_t = void (*)(function_record*);
|
||||||
|
using wrapper_t = handle (*)(function_record&, pkpy::ArgsView, bool convert, handle parent);
|
||||||
|
|
||||||
|
static_assert(std::is_trivially_copyable_v<pkpy::StrName>);
|
||||||
|
|
||||||
|
private:
|
||||||
|
union {
|
||||||
|
void* data;
|
||||||
|
char buffer[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapper_t wrapper = nullptr;
|
||||||
|
function_record* next = nullptr;
|
||||||
|
arguments_t* arguments = nullptr;
|
||||||
|
destructor_t destructor = nullptr;
|
||||||
|
const char* signature = nullptr;
|
||||||
|
return_value_policy policy = return_value_policy::automatic;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename Fn, typename... Extras>
|
||||||
|
function_record(Fn&& f, const Extras&... extras) {
|
||||||
|
using Callable = std::decay_t<Fn>;
|
||||||
|
|
||||||
|
if constexpr(std::is_trivially_copyable_v<Callable> && sizeof(Callable) <= sizeof(buffer)) {
|
||||||
|
// if the callable object is trivially copyable and the size is less than 16 bytes, store it in the
|
||||||
|
// buffer
|
||||||
|
new (buffer) auto(std::forward<Fn>(f));
|
||||||
|
destructor = [](function_record* self) {
|
||||||
|
reinterpret_cast<Callable*>(self->buffer)->~Callable();
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// otherwise, store it in the heap
|
||||||
|
data = new auto(std::forward<Fn>(f));
|
||||||
|
destructor = [](function_record* self) {
|
||||||
|
delete static_cast<Callable*>(self->data);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
using Parser = template_parser<Callable, std::tuple<Extras...>>;
|
||||||
|
Parser::initialize(*this, extras...);
|
||||||
|
wrapper = Parser::wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
function_record(const function_record&) = delete;
|
||||||
|
function_record& operator= (const function_record&) = delete;
|
||||||
|
function_record& operator= (function_record&&) = delete;
|
||||||
|
|
||||||
|
function_record(function_record&& other) noexcept {
|
||||||
|
std::memcpy(this, &other, sizeof(function_record));
|
||||||
|
std::memset(&other, 0, sizeof(function_record));
|
||||||
|
}
|
||||||
|
|
||||||
|
~function_record() {
|
||||||
|
if(destructor) { destructor(this); }
|
||||||
|
if(arguments) { delete arguments; }
|
||||||
|
if(next) { delete next; }
|
||||||
|
if(signature) { delete[] signature; }
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(function_record* record) {
|
||||||
|
function_record* p = this;
|
||||||
|
while(p->next) {
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
p->next = record;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& _as() {
|
||||||
|
if constexpr(std::is_trivially_copyable_v<T> && sizeof(T) <= sizeof(buffer)) {
|
||||||
|
return *reinterpret_cast<T*>(buffer);
|
||||||
|
} else {
|
||||||
|
return *static_cast<T*>(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle operator() (pkpy::ArgsView view) {
|
||||||
|
function_record* p = this;
|
||||||
|
// foreach function record and call the function with not convert
|
||||||
|
while(p != nullptr) {
|
||||||
|
handle result = p->wrapper(*p, view, false, {});
|
||||||
|
if(result) { return result; }
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = this;
|
||||||
|
// foreach function record and call the function with convert
|
||||||
|
while(p != nullptr) {
|
||||||
|
handle result = p->wrapper(*p, view, true, {});
|
||||||
|
if(result) { return result; }
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string msg = "no matching function found, function signature:\n";
|
||||||
|
std::size_t index = 0;
|
||||||
|
p = this;
|
||||||
|
while(p != nullptr) {
|
||||||
|
msg += " ";
|
||||||
|
msg += p->signature;
|
||||||
|
msg += "\n";
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
vm->TypeError(msg);
|
||||||
|
PK_UNREACHABLE();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Fn, std::size_t... Is, typename... Args>
|
||||||
|
handle invoke(Fn&& fn,
|
||||||
|
std::index_sequence<Is...>,
|
||||||
|
std::tuple<impl::type_caster<Args>...>& casters,
|
||||||
|
return_value_policy policy,
|
||||||
|
handle parent) {
|
||||||
|
using underlying_type = std::decay_t<Fn>;
|
||||||
|
using return_type = callable_return_t<underlying_type>;
|
||||||
|
|
||||||
|
constexpr bool is_void = std::is_void_v<return_type>;
|
||||||
|
constexpr bool is_member_function_pointer = std::is_member_function_pointer_v<underlying_type>;
|
||||||
|
|
||||||
|
if constexpr(is_member_function_pointer) {
|
||||||
|
// helper function to unpack the arguments to call the member pointer
|
||||||
|
auto unpack = [&](class_type_t<underlying_type>& self, auto&... args) {
|
||||||
|
return (self.*fn)(args...);
|
||||||
|
};
|
||||||
|
|
||||||
|
if constexpr(!is_void) {
|
||||||
|
return pybind11::cast(unpack(std::get<Is>(casters).value...), policy, parent);
|
||||||
|
} else {
|
||||||
|
unpack(std::get<Is>(casters).value...);
|
||||||
|
return vm->None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if constexpr(!is_void) {
|
||||||
|
return pybind11::cast(fn(std::get<Is>(casters).value...), policy, parent);
|
||||||
|
} else {
|
||||||
|
fn(std::get<Is>(casters).value...);
|
||||||
|
return vm->None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct arguments_info_t {
|
||||||
|
int argc = 0;
|
||||||
|
int args_pos = -1;
|
||||||
|
int kwargs_pos = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct extras_info_t {
|
||||||
|
int doc_pos = -1;
|
||||||
|
int named_argc = 0;
|
||||||
|
int policy_pos = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Callable, typename... Extras, typename... Args, std::size_t... Is>
|
||||||
|
struct template_parser<Callable, std::tuple<Extras...>, std::tuple<Args...>, std::index_sequence<Is...>> {
|
||||||
|
constexpr static arguments_info_t parse_arguments() {
|
||||||
|
constexpr auto args_count = types_count_v<args, Args...>;
|
||||||
|
constexpr auto kwargs_count = types_count_v<kwargs, Args...>;
|
||||||
|
|
||||||
|
static_assert(args_count <= 1, "py::args can occur at most once");
|
||||||
|
static_assert(kwargs_count <= 1, "py::kwargs can occur at most once");
|
||||||
|
|
||||||
|
constexpr auto args_pos = type_index_v<args, Args...>;
|
||||||
|
constexpr auto kwargs_pos = type_index_v<kwargs, Args...>;
|
||||||
|
|
||||||
|
if constexpr(kwargs_count == 1) {
|
||||||
|
static_assert(kwargs_pos == sizeof...(Args) - 1, "py::kwargs must be the last argument");
|
||||||
|
|
||||||
|
// FIXME: temporarily, args and kwargs must be at the end of the arguments list
|
||||||
|
if constexpr(args_count == 1) {
|
||||||
|
static_assert(args_pos == kwargs_pos - 1, "py::args must be before py::kwargs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {sizeof...(Args), args_pos, kwargs_pos};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr static extras_info_t parse_extras() {
|
||||||
|
constexpr auto doc_count = types_count_v<const char*, Extras...>;
|
||||||
|
constexpr auto policy_count = types_count_v<return_value_policy, Extras...>;
|
||||||
|
|
||||||
|
static_assert(doc_count <= 1, "doc can occur at most once");
|
||||||
|
static_assert(policy_count <= 1, "return_value_policy can occur at most once");
|
||||||
|
|
||||||
|
constexpr auto doc_pos = type_index_v<const char*, Extras...>;
|
||||||
|
constexpr auto policy_pos = type_index_v<return_value_policy, Extras...>;
|
||||||
|
|
||||||
|
constexpr auto named_argc = types_count_v<arg, Extras...>;
|
||||||
|
constexpr auto normal_argc =
|
||||||
|
sizeof...(Args) - (arguments_info.args_pos != -1) - (arguments_info.kwargs_pos != -1);
|
||||||
|
|
||||||
|
static_assert(named_argc == 0 || named_argc == normal_argc,
|
||||||
|
"named arguments must be the same as the number of function arguments");
|
||||||
|
|
||||||
|
return {doc_pos, named_argc, policy_pos};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline static auto arguments_info = parse_arguments();
|
||||||
|
constexpr inline static auto extras_info = parse_extras();
|
||||||
|
|
||||||
|
static void initialize(function_record& record, const Extras&... extras) {
|
||||||
|
auto extras_tuple = std::make_tuple(extras...);
|
||||||
|
constexpr static bool has_named_args = (extras_info.named_argc > 0);
|
||||||
|
// set return value policy
|
||||||
|
if constexpr(extras_info.policy_pos != -1) { record.policy = std::get<extras_info.policy_pos>(extras_tuple); }
|
||||||
|
|
||||||
|
// TODO: set others
|
||||||
|
|
||||||
|
// set default arguments
|
||||||
|
if constexpr(has_named_args) {
|
||||||
|
record.arguments = new function_record::arguments_t();
|
||||||
|
|
||||||
|
auto add_arguments = [&](const auto& arg) {
|
||||||
|
if constexpr(std::is_same_v<pybind11::arg, remove_cvref_t<decltype(arg)>>) {
|
||||||
|
auto& arguments = *record.arguments;
|
||||||
|
arguments.names.emplace_back(arg.name);
|
||||||
|
arguments.defaults.emplace_back(arg.default_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(add_arguments(extras), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set signature
|
||||||
|
{
|
||||||
|
std::string sig = "(";
|
||||||
|
std::size_t index = 0;
|
||||||
|
auto append = [&](auto _t) {
|
||||||
|
using T = pybind11_decay_t<typename decltype(_t)::type>;
|
||||||
|
if constexpr(std::is_same_v<T, args>) {
|
||||||
|
sig += "*args";
|
||||||
|
} else if constexpr(std::is_same_v<T, kwargs>) {
|
||||||
|
sig += "**kwargs";
|
||||||
|
} else if constexpr(has_named_args) {
|
||||||
|
sig += record.arguments->names[index].c_str();
|
||||||
|
sig += ": ";
|
||||||
|
sig += type_info::of<T>().name;
|
||||||
|
if(record.arguments->defaults[index]) {
|
||||||
|
sig += " = ";
|
||||||
|
sig += record.arguments->defaults[index].repr();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sig += "_: ";
|
||||||
|
sig += type_info::of<T>().name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(index + 1 < arguments_info.argc) { sig += ", "; }
|
||||||
|
index++;
|
||||||
|
};
|
||||||
|
(append(type_identity<Args>{}), ...);
|
||||||
|
sig += ")";
|
||||||
|
char* buffer = new char[sig.size() + 1];
|
||||||
|
std::memcpy(buffer, sig.data(), sig.size());
|
||||||
|
buffer[sig.size()] = '\0';
|
||||||
|
record.signature = buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static handle wrapper(function_record& record, pkpy::ArgsView view, bool convert, handle parent) {
|
||||||
|
constexpr auto argc = arguments_info.argc;
|
||||||
|
constexpr auto named_argc = extras_info.named_argc;
|
||||||
|
constexpr auto args_pos = arguments_info.args_pos;
|
||||||
|
constexpr auto kwargs_pos = arguments_info.kwargs_pos;
|
||||||
|
constexpr auto normal_argc = argc - (args_pos != -1) - (kwargs_pos != -1);
|
||||||
|
|
||||||
|
// avoid gc call in bound function
|
||||||
|
vm->heap.gc_scope_lock();
|
||||||
|
|
||||||
|
// add 1 to avoid zero-size array when argc is 0
|
||||||
|
handle stack[argc + 1] = {};
|
||||||
|
|
||||||
|
// ensure the number of passed arguments is no greater than the number of parameters
|
||||||
|
if(args_pos == -1 && view.size() > normal_argc) { return handle(); }
|
||||||
|
|
||||||
|
// if have default arguments, load them
|
||||||
|
if constexpr(named_argc > 0) {
|
||||||
|
auto& defaults = record.arguments->defaults;
|
||||||
|
std::memcpy(stack, defaults.data(), defaults.size() * sizeof(handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
// load arguments from call arguments
|
||||||
|
const auto size = std::min(view.size(), normal_argc);
|
||||||
|
std::memcpy(stack, view.begin(), size * sizeof(handle));
|
||||||
|
|
||||||
|
// pack the args
|
||||||
|
if constexpr(args_pos != -1) {
|
||||||
|
const auto n = std::max(view.size() - normal_argc, 0);
|
||||||
|
tuple args = tuple(n);
|
||||||
|
for(std::size_t i = 0; i < n; ++i) {
|
||||||
|
args[i] = view[normal_argc + i];
|
||||||
|
}
|
||||||
|
stack[args_pos] = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve keyword arguments
|
||||||
|
const auto n = vm->s_data._sp - view.end();
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
if constexpr(named_argc > 0) {
|
||||||
|
int arg_index = 0;
|
||||||
|
auto& arguments = *record.arguments;
|
||||||
|
|
||||||
|
while(arg_index < named_argc && index < n) {
|
||||||
|
const auto key = pkpy::_py_cast<pkpy::i64>(vm, view.end()[index]);
|
||||||
|
const auto value = view.end()[index + 1];
|
||||||
|
const auto name = pkpy::StrName(key);
|
||||||
|
auto& arg_name = record.arguments->names[arg_index];
|
||||||
|
|
||||||
|
if(name == arg_name) {
|
||||||
|
stack[arg_index] = value;
|
||||||
|
index += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack the kwargs
|
||||||
|
if constexpr(kwargs_pos != -1) {
|
||||||
|
dict kwargs;
|
||||||
|
while(index < n) {
|
||||||
|
const auto key = pkpy::_py_cast<pkpy::i64>(vm, view.end()[index]);
|
||||||
|
const str name = str(pkpy::StrName(key).sv());
|
||||||
|
kwargs[name] = view.end()[index + 1];
|
||||||
|
index += 2;
|
||||||
|
}
|
||||||
|
stack[kwargs_pos] = kwargs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if have rest keyword arguments, call fails
|
||||||
|
if(index != n) { return handle(); }
|
||||||
|
|
||||||
|
// check if all the arguments are valid
|
||||||
|
for(std::size_t i = 0; i < argc; ++i) {
|
||||||
|
if(!stack[i]) { return handle(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ok, all the arguments are valid, call the function
|
||||||
|
std::tuple<impl::type_caster<Args>...> casters;
|
||||||
|
|
||||||
|
// check type compatibility
|
||||||
|
if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
|
||||||
|
return invoke(record._as<Callable>(), std::index_sequence<Is...>{}, casters, record.policy, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline auto _wrapper(pkpy::VM* vm, pkpy::ArgsView view) {
|
||||||
|
auto&& record = unpack<function_record>(view);
|
||||||
|
return record(view).ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool is_method, typename Fn, typename... Extras>
|
||||||
|
handle bind_function(const handle& obj, const char* name, Fn&& fn, pkpy::BindType type, const Extras&... extras) {
|
||||||
|
// do not use cpp_function directly to avoid unnecessary reference count change
|
||||||
|
pkpy::PyVar var = obj.ptr();
|
||||||
|
cpp_function callable = var->attr().try_get(name);
|
||||||
|
function_record* record = nullptr;
|
||||||
|
|
||||||
|
if constexpr(is_method && types_count_v<arg, Extras...> > 0) {
|
||||||
|
// if the function is a method and has named arguments
|
||||||
|
// prepend self to the arguments list
|
||||||
|
record = new function_record(std::forward<Fn>(fn), arg("self"), extras...);
|
||||||
|
} else {
|
||||||
|
record = new function_record(std::forward<Fn>(fn), extras...);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!callable) {
|
||||||
|
// if the function is not bound yet, bind it
|
||||||
|
void* data = interpreter::take_ownership(std::move(*record));
|
||||||
|
callable = interpreter::bind_func(var, name, -1, _wrapper, data, type);
|
||||||
|
} else {
|
||||||
|
// if the function is already bound, append the new record to the function
|
||||||
|
function_record* last = callable.get_userdata_as<function_record*>();
|
||||||
|
|
||||||
|
if constexpr((types_count_v<prepend, Extras...> != 0)) {
|
||||||
|
// if prepend is specified, append the new record to the beginning of the list
|
||||||
|
callable.set_userdata(record);
|
||||||
|
record->append(last);
|
||||||
|
} else {
|
||||||
|
// otherwise, append the new record to the end of the list
|
||||||
|
last->append(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return callable;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pybind11::impl
|
||||||
|
|
||||||
|
namespace pybind11::impl {
|
||||||
|
|
||||||
|
template <typename Getter>
|
||||||
|
pkpy::PyVar getter_wrapper(pkpy::VM* vm, pkpy::ArgsView view) {
|
||||||
|
handle result = vm->None;
|
||||||
|
auto&& getter = unpack<Getter>(view);
|
||||||
|
constexpr auto policy = return_value_policy::reference_internal;
|
||||||
|
|
||||||
|
if constexpr(std::is_member_pointer_v<Getter>) {
|
||||||
|
using Self = class_type_t<Getter>;
|
||||||
|
auto& self = handle(view[0])._as<instance>()._as<Self>();
|
||||||
|
|
||||||
|
if constexpr(std::is_member_object_pointer_v<Getter>) {
|
||||||
|
// specialize for pointer to data member
|
||||||
|
result = cast(self.*getter, policy, view[0]);
|
||||||
|
} else {
|
||||||
|
// specialize for pointer to member function
|
||||||
|
result = cast((self.*getter)(), policy, view[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// specialize for function pointer and lambda
|
||||||
|
using Self = remove_cvref_t<std::tuple_element_t<0, callable_args_t<Getter>>>;
|
||||||
|
auto& self = handle(view[0])._as<instance>()._as<Self>();
|
||||||
|
|
||||||
|
result = cast(getter(self), policy, view[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Setter>
|
||||||
|
pkpy::PyVar setter_wrapper(pkpy::VM* vm, pkpy::ArgsView view) {
|
||||||
|
auto&& setter = unpack<Setter>(view);
|
||||||
|
|
||||||
|
if constexpr(std::is_member_pointer_v<Setter>) {
|
||||||
|
using Self = class_type_t<Setter>;
|
||||||
|
auto& self = handle(view[0])._as<instance>()._as<Self>();
|
||||||
|
|
||||||
|
if constexpr(std::is_member_object_pointer_v<Setter>) {
|
||||||
|
// specialize for pointer to data member
|
||||||
|
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
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// specialize for function pointer and lambda
|
||||||
|
using Self = remove_cvref_t<std::tuple_element_t<0, callable_args_t<Setter>>>;
|
||||||
|
auto& self = handle(view[0])._as<instance>()._as<Self>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vm->TypeError("Unexpected argument type");
|
||||||
|
PK_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Getter, typename Setter, typename... Extras>
|
||||||
|
handle bind_property(const handle& obj, const char* name, Getter&& getter_, Setter&& setter_, const Extras&... extras) {
|
||||||
|
handle getter = none();
|
||||||
|
handle setter = none();
|
||||||
|
using Wrapper = pkpy::PyVar (*)(pkpy::VM*, pkpy::ArgsView);
|
||||||
|
|
||||||
|
constexpr auto create = [](Wrapper wrapper, int argc, auto&& f) {
|
||||||
|
if constexpr(need_host<remove_cvref_t<decltype(f)>>) {
|
||||||
|
// otherwise, store it in the type_info
|
||||||
|
void* data = interpreter::take_ownership(std::forward<decltype(f)>(f));
|
||||||
|
// store the index in the object
|
||||||
|
return vm->heap.gcnew<pkpy::NativeFunc>(vm->tp_native_func, wrapper, argc, data);
|
||||||
|
} else {
|
||||||
|
// if the function is trivially copyable and the size is less than 16 bytes, store it in the object
|
||||||
|
// directly
|
||||||
|
return vm->heap.gcnew<pkpy::NativeFunc>(vm->tp_native_func, wrapper, argc, f);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getter = create(impl::getter_wrapper<std::decay_t<Getter>>, 1, std::forward<Getter>(getter_));
|
||||||
|
|
||||||
|
if constexpr(!std::is_same_v<Setter, std::nullptr_t>) {
|
||||||
|
setter = create(impl::setter_wrapper<std::decay_t<Setter>>, 2, std::forward<Setter>(setter_));
|
||||||
|
}
|
||||||
|
|
||||||
|
handle property = pybind11::property(getter, setter);
|
||||||
|
setattr(obj, name, property);
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pybind11::impl
|
||||||
142
include/pybind11/internal/instance.h
Normal file
142
include/pybind11/internal/instance.h
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "kernel.h"
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
|
||||||
|
struct type_info {
|
||||||
|
std::string_view name;
|
||||||
|
std::size_t size;
|
||||||
|
std::size_t alignment;
|
||||||
|
void (*destructor)(void*);
|
||||||
|
const std::type_info* type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static type_info& of() {
|
||||||
|
static_assert(!std::is_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
|
||||||
|
"T must not be a reference type or const type.");
|
||||||
|
static type_info info = {
|
||||||
|
type_name<T>(),
|
||||||
|
sizeof(T),
|
||||||
|
alignof(T),
|
||||||
|
[](void* ptr) {
|
||||||
|
((T*)ptr)->~T();
|
||||||
|
operator delete (ptr);
|
||||||
|
},
|
||||||
|
&typeid(T),
|
||||||
|
};
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// all registered C++ class will be ensured as instance type.
|
||||||
|
class instance {
|
||||||
|
public:
|
||||||
|
// use to record the type information of C++ class.
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum Flag {
|
||||||
|
None = 0,
|
||||||
|
Own = 1 << 0, // if the instance is owned by C++ side.
|
||||||
|
Ref = 1 << 1, // need to mark the parent object.
|
||||||
|
};
|
||||||
|
|
||||||
|
Flag flag;
|
||||||
|
void* data;
|
||||||
|
const type_info* type;
|
||||||
|
pkpy::PyVar parent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
instance() noexcept : flag(Flag::None), data(nullptr), type(nullptr), parent(nullptr) {}
|
||||||
|
|
||||||
|
instance(const instance&) = delete;
|
||||||
|
|
||||||
|
instance(instance&& other) noexcept : flag(other.flag), data(other.data), type(other.type), parent(other.parent) {
|
||||||
|
other.flag = Flag::None;
|
||||||
|
other.data = nullptr;
|
||||||
|
other.type = nullptr;
|
||||||
|
other.parent = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pkpy::PyVar create(pkpy::Type type, const type_info* info) noexcept {
|
||||||
|
instance instance;
|
||||||
|
instance.type = info;
|
||||||
|
instance.data = operator new (info->size);
|
||||||
|
instance.flag = Flag::Own;
|
||||||
|
return vm->heap.gcnew<pybind11::instance>(type, std::move(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static pkpy::PyVar create(T&& value,
|
||||||
|
pkpy::Type type,
|
||||||
|
return_value_policy policy = return_value_policy::automatic_reference,
|
||||||
|
pkpy::PyVar parent = nullptr) noexcept {
|
||||||
|
using underlying_type = remove_cvref_t<T>;
|
||||||
|
|
||||||
|
auto& _value = [&]() -> auto& {
|
||||||
|
// note that, pybind11 will ignore the const qualifier.
|
||||||
|
// in fact, try to modify a const value will result in undefined behavior.
|
||||||
|
if constexpr(std::is_pointer_v<underlying_type>) {
|
||||||
|
return *reinterpret_cast<underlying_type*>(value);
|
||||||
|
} else {
|
||||||
|
return const_cast<underlying_type&>(value);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
using primary = std::remove_pointer_t<underlying_type>;
|
||||||
|
instance instance;
|
||||||
|
instance.type = &type_info::of<primary>();
|
||||||
|
|
||||||
|
if(policy == return_value_policy::take_ownership) {
|
||||||
|
instance.data = &_value;
|
||||||
|
instance.flag = Flag::Own;
|
||||||
|
} else if(policy == return_value_policy::copy) {
|
||||||
|
if constexpr(std::is_copy_constructible_v<primary>) {
|
||||||
|
instance.data = ::new auto(_value);
|
||||||
|
instance.flag = Flag::Own;
|
||||||
|
} else {
|
||||||
|
std::string msg = "cannot use copy policy on non-copyable type: ";
|
||||||
|
msg += type_name<primary>();
|
||||||
|
vm->RuntimeError(msg);
|
||||||
|
}
|
||||||
|
} else if(policy == return_value_policy::move) {
|
||||||
|
if constexpr(std::is_move_constructible_v<primary>) {
|
||||||
|
instance.data = ::new auto(std::move(_value));
|
||||||
|
instance.flag = Flag::Own;
|
||||||
|
} else {
|
||||||
|
std::string msg = "cannot use move policy on non-moveable type: ";
|
||||||
|
msg += type_name<primary>();
|
||||||
|
vm->RuntimeError(msg);
|
||||||
|
}
|
||||||
|
} else if(policy == return_value_policy::reference) {
|
||||||
|
instance.data = &_value;
|
||||||
|
instance.flag = Flag::None;
|
||||||
|
} else if(policy == return_value_policy::reference_internal) {
|
||||||
|
instance.data = &_value;
|
||||||
|
instance.flag = Flag::Ref;
|
||||||
|
instance.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm->heap.gcnew<pybind11::instance>(type, std::move(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
~instance() {
|
||||||
|
if(flag & Flag::Own) { type->destructor(data); }
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& _as() noexcept {
|
||||||
|
return *static_cast<T*>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
void _gc_mark(pkpy::VM* vm) const noexcept {
|
||||||
|
if(parent && (flag & Flag::Ref)) { PK_OBJ_MARK(parent); }
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void _gc_mark() const noexcept {
|
||||||
|
if(parent && (flag & Flag::Ref)) { PK_OBJ_MARK(parent); }
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pybind11
|
||||||
219
include/pybind11/internal/kernel.h
Normal file
219
include/pybind11/internal/kernel.h
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
|
#include <pocketpy.h>
|
||||||
|
|
||||||
|
#include "type_traits.h"
|
||||||
|
|
||||||
|
namespace pybind11::impl {
|
||||||
|
struct capsule {
|
||||||
|
void* ptr;
|
||||||
|
void (*destructor)(void*);
|
||||||
|
|
||||||
|
template <typename T, typename = std::enable_if_t<!(std::is_same_v<remove_cvref_t<T>, capsule>)>>
|
||||||
|
capsule(T&& value) :
|
||||||
|
ptr(new auto(std::forward<T>(value))), destructor([](void* ptr) {
|
||||||
|
delete static_cast<std::decay_t<T>*>(ptr);
|
||||||
|
}) {}
|
||||||
|
|
||||||
|
capsule(void* ptr, void (*destructor)(void*)) : ptr(ptr), destructor(destructor) {}
|
||||||
|
|
||||||
|
capsule(const capsule&) = delete;
|
||||||
|
|
||||||
|
capsule(capsule&& other) noexcept : ptr(other.ptr), destructor(other.destructor) {
|
||||||
|
other.ptr = nullptr;
|
||||||
|
other.destructor = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
~capsule() {
|
||||||
|
if(ptr && destructor) destructor(ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace pybind11::impl
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
|
||||||
|
class handle;
|
||||||
|
class object;
|
||||||
|
class iterator;
|
||||||
|
class str;
|
||||||
|
struct arg;
|
||||||
|
struct args_proxy;
|
||||||
|
struct kwargs_proxy;
|
||||||
|
|
||||||
|
inline pkpy::VM* vm = nullptr;
|
||||||
|
|
||||||
|
class interpreter {
|
||||||
|
inline static std::vector<impl::capsule>* _capsules = nullptr;
|
||||||
|
inline static std::vector<void (*)()>* _init = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline static void initialize(bool enable_os = true) {
|
||||||
|
if(vm == nullptr) {
|
||||||
|
vm = new pkpy::VM();
|
||||||
|
if(_init != nullptr) {
|
||||||
|
for(auto& fn: *_init)
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void finalize() {
|
||||||
|
if(_capsules != nullptr) {
|
||||||
|
delete _capsules;
|
||||||
|
_capsules = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(vm != nullptr) {
|
||||||
|
delete vm;
|
||||||
|
vm = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline static void* take_ownership(T&& value) {
|
||||||
|
if(_capsules == nullptr) _capsules = new std::vector<impl::capsule>();
|
||||||
|
_capsules->emplace_back(std::forward<T>(value));
|
||||||
|
return _capsules->back().ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void register_init(void (*init)()) {
|
||||||
|
if(_init == nullptr) _init = new std::vector<void (*)()>();
|
||||||
|
_init->push_back(init);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static pkpy::PyVar bind_func(pkpy::PyVar scope,
|
||||||
|
pkpy::StrName name,
|
||||||
|
int argc,
|
||||||
|
pkpy::NativeFuncC fn,
|
||||||
|
pkpy::any any = {},
|
||||||
|
pkpy::BindType type = pkpy::BindType::DEFAULT) {
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
return vm->bind_func(scope.get(), name, argc, fn, any, type);
|
||||||
|
#else
|
||||||
|
return vm->bind_func(scope, name, argc, fn, std::move(any), type);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline static handle vectorcall(const handle& self, const handle& func, const Args&... args);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline bool need_host = !(std::is_trivially_copyable_v<T> && (sizeof(T) <= 8));
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
decltype(auto) unpack(pkpy::ArgsView view) {
|
||||||
|
if constexpr(need_host<T>) {
|
||||||
|
void* data = pkpy::lambda_get_userdata<void*>(view.begin());
|
||||||
|
return *static_cast<T*>(data);
|
||||||
|
} else {
|
||||||
|
return pkpy::lambda_get_userdata<T>(view.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename policy>
|
||||||
|
class accessor;
|
||||||
|
|
||||||
|
namespace policy {
|
||||||
|
struct attr;
|
||||||
|
struct item;
|
||||||
|
struct tuple;
|
||||||
|
struct list;
|
||||||
|
struct dict;
|
||||||
|
} // namespace policy
|
||||||
|
|
||||||
|
using attr_accessor = accessor<policy::attr>;
|
||||||
|
using item_accessor = accessor<policy::item>;
|
||||||
|
using tuple_accessor = accessor<policy::tuple>;
|
||||||
|
using list_accessor = accessor<policy::list>;
|
||||||
|
using dict_accessor = accessor<policy::dict>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T cast(const handle& obj, bool convert = false);
|
||||||
|
|
||||||
|
enum class return_value_policy : uint8_t {
|
||||||
|
/**
|
||||||
|
* This is the default return value policy, which falls back to the policy
|
||||||
|
* return_value_policy::take_ownership when the return value is a pointer.
|
||||||
|
* Otherwise, it uses return_value::move or return_value::copy for rvalue
|
||||||
|
* and lvalue references, respectively. See below for a description of what
|
||||||
|
* all of these different policies do.
|
||||||
|
*/
|
||||||
|
automatic = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As above, but use policy return_value_policy::reference when the return
|
||||||
|
* value is a pointer. This is the default conversion policy for function
|
||||||
|
* arguments when calling Python functions manually from C++ code (i.e. via
|
||||||
|
* handle::operator()). You probably won't need to use this.
|
||||||
|
*/
|
||||||
|
automatic_reference,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference an existing object (i.e. do not create a new copy) and take
|
||||||
|
* ownership. Python will call the destructor and delete operator when the
|
||||||
|
* object's reference count reaches zero. Undefined behavior ensues when
|
||||||
|
* the C++ side does the same..
|
||||||
|
*/
|
||||||
|
take_ownership,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new copy of the returned object, which will be owned by
|
||||||
|
* Python. This policy is comparably safe because the lifetimes of the two
|
||||||
|
* instances are decoupled.
|
||||||
|
*/
|
||||||
|
copy,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use std::move to move the return value contents into a new instance
|
||||||
|
* that will be owned by Python. This policy is comparably safe because the
|
||||||
|
* lifetimes of the two instances (move source and destination) are
|
||||||
|
* decoupled.
|
||||||
|
*/
|
||||||
|
move,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference an existing object, but do not take ownership. The C++ side
|
||||||
|
* is responsible for managing the object's lifetime and deallocating it
|
||||||
|
* when it is no longer used. Warning: undefined behavior will ensue when
|
||||||
|
* the C++ side deletes an object that is still referenced and used by
|
||||||
|
* Python.
|
||||||
|
*/
|
||||||
|
reference,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This policy only applies to methods and properties. It references the
|
||||||
|
* object without taking ownership similar to the above
|
||||||
|
* return_value_policy::reference policy. In contrast to that policy, the
|
||||||
|
* function or property's implicit this argument (called the parent) is
|
||||||
|
* considered to be the the owner of the return value (the child).
|
||||||
|
* pybind11 then couples the lifetime of the parent to the child via a
|
||||||
|
* reference relationship that ensures that the parent cannot be garbage
|
||||||
|
* collected while Python is still using the child. More advanced
|
||||||
|
* variations of this scheme are also possible using combinations of
|
||||||
|
* return_value_policy::reference and the keep_alive call policy
|
||||||
|
*/
|
||||||
|
reference_internal
|
||||||
|
};
|
||||||
|
|
||||||
|
struct empty {};
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
void print(Args&&... args);
|
||||||
|
|
||||||
|
class object;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
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
|
||||||
|
using error_already_set = pkpy::Exception;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline void setattr(const handle& obj, const handle& name, const handle& value);
|
||||||
|
inline void setattr(const handle& obj, const char* name, const handle& value);
|
||||||
|
|
||||||
|
} // namespace pybind11
|
||||||
50
include/pybind11/internal/module.h
Normal file
50
include/pybind11/internal/module.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "cpp_function.h"
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
|
||||||
|
class module_ : public object {
|
||||||
|
|
||||||
|
public:
|
||||||
|
using object::object;
|
||||||
|
|
||||||
|
static module_ __main__() { return vm->_main; }
|
||||||
|
|
||||||
|
static module_ import(const char* name) {
|
||||||
|
if(name == std::string_view{"__main__"}) {
|
||||||
|
return vm->_main;
|
||||||
|
} else {
|
||||||
|
return vm->py_import(name, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module_ def_submodule(const char* name, const char* doc = nullptr) {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Fn, typename... Extras>
|
||||||
|
module_& def(const char* name, Fn&& fn, const Extras... extras) {
|
||||||
|
impl::bind_function<false>(*this, name, std::forward<Fn>(fn), pkpy::BindType::DEFAULT, extras...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
|
||||||
|
static void _pybind11_register_##name(pybind11::module_& variable); \
|
||||||
|
namespace pybind11::impl { \
|
||||||
|
auto _module_##name = [] { \
|
||||||
|
interpreter::register_init([] { \
|
||||||
|
pybind11::module_ m = vm->new_module(#name, ""); \
|
||||||
|
_pybind11_register_##name(m); \
|
||||||
|
}); \
|
||||||
|
return 1; \
|
||||||
|
}(); \
|
||||||
|
} \
|
||||||
|
static void _pybind11_register_##name(pybind11::module_& variable)
|
||||||
|
|
||||||
|
} // namespace pybind11
|
||||||
272
include/pybind11/internal/object.h
Normal file
272
include/pybind11/internal/object.h
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "instance.h"
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
class interface {
|
||||||
|
private:
|
||||||
|
pkpy::PyVar ptr() const { return static_cast<const Derived*>(this)->ptr(); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool is_none() const {
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
return ptr().operator== (vm->None.get());
|
||||||
|
#else
|
||||||
|
return ptr() == vm->None;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is(const interface& other) const {
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
return ptr().operator== (other.ptr().get());
|
||||||
|
#else
|
||||||
|
return ptr() == other.ptr();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in(const interface& other) const {
|
||||||
|
return pybind11::cast<bool>(vm->call(vm->py_op("contains"), other.ptr(), ptr()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool contains(const interface& other) const {
|
||||||
|
return pybind11::cast<bool>(vm->call(vm->py_op("contains"), ptr(), other.ptr()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
attr_accessor attr(pkpy::StrName key) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
iterator begin() const;
|
||||||
|
iterator end() const;
|
||||||
|
|
||||||
|
attr_accessor attr(const char* key) const;
|
||||||
|
attr_accessor attr(const handle& key) const;
|
||||||
|
attr_accessor doc() const;
|
||||||
|
|
||||||
|
item_accessor operator[] (int index) const;
|
||||||
|
item_accessor operator[] (const char* key) const;
|
||||||
|
item_accessor operator[] (const handle& key) const;
|
||||||
|
|
||||||
|
args_proxy operator* () const;
|
||||||
|
object operator- () const;
|
||||||
|
object operator~() const;
|
||||||
|
|
||||||
|
template <return_value_policy policy = return_value_policy::automatic, typename... Args>
|
||||||
|
object operator() (Args&&... args) const;
|
||||||
|
|
||||||
|
str package() const;
|
||||||
|
str name() const;
|
||||||
|
str repr() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
T cast() const {
|
||||||
|
return pybind11::cast<T>(ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a internal function, use to interact with pocketpy python
|
||||||
|
template <typename T>
|
||||||
|
decltype(auto) _as() const {
|
||||||
|
static_assert(!std::is_reference_v<T>, "T must not be a reference type.");
|
||||||
|
if constexpr(std::is_same_v<T, empty>) {
|
||||||
|
return empty();
|
||||||
|
} else {
|
||||||
|
#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);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// a lightweight wrapper for python objects
|
||||||
|
class handle : public interface<handle> {
|
||||||
|
protected:
|
||||||
|
mutable pkpy::PyVar m_ptr = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
handle() = default;
|
||||||
|
handle(const handle& h) = default;
|
||||||
|
handle& operator= (const handle& other) = default;
|
||||||
|
|
||||||
|
handle(std::nullptr_t) = delete;
|
||||||
|
|
||||||
|
handle(pkpy::PyVar ptr) : m_ptr(ptr) {}
|
||||||
|
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
handle(pkpy::PyObject* ptr) : m_ptr(ptr) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pkpy::PyVar ptr() const { return m_ptr; }
|
||||||
|
|
||||||
|
explicit operator bool () const { return m_ptr != nullptr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// a helper class to visit type
|
||||||
|
struct type_visitor {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr static bool is_type = std::is_same_v<pkpy::Type, std::decay_t<decltype(T::type_or_check())>>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static pkpy::Type type() {
|
||||||
|
if constexpr(is_pyobject_v<T>) {
|
||||||
|
if constexpr(is_type<T>) {
|
||||||
|
// for some type, they have according type in python, e.g. bool, int, float
|
||||||
|
// so just return the according type
|
||||||
|
return T::type_or_check();
|
||||||
|
} else {
|
||||||
|
// for other type, they don't have according type in python, like iterable, iterator
|
||||||
|
static_assert(dependent_false<T>, "type_or_check not defined");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
// for C++ type, lookup the type in the type map
|
||||||
|
auto type = vm->_cxx_typeid_map.try_get(typeid(T));
|
||||||
|
// if found, return the type
|
||||||
|
if(type) return *type;
|
||||||
|
#else
|
||||||
|
auto result = vm->_cxx_typeid_map.find(typeid(T));
|
||||||
|
if(result != vm->_cxx_typeid_map.end()) { return result->second; }
|
||||||
|
#endif
|
||||||
|
// if not found, raise error
|
||||||
|
std::string msg = "can not c++ instance cast to object, type: {";
|
||||||
|
msg += type_name<T>();
|
||||||
|
msg += "} is not registered.";
|
||||||
|
vm->TypeError(msg);
|
||||||
|
PK_UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Base = void>
|
||||||
|
static handle create(const handle& scope, const char* name, bool is_builtin = false) {
|
||||||
|
pkpy::Type type = vm->tp_object;
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
pkpy::PyTypeInfo::Vt vt = pkpy::PyTypeInfo::Vt::get<instance>();
|
||||||
|
|
||||||
|
if(is_builtin) { vt = pkpy::PyTypeInfo::Vt::get<T>(); }
|
||||||
|
|
||||||
|
if constexpr(!std::is_same_v<Base, void>) {
|
||||||
|
type = type_visitor::type<Base>();
|
||||||
|
vt = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
handle result = vm->new_type_object(scope.ptr().get(), name, type, true, vt);
|
||||||
|
if(!is_builtin) { vm->_cxx_typeid_map.insert(typeid(T), result._as<pkpy::Type>()); }
|
||||||
|
#else
|
||||||
|
if constexpr(!std::is_same_v<Base, void>) { type = type_visitor::type<Base>(); }
|
||||||
|
handle result = vm->new_type_object(scope.ptr(), name, type, true);
|
||||||
|
if(!is_builtin) (vm->_cxx_typeid_map.try_emplace(typeid(T), result._as<pkpy::Type>()));
|
||||||
|
#endif
|
||||||
|
// set __module__
|
||||||
|
setattr(scope, name, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static bool check(const handle& obj) {
|
||||||
|
if constexpr(is_pyobject_v<T>) {
|
||||||
|
if constexpr(is_type<T>) {
|
||||||
|
return vm->isinstance(obj.ptr(), T::type_or_check());
|
||||||
|
} else {
|
||||||
|
// some type, like iterable, iterator, they don't have according type in python
|
||||||
|
// but they have a function to check the type, then just call the function
|
||||||
|
return T::type_or_check(obj);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return vm->isinstance(obj.ptr(), type<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// undef in pybind11.h
|
||||||
|
#define PYBIND11_TYPE_IMPLEMENT(parent, name, tp) \
|
||||||
|
\
|
||||||
|
private: \
|
||||||
|
using underlying_type = name; \
|
||||||
|
\
|
||||||
|
inline static auto type_or_check = [] { \
|
||||||
|
return tp; \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
decltype(auto) self() const { return _as<underlying_type>(); } \
|
||||||
|
\
|
||||||
|
template <typename... Args> \
|
||||||
|
static handle create(Args&&... args) { \
|
||||||
|
if constexpr(pkpy::is_sso_v<underlying_type>) { \
|
||||||
|
return pkpy::py_var(vm, std::forward<Args>(args)...); \
|
||||||
|
} else { \
|
||||||
|
return vm->heap.gcnew<underlying_type>(type_or_check(), std::forward<Args>(args)...); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
friend type_visitor; \
|
||||||
|
using parent::parent;
|
||||||
|
|
||||||
|
/*=============================================================================//
|
||||||
|
// pkpy does not use reference counts, so object is just fot API compatibility //
|
||||||
|
//=============================================================================*/
|
||||||
|
class object : public handle {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(handle, empty, vm->tp_object);
|
||||||
|
|
||||||
|
public:
|
||||||
|
object(const handle& h) : handle(h) {
|
||||||
|
// object is must not null ptr
|
||||||
|
assert(h.ptr() != nullptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// undef after usage
|
||||||
|
#define PYBIND11_BINARY_OPERATOR(OP, NAME) \
|
||||||
|
inline object operator OP (const handle& lhs, const handle& rhs) { return handle(vm->py_op(NAME))(lhs, rhs); }
|
||||||
|
|
||||||
|
#define PYBIND11_INPLACE_OPERATOR(OP, NAME) \
|
||||||
|
inline object operator OP (handle& lhs, const handle& rhs) { \
|
||||||
|
handle result = handle(vm->py_op(NAME))(lhs, rhs); \
|
||||||
|
return lhs = result; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PYBIND11_BINARY_LOGIC_OPERATOR(OP, NAME) \
|
||||||
|
inline bool operator OP (const handle& lhs, const handle& rhs) { \
|
||||||
|
return pybind11::cast<bool>(handle(vm->py_op(NAME))(lhs, rhs)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_BINARY_OPERATOR(+, "add");
|
||||||
|
PYBIND11_BINARY_OPERATOR(-, "sub");
|
||||||
|
PYBIND11_BINARY_OPERATOR(*, "mul");
|
||||||
|
PYBIND11_BINARY_OPERATOR(/, "truediv");
|
||||||
|
PYBIND11_BINARY_OPERATOR(%, "mod");
|
||||||
|
PYBIND11_BINARY_OPERATOR(|, "or_");
|
||||||
|
PYBIND11_BINARY_OPERATOR(&, "and_");
|
||||||
|
PYBIND11_BINARY_OPERATOR(^, "xor");
|
||||||
|
PYBIND11_BINARY_OPERATOR(<<, "lshift");
|
||||||
|
PYBIND11_BINARY_OPERATOR(>>, "rshift");
|
||||||
|
|
||||||
|
PYBIND11_INPLACE_OPERATOR(+=, "iadd");
|
||||||
|
PYBIND11_INPLACE_OPERATOR(-=, "isub");
|
||||||
|
PYBIND11_INPLACE_OPERATOR(*=, "imul");
|
||||||
|
PYBIND11_INPLACE_OPERATOR(/=, "itruediv");
|
||||||
|
PYBIND11_INPLACE_OPERATOR(%=, "imod");
|
||||||
|
PYBIND11_INPLACE_OPERATOR(|=, "ior");
|
||||||
|
PYBIND11_INPLACE_OPERATOR(&=, "iand");
|
||||||
|
PYBIND11_INPLACE_OPERATOR(^=, "ixor");
|
||||||
|
PYBIND11_INPLACE_OPERATOR(<<=, "ilshift");
|
||||||
|
PYBIND11_INPLACE_OPERATOR(>>=, "irshift");
|
||||||
|
|
||||||
|
PYBIND11_BINARY_LOGIC_OPERATOR(==, "eq");
|
||||||
|
PYBIND11_BINARY_LOGIC_OPERATOR(!=, "ne");
|
||||||
|
PYBIND11_BINARY_LOGIC_OPERATOR(<, "lt");
|
||||||
|
PYBIND11_BINARY_LOGIC_OPERATOR(>, "gt");
|
||||||
|
PYBIND11_BINARY_LOGIC_OPERATOR(<=, "le");
|
||||||
|
PYBIND11_BINARY_LOGIC_OPERATOR(>=, "ge");
|
||||||
|
|
||||||
|
#undef PYBIND11_BINARY_OPERATOR
|
||||||
|
#undef PYBIND11_INPLACE_OPERATOR
|
||||||
|
#undef PYBIND11_BINARY_LOGIC_OPERATOR
|
||||||
|
}; // namespace pybind11
|
||||||
198
include/pybind11/internal/type_traits.h
Normal file
198
include/pybind11/internal/type_traits.h
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <tuple>
|
||||||
|
#include <string_view>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool dependent_false = false;
|
||||||
|
|
||||||
|
template <typename T, typename Tuple>
|
||||||
|
struct tuple_push_front;
|
||||||
|
|
||||||
|
template <typename T, typename... Ts>
|
||||||
|
struct tuple_push_front<T, std::tuple<Ts...>> {
|
||||||
|
using type = std::tuple<T, Ts...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Tuple>
|
||||||
|
using tuple_push_front_t = typename tuple_push_front<T, Tuple>::type;
|
||||||
|
|
||||||
|
// traits for function types
|
||||||
|
template <typename Fn>
|
||||||
|
struct function_traits {
|
||||||
|
static_assert(dependent_false<Fn>, "unsupported function type");
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(...) \
|
||||||
|
template <typename R, typename... Args> \
|
||||||
|
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); \
|
||||||
|
};
|
||||||
|
|
||||||
|
PYBIND11_FUNCTION_TRAITS_SPECIALIZE()
|
||||||
|
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(&)
|
||||||
|
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const)
|
||||||
|
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const&)
|
||||||
|
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(noexcept)
|
||||||
|
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(& noexcept)
|
||||||
|
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const noexcept)
|
||||||
|
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const& noexcept)
|
||||||
|
|
||||||
|
#undef PYBIND11_FUNCTION_TRAITS_SPECIALIZE
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using function_return_t = typename function_traits<T>::return_type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using function_args_t = typename function_traits<T>::args_type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr std::size_t function_args_count = function_traits<T>::args_count;
|
||||||
|
|
||||||
|
// traits for member pointers
|
||||||
|
template <typename T>
|
||||||
|
struct member_traits;
|
||||||
|
|
||||||
|
template <typename M, typename C>
|
||||||
|
struct member_traits<M C::*> {
|
||||||
|
using member_type = M;
|
||||||
|
using class_type = C;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using member_type_t = typename member_traits<T>::member_type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using class_type_t = typename member_traits<T>::class_type;
|
||||||
|
|
||||||
|
// some traits for distinguishing between function pointers, member function pointers and
|
||||||
|
// functors
|
||||||
|
using std::is_member_function_pointer_v;
|
||||||
|
using std::is_member_object_pointer_v;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline bool is_function_pointer_v = std::is_function_v<std::remove_pointer_t<T>>;
|
||||||
|
|
||||||
|
template <typename T, typename U = void>
|
||||||
|
constexpr bool is_functor_v = false;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline bool is_functor_v<T, std::void_t<decltype(&T::operator())>> = true;
|
||||||
|
|
||||||
|
template <typename T, typename SFINAE = void>
|
||||||
|
struct callable_traits;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct callable_traits<T, std::enable_if_t<is_member_function_pointer_v<T>>> {
|
||||||
|
using args_type = tuple_push_front_t<class_type_t<T>&, function_args_t<member_type_t<T>>>;
|
||||||
|
using return_type = function_return_t<member_type_t<T>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct callable_traits<T, std::enable_if_t<is_function_pointer_v<T>>> {
|
||||||
|
using args_type = function_args_t<std::remove_pointer_t<T>>;
|
||||||
|
using return_type = function_return_t<std::remove_pointer_t<T>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct callable_traits<T, std::enable_if_t<is_functor_v<T>>> {
|
||||||
|
using args_type = function_args_t<member_type_t<decltype(&T::operator())>>;
|
||||||
|
using return_type = function_return_t<member_type_t<decltype(&T::operator())>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Callable>
|
||||||
|
using callable_args_t = typename callable_traits<Callable>::args_type;
|
||||||
|
|
||||||
|
template <typename Callable>
|
||||||
|
using callable_return_t = typename callable_traits<Callable>::return_type;
|
||||||
|
|
||||||
|
template <typename Callable>
|
||||||
|
constexpr std::size_t callable_args_count_v = std::tuple_size_v<callable_args_t<Callable>>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct type_identity {
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||||
|
|
||||||
|
template <typename T, typename... Ts>
|
||||||
|
constexpr inline std::size_t types_count_v = (std::is_same_v<T, Ts> + ...);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline std::size_t types_count_v<T> = 0;
|
||||||
|
|
||||||
|
template <typename T, typename... Ts>
|
||||||
|
constexpr inline int type_index_v = [] {
|
||||||
|
bool arr[sizeof...(Ts) + 1] = {std::is_same_v<T, Ts>...};
|
||||||
|
for(int i = 0; i < sizeof...(Ts); ++i) {
|
||||||
|
if(arr[i]) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}();
|
||||||
|
|
||||||
|
static_assert(types_count_v<int, int, float, int> == 2);
|
||||||
|
static_assert(types_count_v<int, float, double> == 0);
|
||||||
|
static_assert(type_index_v<int, int, float, int> == 0);
|
||||||
|
static_assert(type_index_v<float, int, float, int> == 1);
|
||||||
|
static_assert(type_index_v<int, float, double> == -1);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline bool is_multiple_pointer_v = std::is_pointer_v<T> && is_multiple_pointer_v<std::remove_pointer_t<T>>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using pybind11_decay_t = std::decay_t<std::remove_pointer_t<std::decay_t<T>>>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr auto type_name() {
|
||||||
|
#if __GNUC__ || __clang__
|
||||||
|
std::string_view name = __PRETTY_FUNCTION__;
|
||||||
|
std::size_t start = name.find('=') + 2;
|
||||||
|
std::size_t end = name.size() - 1;
|
||||||
|
return std::string_view{name.data() + start, end - start};
|
||||||
|
#elif _MSC_VER
|
||||||
|
std::string_view name = __FUNCSIG__;
|
||||||
|
std::size_t start = name.find('<') + 1;
|
||||||
|
std::size_t end = name.rfind(">(");
|
||||||
|
name = std::string_view{name.data() + start, end - start};
|
||||||
|
start = name.find(' ');
|
||||||
|
return start == std::string_view::npos ? name : std::string_view{name.data() + start + 1, name.size() - start - 1};
|
||||||
|
#else
|
||||||
|
static_assert(false, "Unsupported compiler");
|
||||||
|
#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
|
||||||
401
include/pybind11/internal/types.h
Normal file
401
include/pybind11/internal/types.h
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "object.h"
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
handle cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = {});
|
||||||
|
|
||||||
|
struct arg {
|
||||||
|
const char* name;
|
||||||
|
handle default_;
|
||||||
|
|
||||||
|
arg(const char* name) : name(name), default_() {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
arg& operator= (T&& value) {
|
||||||
|
default_ = cast(std::forward<T>(value));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// undef in pybind11.h
|
||||||
|
#define PYBIND11_REGISTER_INIT(func) \
|
||||||
|
static inline int _register = [] { \
|
||||||
|
interpreter::register_init(func); \
|
||||||
|
return 0; \
|
||||||
|
}();
|
||||||
|
|
||||||
|
class none : public object {
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, empty, vm->tp_none_type);
|
||||||
|
#else
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, empty, [](const handle& obj) {
|
||||||
|
return obj.is_none();
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
none() : object(vm->None) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// corresponding to type in Python
|
||||||
|
class type : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Type, vm->tp_type);
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
static handle handle_of() {
|
||||||
|
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())); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// corresponding to bool in Python
|
||||||
|
class bool_ : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, bool, vm->tp_bool);
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool_(bool value) : object(create(value)) {}
|
||||||
|
|
||||||
|
operator bool () const { return self(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// corresponding to int in Python
|
||||||
|
class int_ : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, pkpy::i64, vm->tp_int);
|
||||||
|
|
||||||
|
public:
|
||||||
|
int_(int64_t value) : object(create(value)) {}
|
||||||
|
|
||||||
|
operator int64_t () const { return self(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// corresponding to float in Python
|
||||||
|
class float_ : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, pkpy::f64, vm->tp_float);
|
||||||
|
|
||||||
|
public:
|
||||||
|
float_(double value) : object(create(value)) {}
|
||||||
|
|
||||||
|
operator double () const { return self(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class iterable : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, empty, [](const handle& obj) {
|
||||||
|
return vm->getattr(obj.ptr(), pkpy::__iter__, false) != nullptr;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
class iterator : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, empty, [](const handle& obj) {
|
||||||
|
return vm->getattr(obj.ptr(), pkpy::__next__, false) != nullptr &&
|
||||||
|
vm->getattr(obj.ptr(), pkpy::__iter__, false) != nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
|
handle m_value;
|
||||||
|
|
||||||
|
iterator(pkpy::PyVar n, pkpy::PyVar s) : object(n), m_value(s) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
iterator(const handle& obj) : object(obj) { m_value = vm->py_next(obj.ptr()); }
|
||||||
|
|
||||||
|
iterator operator++ () {
|
||||||
|
m_value = vm->py_next(m_ptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator++ (int) {
|
||||||
|
m_value = vm->py_next(m_ptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handle& operator* () const { return m_value; }
|
||||||
|
|
||||||
|
friend bool operator== (const iterator& lhs, const iterator& rhs) { return lhs.m_value.is(rhs.m_value); }
|
||||||
|
|
||||||
|
friend bool operator!= (const iterator& lhs, const iterator& rhs) { return !(lhs == rhs); }
|
||||||
|
|
||||||
|
static iterator sentinel() { return iterator(vm->None, vm->StopIteration); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class str : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Str, vm->tp_str);
|
||||||
|
|
||||||
|
public:
|
||||||
|
str(const char* c, int len) : object(create(c, len)) {};
|
||||||
|
|
||||||
|
str(const char* c = "") : str(c, strlen(c)) {}
|
||||||
|
|
||||||
|
str(const std::string& s) : str(s.data(), s.size()) {}
|
||||||
|
|
||||||
|
str(std::string_view sv) : str(sv.data(), sv.size()) {}
|
||||||
|
|
||||||
|
// explicit str(const bytes& b);
|
||||||
|
explicit str(handle h);
|
||||||
|
|
||||||
|
operator std::string_view () const { return self().sv(); }
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
str format(Args&&... args) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// class bytes : public object {
|
||||||
|
// public:
|
||||||
|
// using object::object;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// class bytearray : public object {
|
||||||
|
// public:
|
||||||
|
// using object::object;
|
||||||
|
// };
|
||||||
|
|
||||||
|
class tuple : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Tuple, vm->tp_tuple);
|
||||||
|
|
||||||
|
public:
|
||||||
|
tuple(int n) : object(create(n)) {}
|
||||||
|
|
||||||
|
template <typename... Args, std::enable_if_t<(sizeof...(Args) > 1)>* = nullptr>
|
||||||
|
tuple(Args&&... args) : object(create(sizeof...(Args))) {
|
||||||
|
int index = 0;
|
||||||
|
((self()[index++] = pybind11::cast(std::forward<Args>(args)).ptr()), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() const { return self().size(); }
|
||||||
|
|
||||||
|
bool empty() const { return size() == 0; }
|
||||||
|
|
||||||
|
tuple_accessor operator[] (int i) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class list : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, pkpy::List, vm->tp_list)
|
||||||
|
|
||||||
|
public:
|
||||||
|
list() : object(create(0)) {}
|
||||||
|
|
||||||
|
list(int n) : object(create(n)) {}
|
||||||
|
|
||||||
|
template <typename... Args, std::enable_if_t<(sizeof...(Args) > 1)>* = nullptr>
|
||||||
|
list(Args&&... args) : object(create(sizeof...(Args))) {
|
||||||
|
int index = 0;
|
||||||
|
((self()[index++] = pybind11::cast(std::forward<Args>(args)).ptr()), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() const { return self().size(); }
|
||||||
|
|
||||||
|
bool empty() const { return size() == 0; }
|
||||||
|
|
||||||
|
void clear() { self().clear(); }
|
||||||
|
|
||||||
|
list_accessor operator[] (int i) const;
|
||||||
|
|
||||||
|
void append(const handle& obj) { self().push_back(obj.ptr()); }
|
||||||
|
|
||||||
|
void extend(const handle& iterable) {
|
||||||
|
for(auto& item: iterable) {
|
||||||
|
append(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(int index, const handle& obj) {
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
const auto pos = self().begin() + index;
|
||||||
|
self().insert(pos, obj.ptr());
|
||||||
|
#else
|
||||||
|
self().insert(index, obj.ptr());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class slice : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Slice, vm->tp_slice);
|
||||||
|
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
|
||||||
|
// class set : public object {
|
||||||
|
// public:
|
||||||
|
// using object::object;
|
||||||
|
// // set() : object(vm->new_object<pkpy::Se>(pkpy::VM::tp_set), true) {}
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
|
||||||
|
class dict : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Dict, vm->tp_dict);
|
||||||
|
|
||||||
|
public:
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
dict() : object(create()) {}
|
||||||
|
|
||||||
|
template <typename... Args, typename = std::enable_if_t<(std::is_same_v<remove_cvref_t<Args>, arg> && ...)>>
|
||||||
|
dict(Args&&... args) : object(create()) {
|
||||||
|
auto foreach_ = [&](pybind11::arg& arg) {
|
||||||
|
setitem(str(arg.name), arg.default_);
|
||||||
|
};
|
||||||
|
(foreach_(args), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setitem(const handle& key, const handle& value) { self().set(vm, key.ptr(), value.ptr()); }
|
||||||
|
|
||||||
|
handle getitem(const handle& key) const { return self().try_get(vm, key.ptr()); }
|
||||||
|
|
||||||
|
struct iterator {
|
||||||
|
pkpy_DictIter iter;
|
||||||
|
std::pair<handle, handle> value;
|
||||||
|
|
||||||
|
iterator operator++ () {
|
||||||
|
bool is_ended = pkpy_DictIter__next(&iter, (PyVar*)&value.first, (PyVar*)&value.second);
|
||||||
|
if(!is_ended) {
|
||||||
|
iter._dict = nullptr;
|
||||||
|
iter._index = -1;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<handle, handle> operator* () const { return value; }
|
||||||
|
|
||||||
|
bool operator== (const iterator& other) const {
|
||||||
|
return iter._dict == other.iter._dict && iter._index == other.iter._index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!= (const iterator& other) const { return !(*this == other); }
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() const {
|
||||||
|
iterator iter{self().iter(), {}};
|
||||||
|
++iter;
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() const { return {nullptr, -1}; }
|
||||||
|
#else
|
||||||
|
dict() : object(create(vm)) {}
|
||||||
|
|
||||||
|
template <typename... Args, typename = std::enable_if_t<(std::is_same_v<remove_cvref_t<Args>, arg> && ...)>>
|
||||||
|
dict(Args&&... args) : object(create(vm)) {
|
||||||
|
auto foreach_ = [&](pybind11::arg& arg) {
|
||||||
|
setitem(str(arg.name), arg.default_);
|
||||||
|
};
|
||||||
|
(foreach_(args), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setitem(const handle& key, const handle& value) { self().set(key.ptr(), value.ptr()); }
|
||||||
|
|
||||||
|
handle getitem(const handle& key) const { return self().try_get(key.ptr()); }
|
||||||
|
|
||||||
|
struct iterator {
|
||||||
|
pkpy::Dict::Item* items;
|
||||||
|
pkpy::Dict::ItemNode* nodes;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
iterator operator++ () {
|
||||||
|
index = nodes[index].next;
|
||||||
|
if(index == -1) {
|
||||||
|
items = nullptr;
|
||||||
|
nodes = nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<handle, handle> operator* () const { return {items[index].first, items[index].second}; }
|
||||||
|
|
||||||
|
bool operator== (const iterator& other) const {
|
||||||
|
return items == other.items && nodes == other.nodes && index == other.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!= (const iterator& other) const { return !(*this == other); }
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() const {
|
||||||
|
auto index = self()._head_idx;
|
||||||
|
if(index == -1) {
|
||||||
|
return end();
|
||||||
|
} else {
|
||||||
|
return {self()._items, self()._nodes, index};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() const { return {nullptr, nullptr, -1}; }
|
||||||
|
|
||||||
|
template <typename Key>
|
||||||
|
bool contains(Key&& key) const {
|
||||||
|
return self().contains(vm, pybind11::cast(std::forward<Key>(key)).ptr());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int size() const { return self().size(); }
|
||||||
|
|
||||||
|
bool empty() const { return size() == 0; }
|
||||||
|
|
||||||
|
void clear() { self().clear(); }
|
||||||
|
|
||||||
|
dict_accessor operator[] (int index) const;
|
||||||
|
dict_accessor operator[] (std::string_view) const;
|
||||||
|
dict_accessor operator[] (const handle& key) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class function : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Function, vm->tp_function);
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// class buffer : public object {
|
||||||
|
// public:
|
||||||
|
// using object::object;
|
||||||
|
//};
|
||||||
|
//
|
||||||
|
// class memory_view : public object {
|
||||||
|
// public:
|
||||||
|
// using object::object;
|
||||||
|
//};
|
||||||
|
//
|
||||||
|
class capsule : public object {
|
||||||
|
PYBIND11_REGISTER_INIT([] {
|
||||||
|
type_visitor::create<impl::capsule>(vm->builtins, "capsule", true);
|
||||||
|
});
|
||||||
|
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, impl::capsule, handle(vm->builtins->attr("capsule"))._as<pkpy::Type>());
|
||||||
|
|
||||||
|
public:
|
||||||
|
capsule(void* ptr, void (*destructor)(void*) = nullptr) : object(create(ptr, destructor)) {}
|
||||||
|
|
||||||
|
void* data() const { return self().ptr; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& cast() const {
|
||||||
|
return *static_cast<T*>(self().ptr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class property : public object {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Property, vm->tp_property);
|
||||||
|
|
||||||
|
public:
|
||||||
|
#if PK_VERSION_MAJOR == 2
|
||||||
|
property(handle getter, handle setter) : object(create(getter.ptr(), setter.ptr())) {}
|
||||||
|
#else
|
||||||
|
property(handle getter, handle setter) : object(create(pkpy::Property{getter.ptr(), setter.ptr()})) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
handle getter() const { return self().getter; }
|
||||||
|
|
||||||
|
handle setter() const { return self().setter; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class args : public tuple {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(tuple, pybind11::empty, vm->tp_tuple);
|
||||||
|
};
|
||||||
|
|
||||||
|
class kwargs : public dict {
|
||||||
|
PYBIND11_TYPE_IMPLEMENT(dict, pybind11::empty, vm->tp_dict);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pybind11
|
||||||
201
include/pybind11/operators.h
Normal file
201
include/pybind11/operators.h
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "pybind11.h"
|
||||||
|
|
||||||
|
namespace pybind11::impl {
|
||||||
|
|
||||||
|
enum op_id : int {
|
||||||
|
op_add,
|
||||||
|
op_sub,
|
||||||
|
op_mul,
|
||||||
|
op_div,
|
||||||
|
op_mod,
|
||||||
|
op_divmod,
|
||||||
|
op_pow,
|
||||||
|
op_lshift,
|
||||||
|
op_rshift,
|
||||||
|
op_and,
|
||||||
|
op_xor,
|
||||||
|
op_or,
|
||||||
|
op_neg,
|
||||||
|
op_pos,
|
||||||
|
op_abs,
|
||||||
|
op_invert,
|
||||||
|
op_int,
|
||||||
|
op_long,
|
||||||
|
op_float,
|
||||||
|
op_str,
|
||||||
|
op_cmp,
|
||||||
|
op_gt,
|
||||||
|
op_ge,
|
||||||
|
op_lt,
|
||||||
|
op_le,
|
||||||
|
op_eq,
|
||||||
|
op_ne,
|
||||||
|
op_iadd,
|
||||||
|
op_isub,
|
||||||
|
op_imul,
|
||||||
|
op_idiv,
|
||||||
|
op_imod,
|
||||||
|
op_ilshift,
|
||||||
|
op_irshift,
|
||||||
|
op_iand,
|
||||||
|
op_ixor,
|
||||||
|
op_ior,
|
||||||
|
op_complex,
|
||||||
|
op_bool,
|
||||||
|
op_nonzero,
|
||||||
|
op_repr,
|
||||||
|
op_truediv,
|
||||||
|
op_itruediv,
|
||||||
|
op_hash
|
||||||
|
};
|
||||||
|
|
||||||
|
enum op_type : int {
|
||||||
|
op_l, /* base type on left */
|
||||||
|
op_r, /* base type on right */
|
||||||
|
op_u /* unary operator */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct self_t {};
|
||||||
|
|
||||||
|
const static self_t self = self_t();
|
||||||
|
|
||||||
|
/// Type for an unused type slot
|
||||||
|
struct undefined_t {};
|
||||||
|
|
||||||
|
/// Don't warn about an unused variable
|
||||||
|
inline self_t __self() { return self; }
|
||||||
|
|
||||||
|
/// base template of operator implementations
|
||||||
|
template <op_id, op_type, typename B, typename L, typename R>
|
||||||
|
struct op_impl {};
|
||||||
|
|
||||||
|
/// Operator implementation generator
|
||||||
|
template <op_id id, op_type ot, typename L, typename R>
|
||||||
|
struct op_ {
|
||||||
|
constexpr static bool op_enable_if_hook = true;
|
||||||
|
|
||||||
|
template <typename Class, typename... Extra>
|
||||||
|
void execute(Class& cl, const Extra&... extra) const {
|
||||||
|
using Base = typename Class::underlying_type;
|
||||||
|
using L_type = std::conditional_t<std::is_same<L, self_t>::value, Base, L>;
|
||||||
|
using R_type = std::conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||||
|
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||||
|
cl.def(op::name(), &op::execute, extra...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Class, typename... Extra>
|
||||||
|
void execute_cast(Class& cl, const Extra&... extra) const {
|
||||||
|
using Base = typename Class::type;
|
||||||
|
using L_type = std::conditional_t<std::is_same<L, self_t>::value, Base, L>;
|
||||||
|
using R_type = std::conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||||
|
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||||
|
cl.def(op::name(), &op::execute_cast, extra...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \
|
||||||
|
template <typename B, typename L, typename R> \
|
||||||
|
struct op_impl<op_##id, op_l, B, L, R> { \
|
||||||
|
static char const* name() { return "__" #id "__"; } \
|
||||||
|
static auto execute(const L& l, const R& r) -> decltype(expr) { return (expr); } \
|
||||||
|
static B execute_cast(const L& l, const R& r) { return B(expr); } \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
template <typename B, typename L, typename R> \
|
||||||
|
struct op_impl<op_##id, op_r, B, L, R> { \
|
||||||
|
static char const* name() { return "__" #rid "__"; } \
|
||||||
|
static auto execute(const R& r, const L& l) -> decltype(expr) { return (expr); } \
|
||||||
|
static B execute_cast(const R& r, const L& l) { return B(expr); } \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
inline op_<op_##id, op_l, self_t, self_t> op(const self_t&, const self_t&) { \
|
||||||
|
return op_<op_##id, op_l, self_t, self_t>(); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
template <typename T> \
|
||||||
|
op_<op_##id, op_l, self_t, T> op(const self_t&, const T&) { \
|
||||||
|
return op_<op_##id, op_l, self_t, T>(); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
template <typename T> \
|
||||||
|
op_<op_##id, op_r, T, self_t> op(const T&, const self_t&) { \
|
||||||
|
return op_<op_##id, op_r, T, self_t>(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \
|
||||||
|
template <typename B, typename L, typename R> \
|
||||||
|
struct op_impl<op_##id, op_l, B, L, R> { \
|
||||||
|
static char const* name() { return "__" #id "__"; } \
|
||||||
|
static auto execute(L& l, const R& r) -> decltype(expr) { return expr; } \
|
||||||
|
static B execute_cast(L& l, const R& r) { return B(expr); } \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
template <typename T> \
|
||||||
|
op_<op_##id, op_l, self_t, T> op(const self_t&, const T&) { \
|
||||||
|
return op_<op_##id, op_l, self_t, T>(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PYBIND11_UNARY_OPERATOR(id, op, expr) \
|
||||||
|
template <typename B, typename L> \
|
||||||
|
struct op_impl<op_##id, op_u, B, L, undefined_t> { \
|
||||||
|
static char const* name() { return "__" #id "__"; } \
|
||||||
|
static auto execute(const L& l) -> decltype(expr) { return expr; } \
|
||||||
|
static B execute_cast(const L& l) { return B(expr); } \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t&) { \
|
||||||
|
return op_<op_##id, op_u, self_t, undefined_t>(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l* r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(and, rand, operator&, l& r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r)
|
||||||
|
PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r)
|
||||||
|
// PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
|
||||||
|
PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
|
||||||
|
PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
|
||||||
|
|
||||||
|
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
|
||||||
|
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
|
||||||
|
|
||||||
|
// WARNING: This usage of `abs` should only be done for existing STL overloads.
|
||||||
|
// Adding overloads directly in to the `std::` namespace is advised against:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/extending_std
|
||||||
|
|
||||||
|
// PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
|
||||||
|
// PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
|
||||||
|
// PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
|
||||||
|
// PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
|
||||||
|
// PYBIND11_UNARY_OPERATOR(int, int_, (int)l)
|
||||||
|
// PYBIND11_UNARY_OPERATOR(float, float_, (double)l)
|
||||||
|
|
||||||
|
#undef PYBIND11_BINARY_OPERATOR
|
||||||
|
#undef PYBIND11_INPLACE_OPERATOR
|
||||||
|
#undef PYBIND11_UNARY_OPERATOR
|
||||||
|
|
||||||
|
} // namespace pybind11::impl
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
using impl::self;
|
||||||
|
}
|
||||||
21
include/pybind11/pybind11.h
Normal file
21
include/pybind11/pybind11.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "internal/class.h"
|
||||||
|
|
||||||
|
namespace pybind11 {
|
||||||
|
|
||||||
|
namespace literals {
|
||||||
|
inline arg operator""_a (const char* c, size_t) { return arg(c); }
|
||||||
|
} // namespace literals
|
||||||
|
|
||||||
|
struct scoped_interpreter {
|
||||||
|
scoped_interpreter() { interpreter::initialize(); }
|
||||||
|
|
||||||
|
~scoped_interpreter() { interpreter::finalize(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pybind11
|
||||||
|
|
||||||
|
// namespace pybind11
|
||||||
|
|
||||||
|
#undef PYBIND11_TYPE_IMPLEMENT
|
||||||
|
#undef PYBIND11_REGISTER_INIT
|
||||||
144
include/pybind11/stl.h
Normal file
144
include/pybind11/stl.h
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
#include "pybind11.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <deque>
|
||||||
|
#include <forward_list>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace pybind11::impl {
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
struct type_caster<std::array<T, N>> {
|
||||||
|
|
||||||
|
struct wrapper {
|
||||||
|
std::array<T, N> container = {};
|
||||||
|
|
||||||
|
operator std::array<T, N>&& () { return std::move(container); }
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapper value;
|
||||||
|
|
||||||
|
bool load(const handle& src, bool convert) {
|
||||||
|
if(!isinstance<list>(src)) { return false; }
|
||||||
|
auto list = src.cast<pybind11::list>();
|
||||||
|
|
||||||
|
if(list.size() != N) { return false; }
|
||||||
|
|
||||||
|
for(std::size_t i = 0; i < N; ++i) {
|
||||||
|
type_caster<T> caster;
|
||||||
|
if(!caster.load(list[i], convert)) { return false; }
|
||||||
|
value.container[i] = caster.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static handle cast(U&& src, return_value_policy policy, handle parent) {
|
||||||
|
auto list = pybind11::list();
|
||||||
|
for(auto& item: src) {
|
||||||
|
list.append(pybind11::cast(item, policy, parent));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool is_py_list_like_v = false;
|
||||||
|
|
||||||
|
template <typename T, typename Allocator>
|
||||||
|
constexpr bool is_py_list_like_v<std::vector<T, Allocator>> = true;
|
||||||
|
|
||||||
|
template <typename T, typename Allocator>
|
||||||
|
constexpr bool is_py_list_like_v<std::list<T, Allocator>> = true;
|
||||||
|
|
||||||
|
template <typename T, typename Allocator>
|
||||||
|
constexpr bool is_py_list_like_v<std::deque<T, Allocator>> = true;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct type_caster<T, std::enable_if_t<is_py_list_like_v<T>>> {
|
||||||
|
|
||||||
|
struct wrapper {
|
||||||
|
T container;
|
||||||
|
|
||||||
|
operator T&& () { return std::move(container); }
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapper value;
|
||||||
|
|
||||||
|
bool load(const handle& src, bool convert) {
|
||||||
|
if(!isinstance<list>(src)) { return false; }
|
||||||
|
auto list = src.cast<pybind11::list>();
|
||||||
|
|
||||||
|
for(auto item: list) {
|
||||||
|
type_caster<typename T::value_type> caster;
|
||||||
|
if(!caster.load(item, convert)) { return false; }
|
||||||
|
value.container.push_back(caster.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static handle cast(U&& src, return_value_policy policy, handle parent) {
|
||||||
|
auto list = pybind11::list();
|
||||||
|
for(auto& item: src) {
|
||||||
|
list.append(pybind11::cast(item, policy, parent));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool is_py_map_like_v = false;
|
||||||
|
|
||||||
|
template <typename Key, typename T, typename Compare, typename Allocator>
|
||||||
|
constexpr bool is_py_map_like_v<std::map<Key, T, Compare, Allocator>> = true;
|
||||||
|
|
||||||
|
template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
|
||||||
|
constexpr bool is_py_map_like_v<std::unordered_map<Key, T, Hash, KeyEqual, Allocator>> = true;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct type_caster<T, std::enable_if_t<is_py_map_like_v<T>>> {
|
||||||
|
|
||||||
|
struct wrapper {
|
||||||
|
T container;
|
||||||
|
|
||||||
|
operator T&& () { return std::move(container); }
|
||||||
|
};
|
||||||
|
|
||||||
|
wrapper value;
|
||||||
|
|
||||||
|
bool load(const handle& src, bool convert) {
|
||||||
|
if(!isinstance<dict>(src)) { return false; }
|
||||||
|
auto dict = src.cast<pybind11::dict>();
|
||||||
|
|
||||||
|
for(auto item: dict) {
|
||||||
|
type_caster<typename T::key_type> key_caster;
|
||||||
|
if(!key_caster.load(item.first, convert)) { return false; }
|
||||||
|
|
||||||
|
type_caster<typename T::mapped_type> value_caster;
|
||||||
|
if(!value_caster.load(item.second, convert)) { return false; }
|
||||||
|
|
||||||
|
value.container.try_emplace(key_caster.value, value_caster.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static handle cast(U&& src, return_value_policy policy, handle parent) {
|
||||||
|
auto dict = pybind11::dict();
|
||||||
|
for(auto& [key, value]: src) {
|
||||||
|
dict[pybind11::cast(key, policy, parent)] = pybind11::cast(value, policy, parent);
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pybind11::impl
|
||||||
|
|
||||||
@ -77,8 +77,8 @@ class complex:
|
|||||||
|
|
||||||
def __pow__(self, other: int | float):
|
def __pow__(self, other: int | float):
|
||||||
if type(other) in (int, float):
|
if type(other) in (int, float):
|
||||||
return complex(self.__abs__() ** other * math.cos(other * phase(self)),
|
return complex(abs(self) ** other * math.cos(other * phase(self)),
|
||||||
self.__abs__() ** other * math.sin(other * phase(self)))
|
abs(self) ** other * math.sin(other * phase(self)))
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
def __abs__(self) -> float:
|
def __abs__(self) -> float:
|
||||||
@ -97,7 +97,7 @@ def phase(z: complex):
|
|||||||
return math.atan2(z.imag, z.real)
|
return math.atan2(z.imag, z.real)
|
||||||
|
|
||||||
def polar(z: complex):
|
def polar(z: complex):
|
||||||
return z.__abs__(), phase(z)
|
return abs(z), phase(z)
|
||||||
|
|
||||||
def rect(r: float, phi: float):
|
def rect(r: float, phi: float):
|
||||||
return r * math.cos(phi) + r * math.sin(phi) * 1j
|
return r * math.cos(phi) + r * math.sin(phi) * 1j
|
||||||
@ -108,7 +108,7 @@ def exp(z: complex):
|
|||||||
return math.exp(z.real) * rect(1, z.imag)
|
return math.exp(z.real) * rect(1, z.imag)
|
||||||
|
|
||||||
def log(z: complex, base=2.718281828459045):
|
def log(z: complex, base=2.718281828459045):
|
||||||
return math.log(z.__abs__(), base) + phase(z) * 1j
|
return math.log(abs(z), base) + phase(z) * 1j
|
||||||
|
|
||||||
def log10(z: complex):
|
def log10(z: complex):
|
||||||
return log(z, 10)
|
return log(z, 10)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -84,7 +84,7 @@ void add_module_sys(VM* vm){
|
|||||||
|
|
||||||
vm->bind_func(stderr_, "write", 1, [](VM* vm, ArgsView args) {
|
vm->bind_func(stderr_, "write", 1, [](VM* vm, ArgsView args) {
|
||||||
Str& s = CAST(Str&, args[0]);
|
Str& s = CAST(Str&, args[0]);
|
||||||
vm->_stderr(s.data, s.size);
|
vm->stderr_write(s);
|
||||||
return vm->None;
|
return vm->None;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -158,8 +158,7 @@ void __init_builtins(VM* _vm) {
|
|||||||
_vm->bind_func(_vm->builtins, "abs", 1, [](VM* vm, ArgsView args) {
|
_vm->bind_func(_vm->builtins, "abs", 1, [](VM* vm, ArgsView args) {
|
||||||
if(is_int(args[0])) return VAR(std::abs(_CAST(i64, args[0])));
|
if(is_int(args[0])) return VAR(std::abs(_CAST(i64, args[0])));
|
||||||
if(is_float(args[0])) return VAR(std::abs(_CAST(f64, args[0])));
|
if(is_float(args[0])) return VAR(std::abs(_CAST(f64, args[0])));
|
||||||
vm->TypeError("bad operand type for abs()");
|
return vm->call_method(args[0], __abs__);
|
||||||
return vm->None;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
_vm->bind(_vm->builtins, "max(*args, key=None)", [](VM* vm, ArgsView args){
|
_vm->bind(_vm->builtins, "max(*args, key=None)", [](VM* vm, ArgsView args){
|
||||||
|
|||||||
@ -592,6 +592,7 @@ const StrName __package__ = StrName::get("__package__");
|
|||||||
const StrName __path__ = StrName::get("__path__");
|
const StrName __path__ = StrName::get("__path__");
|
||||||
const StrName __class__ = StrName::get("__class__");
|
const StrName __class__ = StrName::get("__class__");
|
||||||
const StrName __missing__ = StrName::get("__missing__");
|
const StrName __missing__ = StrName::get("__missing__");
|
||||||
|
const StrName __abs__ = StrName::get("__abs__");
|
||||||
|
|
||||||
const StrName pk_id_add = StrName::get("add");
|
const StrName pk_id_add = StrName::get("add");
|
||||||
const StrName pk_id_set = StrName::get("set");
|
const StrName pk_id_set = StrName::get("set");
|
||||||
|
|||||||
17
src/vm.cpp
17
src/vm.cpp
@ -518,10 +518,11 @@ i64 VM::py_hash(PyVar obj){
|
|||||||
}
|
}
|
||||||
|
|
||||||
PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar locals){
|
PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar locals){
|
||||||
Frame* frame = &vm->callstack.top();
|
Frame* frame = nullptr;
|
||||||
|
if(!callstack.empty()) frame = &callstack.top();
|
||||||
|
|
||||||
// fast path
|
// fast path
|
||||||
if(globals == vm->None && locals == vm->None){
|
if(frame && globals == vm->None && locals == vm->None){
|
||||||
return vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
|
return vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,7 +535,7 @@ PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar local
|
|||||||
Dict* locals_dict = nullptr;
|
Dict* locals_dict = nullptr;
|
||||||
|
|
||||||
if(globals == vm->None){
|
if(globals == vm->None){
|
||||||
globals_obj = frame->_module;
|
globals_obj = frame ? frame->_module : _main;
|
||||||
}else{
|
}else{
|
||||||
if(is_type(globals, VM::tp_mappingproxy)){
|
if(is_type(globals, VM::tp_mappingproxy)){
|
||||||
globals_obj = PK_OBJ_GET(MappingProxy, globals).obj;
|
globals_obj = PK_OBJ_GET(MappingProxy, globals).obj;
|
||||||
@ -1081,8 +1082,10 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call){
|
|||||||
for(int j=0; j<co_nlocals; j++) _base[j] = buffer[j];
|
for(int j=0; j<co_nlocals; j++) _base[j] = buffer[j];
|
||||||
ret = f.call(vm, ArgsView(s_data._sp - co_nlocals, s_data._sp));
|
ret = f.call(vm, ArgsView(s_data._sp - co_nlocals, s_data._sp));
|
||||||
}else{
|
}else{
|
||||||
if(KWARGC != 0) TypeError("old-style native_func does not accept keyword arguments");
|
if(f.argc != -1){
|
||||||
f.check_size(this, args);
|
if(KWARGC != 0) TypeError("old-style native_func does not accept keyword arguments");
|
||||||
|
f.check_size(this, args);
|
||||||
|
}
|
||||||
ret = f.call(this, args);
|
ret = f.call(this, args);
|
||||||
}
|
}
|
||||||
s_data.reset(p0);
|
s_data.reset(p0);
|
||||||
@ -1133,7 +1136,7 @@ PyVar VM::vectorcall(int ARGC, int KWARGC, bool op_call){
|
|||||||
p1[-(ARGC + 2)] = call_f;
|
p1[-(ARGC + 2)] = call_f;
|
||||||
p1[-(ARGC + 1)] = self;
|
p1[-(ARGC + 1)] = self;
|
||||||
// [call_f, self, args..., kwargs...]
|
// [call_f, self, args..., kwargs...]
|
||||||
return vectorcall(ARGC, KWARGC, false);
|
return vectorcall(ARGC, KWARGC, op_call);
|
||||||
}
|
}
|
||||||
TypeError(_type_name(vm, callable_t).escape() + " object is not callable");
|
TypeError(_type_name(vm, callable_t).escape() + " object is not callable");
|
||||||
PK_UNREACHABLE()
|
PK_UNREACHABLE()
|
||||||
@ -1762,4 +1765,4 @@ void VM::__breakpoint(){
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace pkpy
|
} // namespace pkpy
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user