Compare commits

...

16 Commits

Author SHA1 Message Date
BLUELOVETH
4ba0b7d2e2
Update pybind11.md 2024-06-19 16:19:20 +08:00
BLUELOVETH
2d6def4bbd
Merge pull request #285 from 16bit-ykiko/pybind11-doc
update document.
2024-06-19 16:10:29 +08:00
ykiko
a5a3c75b83 fix typo. 2024-06-19 16:09:54 +08:00
ykiko
8ffcacf99f Update. 2024-06-19 16:06:16 +08:00
ykiko
a2cb91b125 update document. 2024-06-19 15:51:15 +08:00
BLUELOVETH
86006d0b61
Update migrate.md 2024-06-19 15:51:01 +08:00
BLUELOVETH
a08c5af6e9
Update migrate.md 2024-06-19 15:47:41 +08:00
BLUELOVETH
475be8ce80
Merge pull request #283 from 16bit-ykiko/doc
Update README.
2024-06-19 14:24:35 +08:00
ykiko
12d748ba7d fix format. 2024-06-19 14:15:48 +08:00
BLUELOVETH
0c1bd532f8
Merge pull request #282 from 16bit-ykiko/pybind11-fix
some fix.
2024-06-19 13:05:58 +08:00
ykiko
d52cbec80c Update README. 2024-06-19 12:32:58 +08:00
ykiko
e16aa501ef some fix. 2024-06-19 12:19:05 +08:00
blueloveTH
59bbecdbee fix versions 2024-06-19 11:06:19 +08:00
blueloveTH
f5c356a047 some fix 2024-06-19 11:04:32 +08:00
blueloveTH
68f8cd1159 Update migrate.md 2024-06-19 11:02:56 +08:00
blueloveTH
259394d7ad doc fix 2024-06-19 10:58:13 +08:00
17 changed files with 425 additions and 396 deletions

View File

@ -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;
} }
``` ```

View File

@ -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));
```

View File

@ -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
View 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
View File

@ -0,0 +1,291 @@
---
icon: log
title: 'pybind11 User Guide'
order: 21
---
## Quick Start
pkpy provides a [pybind11](https://pybind11.readthedocs.io/en/stable/) compatible layer which allows users to do convenient bindings.
To begin with, use `py::scoped_interpreter guard{}` to start the interpreter before using any Python objects.
Or explicitly call `py::interpreter::initialize()` and `py::interpreter::finalize()`.
### module
```cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
PYBIND11_MODULE(example, m) {
m.def("add", [](int a, int b) {
return a + b;
});
auto math = m.def_submodule("math");
}
```
### function
```cpp
int add(int a, int b) { return a + b; }
int add(int a, int b, int c) { return a + b + c; }
void register_function(py::module_& m)
{
m.def("add", py::overload_cast<int, int>(&add));
// support function overload
m.def("add", py::overload_cast<int, int, int>(&add));
// bind with default arguments
m.def("sub", [](int a, int b) {
return a - b;
}, py::arg("a") = 1, py::arg("b") = 2);
// bind *args
m.def("add", [](py::args args) {
int sum = 0;
for (auto& arg : args) {
sum += arg.cast<int>();
}
return sum;
});
// bind **kwargs
m.def("add", [](py::kwargs kwargs) {
int sum = 0;
for (auto item : kwargs) {
sum += item.second.cast<int>();
}
return sum;
});
}
```
### class
```cpp
struct Point
{
const int x;
int y;
public:
Point() : x(0), y(0) {}
Point(int x, int y) : x(x), y(y) {}
Point(const Point& p) : x(p.x), y(p.y) {}
std::string stringfy() const {
return "(" + std::to_string(x) + ", " + std::to_string(y) + ")";
}
};
struct Point3D : Point
{
private:
int z;
public:
Point3D(int x, int y, int z) : Point(x, y), z(z) {}
int get_z() const { return z; }
void set_z(int z) { this->z = z; }
};
void bind_class(py::module_& m)
{
py::class_<Point>(m, "Point")
.def(py::init<>())
.def(py::init<int, int>())
.def(py::init<const Point&>())
.def_readonly("x", &Point::x)
.def_readwrite("y", &Point::y)
.def("__str__", &Point::stringfy);
// only support single inheritance
py::class_<Point3D, Point>(m, "Point3D", py::dynamic_attr())
.def(py::init<int, int, int>())
.def_property("z", &Point3D::get_z, &Point3D::set_z);
// dynamic_attr will enable the dict of bound class
}
```
### operators
```cpp
#include <pybind11/operators.h>
namespace py = pybind11;
struct Int {
int value;
Int(int value) : value(value) {}
Int operator+(const Int& other) const {
return Int(value + other.value);
}
Int operator-(const Int& other) const {
return Int(value - other.value);
}
bool operator==(const Int& other) const {
return value == other.value;
}
bool operator!=(const Int& other) const {
return value != other.value;
}
};
void bind_operators(py::module_& m)
{
py::class_<Int>(m, "Int")
.def(py::init<int>())
.def(py::self + py::self)
.def(py::self - py::self)
.def(py::self == py::self)
.def(py::self != py::self);
// other operators are similar
}
```
### py::object
`py::object` is just simple wrapper around `PyVar`. It supports some convenient methods to interact with Python objects.
here are some common methods:
```cpp
obj.attr("x"); // access attribute
obj[1]; // access item
obj.is_none(); // same as obj is None in Python
obj.is(obj2); // same as obj is obj2 in Python
// operators
obj + obj2; // same as obj + obj2 in Python
// ...
obj == obj2; // same as obj == obj2 in Python
// ...
obj(...); // same as obj.__call__(...)
py::cast(obj); // cast to Python object
obj.cast<T>; // cast to C++ type
py::type::of(obj); // get type of obj
py::type::of<T>(); // get type of T, if T is registered
```
you can also create some builtin objects with their according wrappers:
```cpp
py::bool_ b = {true};
py::int_ i = {1};
py::float_ f = {1.0};
py::str s = {"hello"};
py::list l = {1, 2, 3};
py::tuple t = {1, 2, 3};
// ...
```
## More Examples
More examples please see the test [folder](https://github.com/pocketpy/gsoc-2024-dev/tree/main/pybind11/tests) in the GSoC repository. All tested features are supported.
## Limits and Comparison
This is a feature list of pybind11 for pocketpy. It lists all completed and pending features. It also lists the features that cannot be implemented in the current version of pocketpy.
### [Function](https://pybind11.readthedocs.io/en/stable/advanced/functions.html)
- [x] Function overloading
- [x] Return value policy
- [x] is_prepend
- [x] `*args` and `**kwargs`
- [ ] Keep-alive
- [ ] Call Guard
- [x] Default arguments
- [ ] Keyword-Only arguments
- [ ] Positional-Only arguments
- [ ] Allow/Prohibiting None arguments
### [Class](https://pybind11.readthedocs.io/en/stable/classes.html)
- [x] Creating bindings for a custom type
- [x] Binding lambda functions
- [x] Dynamic attributes
- [x] Inheritance and automatic downcasting
- [x] Enumerations and internal types
- [ ] Instance and static fields
> Binding static fields may never be implemented in pocketpy because it requires a metaclass, which is a heavy and infrequently used feature.
### [Exceptions](https://pybind11.readthedocs.io/en/stable/advanced/exceptions.html)
Need further discussion.
### [Smart pointers](https://pybind11.readthedocs.io/en/stable/advanced/smart_ptrs.html)
- [ ] std::shared_ptr
- [ ] std::unique_ptr
- [ ] Custom smart pointers
### [Type conversions](https://pybind11.readthedocs.io/en/stable/advanced/cast/index.html)
- [x] Python built-in types
- [x] STL Containers
- [ ] Functional
- [ ] Chrono
### [Python C++ interface](https://pybind11.readthedocs.io/en/stable/advanced/pycpp/object.html)
Need further discussion.
- [x] `object`
- [x] `none`
- [x] `type`
- [x] `bool_`
- [x] `int_`
- [x] `float_`
- [x] `str`
- [ ] `bytes`
- [ ] `bytearray`
- [x] `tuple`
- [x] `list`
- [ ] `set`
- [x] `dict`
- [ ] `slice`
- [x] `iterable`
- [x] `iterator`
- [ ] `function`
- [ ] `buffer`
- [ ] `memoryview`
- [x] `capsule`
### [Miscellaneous](https://pybind11.readthedocs.io/en/stable/advanced/misc.html)
- [ ] Global Interpreter Lock (GIL)
- [ ] Binding sequence data types, iterators, the slicing protocol, etc.
- [x] Convenient operators binding
### Differences between CPython and pocketpy
- only `add`, `sub` and `mul` have corresponding right versions in pocketpy. So if you bind `int() >> py::self`, it will has no effect in pocketpy.
- `__new__` and `__del__` are not supported in pocketpy.
- in-place operators, such as `+=`, `-=`, `*=`, etc., are not supported in pocketpy.
- thre return value of `globals` is immutable in pocketpy.

View File

@ -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

View File

@ -20,10 +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_MAJOR 1
#define PK_VERSION_MINOR 5 #define PK_VERSION_MINOR 4
#define PK_VERSION_PATCH 0 #define PK_VERSION_PATCH 6
#include "config.h" #include "config.h"
#include "export.h" #include "export.h"

View File

@ -81,9 +81,13 @@ inline bool isinstance(const handle& obj, const handle& type) {
inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); } inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); }
namespace impl {
template <typename T, typename SFINAE = void> template <typename T, typename SFINAE = void>
struct type_caster; struct type_caster;
}
template <typename T> template <typename T>
handle cast(T&& value, return_value_policy policy, handle parent) { handle cast(T&& value, return_value_policy policy, handle parent) {
// decay_t can resolve c-array type, but remove_cv_ref_t can't. // decay_t can resolve c-array type, but remove_cv_ref_t can't.
@ -107,7 +111,7 @@ handle cast(T&& value, return_value_policy policy, handle parent) {
: return_value_policy::move; : return_value_policy::move;
} }
return type_caster<underlying_type>::cast(std::forward<T>(value), policy, parent); return impl::type_caster<underlying_type>::cast(std::forward<T>(value), policy, parent);
} }
} }
@ -115,7 +119,7 @@ template <typename T>
T cast(const handle& obj, bool convert) { T cast(const handle& obj, bool convert) {
assert(obj.ptr() != nullptr); assert(obj.ptr() != nullptr);
type_caster<T> caster = {}; impl::type_caster<T> caster = {};
if(caster.load(obj, convert)) { if(caster.load(obj, convert)) {
return caster.value; return caster.value;

View File

@ -2,7 +2,7 @@
#include "instance.h" #include "instance.h"
#include "accessor.h" #include "accessor.h"
namespace pybind11 { namespace pybind11::impl {
using pkpy::is_floating_point_v; using pkpy::is_floating_point_v;
using pkpy::is_integral_v; using pkpy::is_integral_v;

View File

@ -169,15 +169,15 @@ public:
} }
enum_& value(const char* name, T value) { enum_& value(const char* name, T value) {
handle var = type_caster<T>::cast(value, return_value_policy::copy); handle var = pybind11::cast(value, return_value_policy::copy);
this->m_ptr->attr().set(name, var.ptr()); setattr(*this, name, var);
m_values.emplace_back(name, var); m_values.emplace_back(name, var);
return *this; return *this;
} }
enum_& export_values() { enum_& export_values() {
for(auto& [name, value]: m_values) { for(auto& [name, value]: m_values) {
Base::m_scope.ptr()->attr().set(name, value.ptr()); setattr(Base::m_scope, name, value);
} }
return *this; return *this;
} }

View File

@ -176,7 +176,7 @@ public:
template <typename Fn, std::size_t... Is, typename... Args> template <typename Fn, std::size_t... Is, typename... Args>
handle invoke(Fn&& fn, handle invoke(Fn&& fn,
std::index_sequence<Is...>, std::index_sequence<Is...>,
std::tuple<type_caster<Args>...>& casters, std::tuple<impl::type_caster<Args>...>& casters,
return_value_policy policy, return_value_policy policy,
handle parent) { handle parent) {
using underlying_type = std::decay_t<Fn>; using underlying_type = std::decay_t<Fn>;
@ -361,10 +361,10 @@ struct template_parser<Callable, std::tuple<Extras...>, std::tuple<Args...>, std
// resolve keyword arguments // resolve keyword arguments
const auto n = vm->s_data._sp - view.end(); const auto n = vm->s_data._sp - view.end();
std::size_t index = 0; int index = 0;
if constexpr(named_argc > 0) { if constexpr(named_argc > 0) {
std::size_t arg_index = 0; int arg_index = 0;
auto& arguments = *record.arguments; auto& arguments = *record.arguments;
while(arg_index < named_argc && index < n) { while(arg_index < named_argc && index < n) {
@ -403,7 +403,7 @@ struct template_parser<Callable, std::tuple<Extras...>, std::tuple<Args...>, std
} }
// ok, all the arguments are valid, call the function // ok, all the arguments are valid, call the function
std::tuple<type_caster<Args>...> casters; std::tuple<impl::type_caster<Args>...> casters;
// check type compatibility // check type compatibility
if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) { if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
@ -489,14 +489,14 @@ pkpy::PyVar setter_wrapper(pkpy::VM* vm, pkpy::ArgsView view) {
if constexpr(std::is_member_object_pointer_v<Setter>) { if constexpr(std::is_member_object_pointer_v<Setter>) {
// specialize for pointer to data member // specialize for pointer to data member
type_caster<member_type_t<Setter>> caster; impl::type_caster<member_type_t<Setter>> caster;
if(caster.load(view[1], true)) { if(caster.load(view[1], true)) {
self.*setter = caster.value; self.*setter = caster.value;
return vm->None; return vm->None;
} }
} else { } else {
// specialize for pointer to member function // specialize for pointer to member function
type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster; impl::type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
if(caster.load(view[1], true)) { if(caster.load(view[1], true)) {
(self.*setter)(caster.value); (self.*setter)(caster.value);
return vm->None; return vm->None;
@ -507,7 +507,7 @@ pkpy::PyVar setter_wrapper(pkpy::VM* vm, pkpy::ArgsView view) {
using Self = remove_cvref_t<std::tuple_element_t<0, callable_args_t<Setter>>>; using Self = remove_cvref_t<std::tuple_element_t<0, callable_args_t<Setter>>>;
auto& self = handle(view[0])._as<instance>()._as<Self>(); auto& self = handle(view[0])._as<instance>()._as<Self>();
type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster; impl::type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
if(caster.load(view[1], true)) { if(caster.load(view[1], true)) {
setter(self, caster.value); setter(self, caster.value);
return vm->None; return vm->None;

View File

@ -37,7 +37,7 @@ class handle;
class object; class object;
class iterator; class iterator;
class str; class str;
class arg; struct arg;
struct args_proxy; struct args_proxy;
struct kwargs_proxy; struct kwargs_proxy;
@ -210,12 +210,7 @@ constexpr inline bool is_pyobject_v = std::is_base_of_v<object, T>;
#if PK_VERSION_MAJOR == 2 #if PK_VERSION_MAJOR == 2
using error_already_set = pkpy::TopLevelException; using error_already_set = pkpy::TopLevelException;
#else #else
class error_already_set : std::exception { using error_already_set = pkpy::Exception;
public:
error_already_set() = default;
const char* what() const noexcept override { return "An error occurred while calling a Python function."; }
};
#endif #endif
inline void setattr(const handle& obj, const handle& name, const handle& value); inline void setattr(const handle& obj, const handle& name, const handle& value);

View File

@ -19,8 +19,10 @@ public:
} }
module_ def_submodule(const char* name, const char* doc = nullptr) { module_ def_submodule(const char* name, const char* doc = nullptr) {
auto package = this->package()._as<pkpy::Str>() + "." + this->name()._as<pkpy::Str>(); // TODO: resolve package
auto m = vm->new_module(name, 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); setattr(*this, name, m);
return m; return m;
} }

View File

@ -69,15 +69,19 @@ public:
template <typename T> template <typename T>
decltype(auto) _as() const { decltype(auto) _as() const {
static_assert(!std::is_reference_v<T>, "T must not be a reference type."); static_assert(!std::is_reference_v<T>, "T must not be a reference type.");
#if PK_VERSION_MAJOR == 2 if constexpr(std::is_same_v<T, empty>) {
if constexpr(pkpy::is_sso_v<T>) { return empty();
return pkpy::_py_cast<T>(vm, ptr());
} else { } else {
return ptr().template obj_get<T>(); #if PK_VERSION_MAJOR == 2
} if constexpr(pkpy::is_sso_v<T>) {
return pkpy::_py_cast<T>(vm, ptr());
} else {
return ptr().template obj_get<T>();
}
#else #else
return (((pkpy::Py_<T>*)ptr())->_value); return (((pkpy::Py_<T>*)ptr())->_value);
#endif #endif
}
} }
}; };

View File

@ -25,9 +25,9 @@ struct function_traits {
static_assert(dependent_false<Fn>, "unsupported function type"); static_assert(dependent_false<Fn>, "unsupported function type");
}; };
#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(qualifiers) \ #define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(...) \
template <typename R, typename... Args> \ template <typename R, typename... Args> \
struct function_traits<R(Args...) qualifiers> { \ struct function_traits<R(Args...) __VA_ARGS__> { \
using return_type = R; \ using return_type = R; \
using args_type = std::tuple<Args...>; \ using args_type = std::tuple<Args...>; \
constexpr static std::size_t args_count = sizeof...(Args); \ constexpr static std::size_t args_count = sizeof...(Args); \
@ -167,4 +167,32 @@ constexpr auto type_name() {
#endif #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 } // namespace pybind11

View File

@ -49,6 +49,11 @@ public:
return type_visitor::type<T>(); 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())); } static type of(const handle& obj) { return type(vm->_t(obj.ptr())); }
}; };
@ -385,11 +390,11 @@ public:
}; };
class args : public tuple { class args : public tuple {
PYBIND11_TYPE_IMPLEMENT(tuple, struct empty, vm->tp_tuple); PYBIND11_TYPE_IMPLEMENT(tuple, pybind11::empty, vm->tp_tuple);
}; };
class kwargs : public dict { class kwargs : public dict {
PYBIND11_TYPE_IMPLEMENT(dict, struct empty, vm->tp_dict); PYBIND11_TYPE_IMPLEMENT(dict, pybind11::empty, vm->tp_dict);
}; };
} // namespace pybind11 } // namespace pybind11

View File

@ -9,7 +9,7 @@
#include <map> #include <map>
#include <unordered_map> #include <unordered_map>
namespace pybind11 { namespace pybind11::impl {
template <typename T, std::size_t N> template <typename T, std::size_t N>
struct type_caster<std::array<T, N>> { struct type_caster<std::array<T, N>> {
@ -140,5 +140,5 @@ struct type_caster<T, std::enable_if_t<is_py_map_like_v<T>>> {
} }
}; };
} // namespace pybind11 } // namespace pybind11::impl