mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 03:20:18 +00:00
pybind11 for pkpy 2.0 (#299)
* update pkbind. * some fix. * some fix. * update job name. * fix CI. * remove iostream.
This commit is contained in:
parent
372325d663
commit
7e99584ddc
76
.github/workflows/pybind11.yml
vendored
Normal file
76
.github/workflows/pybind11.yml
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
name: PKBIND Build and Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- "docs/**"
|
||||||
|
- "web/**"
|
||||||
|
- "**.md"
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- "docs/**"
|
||||||
|
- "web/**"
|
||||||
|
- "**.md"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_linux:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up GCC
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y gcc g++
|
||||||
|
|
||||||
|
- name: Set up CMake
|
||||||
|
uses: jwlawson/actions-setup-cmake@v1.10
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
cd include/pybind11/tests
|
||||||
|
cmake -B build
|
||||||
|
cmake --build build --config Release --parallel
|
||||||
|
./build/PKBIND_TEST
|
||||||
|
|
||||||
|
build_win:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up MSVC
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
|
||||||
|
- name: Set up CMake
|
||||||
|
uses: jwlawson/actions-setup-cmake@v1.10
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
cd include\pybind11\tests
|
||||||
|
cmake -B build
|
||||||
|
cmake --build build --config Release --parallel
|
||||||
|
build\Release\PKBIND_TEST.exe
|
||||||
|
|
||||||
|
build_mac:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Clang
|
||||||
|
run: |
|
||||||
|
brew install llvm
|
||||||
|
echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> ~/.zshrc
|
||||||
|
source ~/.zshrc
|
||||||
|
|
||||||
|
- name: Set up CMake
|
||||||
|
uses: jwlawson/actions-setup-cmake@v1.10
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: |
|
||||||
|
cd include/pybind11/tests
|
||||||
|
cmake -B build -DENABLE_TEST=ON
|
||||||
|
cmake --build build --config Release --parallel
|
||||||
|
./build/PKBIND_TEST
|
3
include/pybind11/embed.h
Normal file
3
include/pybind11/embed.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pybind11.h"
|
164
include/pybind11/internal/accessor.h
Normal file
164
include/pybind11/internal/accessor.h
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "builtins.h"
|
||||||
|
|
||||||
|
namespace pkbind {
|
||||||
|
|
||||||
|
template <typename policy>
|
||||||
|
class accessor : public interface<accessor<policy>> {
|
||||||
|
using key_type = typename policy::key_type;
|
||||||
|
|
||||||
|
friend interface<handle>;
|
||||||
|
friend interface<accessor<policy>>;
|
||||||
|
friend tuple;
|
||||||
|
friend list;
|
||||||
|
friend dict;
|
||||||
|
|
||||||
|
accessor(handle obj, key_type key) : m_obj(obj), m_value(), m_key(key) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
auto ptr() const {
|
||||||
|
if(m_value.empty()) {
|
||||||
|
m_value = borrow(policy::get(m_obj, m_key));
|
||||||
|
}
|
||||||
|
return m_value.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Value>
|
||||||
|
accessor& operator= (Value&& value) && {
|
||||||
|
policy::set(m_obj, m_key, pkbind::cast(std::forward<Value>(value)));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Value>
|
||||||
|
accessor& operator= (Value&& value) & {
|
||||||
|
m_value = std::forward<Value>(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator handle () const { return ptr(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
handle m_obj;
|
||||||
|
mutable object m_value;
|
||||||
|
key_type m_key;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace policy {
|
||||||
|
|
||||||
|
struct attr {
|
||||||
|
using key_type = name;
|
||||||
|
|
||||||
|
static handle get(handle obj, name key) {
|
||||||
|
raise_call<py_getattr>(obj.ptr(), key.index());
|
||||||
|
return py_retval();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(handle obj, name key, handle value) { raise_call<py_setattr>(obj.ptr(), key.index(), value.ptr()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Key>
|
||||||
|
struct item {
|
||||||
|
using key_type = Key;
|
||||||
|
|
||||||
|
static handle get(handle obj, int key) { return get(obj, int_(key)); }
|
||||||
|
|
||||||
|
static handle get(handle obj, name key) { return get(obj, str(key)); }
|
||||||
|
|
||||||
|
static handle get(handle obj, handle key) {
|
||||||
|
raise_call<py_getitem>(obj.ptr(), key.ptr());
|
||||||
|
return py_retval();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(handle obj, int key, handle value) { set(obj, int_(key), value); }
|
||||||
|
|
||||||
|
static void set(handle obj, name key, handle value) { set(obj, str(key), value); }
|
||||||
|
|
||||||
|
static void set(handle obj, handle key, handle value) { raise_call<py_setitem>(obj.ptr(), key.ptr(), value.ptr()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tuple {
|
||||||
|
using key_type = int;
|
||||||
|
|
||||||
|
static handle get(handle obj, int key) { return py_tuple_getitem(obj.ptr(), key); }
|
||||||
|
|
||||||
|
static void set(handle obj, int key, handle value) { py_tuple_setitem(obj.ptr(), key, value.ptr()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct list {
|
||||||
|
using key_type = int;
|
||||||
|
|
||||||
|
static handle get(handle obj, int key) { return py_list_getitem(obj.ptr(), key); }
|
||||||
|
|
||||||
|
static void set(handle obj, int key, handle value) { py_list_setitem(obj.ptr(), key, value.ptr()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Key>
|
||||||
|
struct dict {
|
||||||
|
using key_type = Key;
|
||||||
|
|
||||||
|
static handle get(handle obj, int key) { return get(obj, int_(key)); }
|
||||||
|
|
||||||
|
static handle get(handle obj, name key) { return get(obj, str(key)); }
|
||||||
|
|
||||||
|
static handle get(handle obj, handle key) {
|
||||||
|
raise_call<py_dict_getitem>(obj.ptr(), key.ptr());
|
||||||
|
return py_retval();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set(handle obj, int key, handle value) { set(obj, int_(key), value); }
|
||||||
|
|
||||||
|
static void set(handle obj, name key, handle value) { set(obj, str(key), value); }
|
||||||
|
|
||||||
|
static void set(handle obj, handle key, handle value) {
|
||||||
|
raise_call<py_dict_setitem>(obj.ptr(), key.ptr(), value.ptr());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace policy
|
||||||
|
|
||||||
|
// implement other methods of interface
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline attr_accessor interface<Derived>::attr(name key) const {
|
||||||
|
return {ptr(), key};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline item_accessor<int> interface<Derived>::operator[] (int key) const {
|
||||||
|
return {ptr(), key};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline item_accessor<name> interface<Derived>::operator[] (name key) const {
|
||||||
|
return {ptr(), key};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
inline item_accessor<handle> interface<Derived>::operator[] (handle key) const {
|
||||||
|
return {ptr(), key};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
object str::format(Args&&... args) {
|
||||||
|
return attr("format")(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline tuple_accessor tuple::operator[] (int index) const { return {m_ptr, index}; }
|
||||||
|
|
||||||
|
inline list_accessor list::operator[] (int index) const { return {m_ptr, index}; };
|
||||||
|
|
||||||
|
inline dict_accessor<int> dict::operator[] (int key) const { return {m_ptr, key}; }
|
||||||
|
|
||||||
|
inline dict_accessor<name> dict::operator[] (name key) const { return {m_ptr, key}; }
|
||||||
|
|
||||||
|
inline dict_accessor<handle> dict::operator[] (handle key) const { return {m_ptr, key}; }
|
||||||
|
|
||||||
|
inline dict::iterator::iterator(handle h) : items(h.attr("items")()), iter(items.begin()) {}
|
||||||
|
|
||||||
|
inline std::pair<object, object> dict::iterator::operator* () const {
|
||||||
|
tuple pair = *iter;
|
||||||
|
return {borrow(pair[0]), borrow(pair[1])};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pkbind
|
@ -2,107 +2,192 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
namespace pybind11 {
|
namespace pkbind {
|
||||||
inline void exec(const char* code, handle global = {}, handle local = {}) {
|
|
||||||
vm->py_exec(code, global.ptr(), local.ptr());
|
template <typename... Args>
|
||||||
|
inline void print(Args&&... args) {
|
||||||
|
handle print = py_getbuiltin(py_name("print"));
|
||||||
|
print(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrapper for builtin functions in Python
|
inline object eval(std::string_view code, handle globals = none(), handle locals = none()) {
|
||||||
inline bool hasattr(const handle& obj, const handle& name) {
|
if(globals.is_none() && locals.is_none()) {
|
||||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
std::string src{code};
|
||||||
return vm->getattr(obj.ptr(), key, false) != nullptr;
|
raise_call<py_eval>(src.c_str(), nullptr);
|
||||||
|
return object::from_ret();
|
||||||
|
} else {
|
||||||
|
handle eval = py_getbuiltin(py_name("eval"));
|
||||||
|
return eval(str(code), globals.is_none() ? dict() : globals, locals.is_none() ? dict() : locals);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool hasattr(const handle& obj, const char* name) { return vm->getattr(obj.ptr(), name, false) != nullptr; }
|
inline object exec(std::string_view code, handle globals = none(), handle locals = none()) {
|
||||||
|
if(globals.is_none() && locals.is_none()) {
|
||||||
inline void delattr(const handle& obj, const handle& name) {
|
std::string src{code};
|
||||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
raise_call<py_exec>(src.c_str(), "exec", EXEC_MODE, nullptr);
|
||||||
vm->delattr(obj.ptr(), key);
|
return object::from_ret();
|
||||||
|
} else {
|
||||||
|
handle exec = py_getbuiltin(py_name("exec"));
|
||||||
|
return exec(str(code), globals, locals);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void delattr(const handle& obj, const char* name) { vm->delattr(obj.ptr(), name); }
|
inline object locals() {
|
||||||
|
handle locals = py_getbuiltin(py_name("locals"));
|
||||||
inline object getattr(const handle& obj, const handle& name) {
|
return locals();
|
||||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
|
||||||
return reinterpret_borrow<object>(vm->getattr(obj.ptr(), key));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline object getattr(const handle& obj, const char* name) {
|
inline object globals() {
|
||||||
return reinterpret_borrow<object>(vm->getattr(obj.ptr(), name));
|
handle globals = py_getbuiltin(py_name("globals"));
|
||||||
|
return globals();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline object getattr(const handle& obj, const handle& name, const handle& default_) {
|
inline bool hasattr(handle obj, name name) {
|
||||||
if(!hasattr(obj, name)) { return reinterpret_borrow<object>(default_); }
|
auto pc = py_peek(0);
|
||||||
return getattr(obj, name);
|
auto result = py_getattr(obj.ptr(), name.index());
|
||||||
|
if(result) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
py_clearexc(pc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline object getattr(const handle& obj, const char* name, const handle& default_) {
|
inline object getattr(handle obj, name name) {
|
||||||
if(!hasattr(obj, name)) { return reinterpret_borrow<object>(default_); }
|
raise_call<py_getattr>(obj.ptr(), name.index());
|
||||||
return getattr(obj, name);
|
return object::from_ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setattr(const handle& obj, const handle& name, const handle& value) {
|
inline void setattr(handle obj, name name, handle value) {
|
||||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
raise_call<py_setattr>(obj.ptr(), name.index(), value.ptr());
|
||||||
vm->setattr(obj.ptr(), key, value.ptr());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setattr(const handle& obj, const char* name, const handle& value) {
|
inline void delattr(handle obj, name name) { raise_call<py_delattr>(obj.ptr(), name.index()); }
|
||||||
vm->setattr(obj.ptr(), name, value.ptr());
|
|
||||||
|
inline py_i64 hash(handle obj) {
|
||||||
|
py_i64 result = 0;
|
||||||
|
raise_call<py_hash>(obj.ptr(), &result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isinstance(handle obj, type type) { return py_isinstance(obj.ptr(), type.index()); }
|
||||||
|
|
||||||
|
inline bool python_error::match(type type) const { return isinstance(m_exception.ptr(), type); }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline bool is_pyobject_v = std::is_base_of_v<object, std::decay_t<T>> || std::is_same_v<type, T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline type type::of() {
|
||||||
|
if constexpr(is_pyobject_v<T>) {
|
||||||
|
if constexpr(is_check_v<T>) {
|
||||||
|
return type(tp_object);
|
||||||
|
} else {
|
||||||
|
return type(T::type_or_check());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto it = m_type_map->find(typeid(T));
|
||||||
|
if(it != m_type_map->end()) {
|
||||||
|
return type(it->second);
|
||||||
|
} else {
|
||||||
|
// if not found, raise error
|
||||||
|
std::string msg = "can not c++ instance cast to object, type: {";
|
||||||
|
msg += type_name<T>();
|
||||||
|
msg += "} is not registered.";
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline bool isinstance(const handle& obj) {
|
inline bool type::isinstance(handle obj) {
|
||||||
pkpy::Type cls = _builtin_cast<pkpy::Type>(type::handle_of<T>().ptr());
|
if constexpr(is_pyobject_v<T>) {
|
||||||
return vm->isinstance(obj.ptr(), cls);
|
// for every python object wrapper type, there must be a `type_or_check` method.
|
||||||
|
// for some types, it returns the underlying type in pkpy, e.g., `int_` -> `tp_int`.
|
||||||
|
// for other types that may not have a corresponding type in pkpy, it returns a check function.
|
||||||
|
// e.g., `iterable` -> `[](handle h){ return hasattr(h, "iter"); }`.
|
||||||
|
auto type_or_check = T::type_or_check();
|
||||||
|
if constexpr(is_check_v<T>) {
|
||||||
|
return type_or_check(obj);
|
||||||
|
} else {
|
||||||
|
return pkbind::isinstance(obj, type(type_or_check));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return pkbind::isinstance(obj, of<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool issubclass(type derived, type base) { return py_issubclass(derived.index(), base.index()); }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline bool isinstance(handle obj) {
|
||||||
|
return type::isinstance<T>(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline bool isinstance<handle>(const handle&) = delete;
|
inline bool isinstance<handle>(handle) = delete;
|
||||||
|
|
||||||
template <>
|
|
||||||
inline bool isinstance<iterable>(const handle& obj) {
|
|
||||||
return hasattr(obj, "__iter__");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
inline bool isinstance<iterator>(const handle& obj) {
|
|
||||||
return hasattr(obj, "__iter__") && hasattr(obj, "__next__");
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isinstance(const handle& obj, const handle& type) {
|
|
||||||
return vm->isinstance(obj.ptr(), _builtin_cast<pkpy::Type>(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); }
|
|
||||||
|
|
||||||
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
|
object cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = {}) {
|
||||||
_cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) {
|
// decay_t can resolve c-array type, but remove_cv_ref_t can't.
|
||||||
using U = std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>;
|
using underlying_type = std::decay_t<T>;
|
||||||
return type_caster<U>::cast(std::forward<T>(value), policy, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
if constexpr(std::is_convertible_v<underlying_type, handle> && !is_pyobject_v<underlying_type>) {
|
||||||
object
|
return object(std::forward<T>(value), object::realloc_t{});
|
||||||
cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) {
|
} else if constexpr(is_unique_pointer_v<underlying_type>) {
|
||||||
return reinterpret_borrow<object>(_cast(std::forward<T>(value), policy, parent));
|
using pointer = typename underlying_type::pointer;
|
||||||
}
|
return type_caster<pointer>::cast(value.release(), return_value_policy::take_ownership, parent);
|
||||||
|
} 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.");
|
||||||
|
|
||||||
template <typename T>
|
// resolve for automatic policy.
|
||||||
T cast(handle obj, bool convert = false) {
|
if(policy == return_value_policy::automatic) {
|
||||||
using Caster = type_caster<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>;
|
policy = std::is_pointer_v<underlying_type> ? return_value_policy::take_ownership
|
||||||
Caster caster;
|
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
|
||||||
|
: return_value_policy::move;
|
||||||
if(caster.load(obj, convert)) {
|
} else if(policy == return_value_policy::automatic_reference) {
|
||||||
if constexpr(std::is_rvalue_reference_v<T>) {
|
policy = std::is_pointer_v<underlying_type> ? return_value_policy::reference
|
||||||
return std::move(caster.value);
|
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
|
||||||
} else {
|
: return_value_policy::move;
|
||||||
return caster.value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return type_caster<underlying_type>::cast(std::forward<T>(value), policy, parent);
|
||||||
}
|
}
|
||||||
throw std::runtime_error("Unable to cast Python instance to C++ type");
|
|
||||||
}
|
}
|
||||||
} // namespace pybind11
|
|
||||||
|
template <typename T>
|
||||||
|
T cast(handle obj, bool convert = true) {
|
||||||
|
using caster_t = type_caster<T>;
|
||||||
|
constexpr auto is_dangling_v = (std::is_reference_v<T> || is_pointer_v<T>) && caster_t::is_temporary_v;
|
||||||
|
static_assert(!is_dangling_v, "dangling reference or pointer is not allowed.");
|
||||||
|
assert(obj.ptr() != nullptr);
|
||||||
|
|
||||||
|
caster_t caster;
|
||||||
|
if(caster.load(obj, convert)) {
|
||||||
|
if constexpr(std::is_reference_v<T>) {
|
||||||
|
return caster.value();
|
||||||
|
} else {
|
||||||
|
return std::move(caster.value());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::string msg = "cast python instance to c++ failed, obj type is: {";
|
||||||
|
msg += type::of(obj).name();
|
||||||
|
msg += "}, target type is: {";
|
||||||
|
msg += type_name<T>();
|
||||||
|
msg += "}.";
|
||||||
|
throw cast_error(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
template <typename T>
|
||||||
|
T interface<Derived>::cast() const {
|
||||||
|
return pkbind::cast<T>(handle(this->ptr()), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pkbind
|
||||||
|
@ -1,166 +1,190 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "instance.h"
|
#include "instance.h"
|
||||||
#include "builtins.h"
|
|
||||||
#include "type_traits.h"
|
|
||||||
|
|
||||||
namespace pybind11 {
|
namespace pkbind {
|
||||||
|
|
||||||
using pkpy::is_floating_point_v;
|
|
||||||
using pkpy::is_integral_v;
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr inline bool is_string_v = std::is_same_v<T, char*> || std::is_same_v<T, const char*> ||
|
constexpr inline bool is_string_v =
|
||||||
std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>;
|
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>
|
/// The `type_caster` class is responsible for converting between Python objects and C++ objects.
|
||||||
constexpr bool is_pyobject_v = std::is_base_of_v<handle, T>;
|
///
|
||||||
|
/// The static method `type_caster<T>::cast(...)` is used to convert a C++ object to a Python object.
|
||||||
|
/// If the conversion fails, an exception is thrown.
|
||||||
|
///
|
||||||
|
/// The method `type_caster<T>::load(...)` is used to try to convert a Python object to a C++ object.
|
||||||
|
/// If the conversion is successful, it returns true, and you can then call `type_caster<T>::value()`
|
||||||
|
/// to access the resulting C++ object. If the conversion fails, it returns false.
|
||||||
|
///
|
||||||
|
/// NOTE: The type T could be a reference type or a pointer type. What is the lifetime of the reference or pointer?
|
||||||
|
/// It depends on the referenced type. For some types, such as bool, int, float, etc., the loaded value is stored
|
||||||
|
/// in the type_caster itself, so the lifetime of the reference is no longer than the lifetime of the type_caster
|
||||||
|
/// object. For other user-registered types, the lifetime of the reference is the same as the corresponding Python
|
||||||
|
/// object. A static variable `is_temporary_v` is used to indicate whether the loaded value is temporary or not.
|
||||||
|
|
||||||
template <typename T, typename>
|
template <typename T, typename SFINAE>
|
||||||
struct type_caster;
|
struct type_caster {
|
||||||
|
T* data;
|
||||||
|
|
||||||
|
static_assert(!std::is_pointer_v<T>, "type caster for pointer type must be specialized.");
|
||||||
|
static_assert(!std::is_reference_v<T>, "type caster for reference type must be specialized.");
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static object cast(U&& value, return_value_policy policy, handle parent) {
|
||||||
|
// TODO: support implicit cast
|
||||||
|
return instance::create(type::of<T>(), std::forward<U>(value), parent, policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if(isinstance<T>(src)) {
|
||||||
|
auto& i = *static_cast<instance*>(py_touserdata(src.ptr()));
|
||||||
|
data = &i.as<T>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& value() { return *data; }
|
||||||
|
|
||||||
|
constexpr inline static bool is_temporary_v = false;
|
||||||
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct type_caster<bool> {
|
struct type_caster<bool> {
|
||||||
bool value;
|
bool data;
|
||||||
|
|
||||||
bool load(const handle& src, bool) {
|
static object cast(bool src, return_value_policy, handle) { return bool_(src); }
|
||||||
if(isinstance<pybind11::bool_>(src)) {
|
|
||||||
value = pkpy::_py_cast<bool>(vm, src.ptr());
|
bool load(handle src, bool) {
|
||||||
|
if(isinstance<pkbind::bool_>(src)) {
|
||||||
|
data = py_tobool(src.ptr());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static handle cast(bool src, return_value_policy, handle) { return src ? vm->True : vm->False; }
|
bool& value() { return data; }
|
||||||
|
|
||||||
|
constexpr inline static bool is_temporary_v = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct type_caster<T, std::enable_if_t<is_integral_v<T>>> {
|
struct type_caster<T, std::enable_if_t<is_integer_v<T>>> {
|
||||||
T value;
|
T data;
|
||||||
|
|
||||||
bool load(const handle& src, bool convert) {
|
static object cast(T src, return_value_policy, handle) { return int_(src); }
|
||||||
if(isinstance<pybind11::int_>(src)) {
|
|
||||||
value = pkpy::_py_cast<T>(vm, src.ptr());
|
bool load(handle src, bool) {
|
||||||
|
if(isinstance<int_>(src)) {
|
||||||
|
data = static_cast<T>(py_toint(src.ptr()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
T& value() { return data; }
|
||||||
|
|
||||||
|
constexpr inline static bool is_temporary_v = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct type_caster<T, std::enable_if_t<is_floating_point_v<T>>> {
|
struct type_caster<T, std::enable_if_t<is_floating_point_v<T>>> {
|
||||||
T value;
|
T data;
|
||||||
|
|
||||||
bool load(const handle& src, bool convert) {
|
static object cast(T src, return_value_policy, handle) { return float_(src); }
|
||||||
if(isinstance<pybind11::float_>(src)) {
|
|
||||||
value = pkpy::_py_cast<T>(vm, src.ptr());
|
bool load(handle src, bool convert) {
|
||||||
|
if(isinstance<pkbind::float_>(src)) {
|
||||||
|
data = py_tofloat(src.ptr());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(convert && isinstance<pybind11::int_>(src)) {
|
if(convert && isinstance<pkbind::int_>(src)) {
|
||||||
value = pkpy::_py_cast<int64_t>(vm, src.ptr());
|
data = py_toint(src.ptr());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
T& value() { return data; }
|
||||||
|
|
||||||
|
constexpr inline static bool is_temporary_v = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct type_caster<T, std::enable_if_t<is_string_v<T>>> {
|
struct type_caster<T, std::enable_if_t<is_string_v<T>>> {
|
||||||
T value;
|
T data;
|
||||||
|
|
||||||
bool load(const handle& src, bool) {
|
template <typename U>
|
||||||
if(isinstance<pybind11::str>(src)) {
|
static object cast(U&& src, return_value_policy, handle) {
|
||||||
// FIXME: support other kinds of string
|
return str(std::forward<U>(src));
|
||||||
value = pkpy::_py_cast<std::string>(vm, src.ptr());
|
}
|
||||||
|
|
||||||
|
bool load(handle src, bool) {
|
||||||
|
if(isinstance<pkbind::str>(src)) {
|
||||||
|
data = py_tostr(src.ptr());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static handle cast(const std::string& src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
T& value() { return data; }
|
||||||
|
|
||||||
|
constexpr inline static bool is_temporary_v = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct type_caster<T, std::enable_if_t<is_pyobject_v<T>>> {
|
struct type_caster<T, std::enable_if_t<is_pyobject_v<T>>> {
|
||||||
T value;
|
T data;
|
||||||
|
|
||||||
bool load(const handle& src, bool) {
|
template <typename U>
|
||||||
|
static object cast(U&& src, return_value_policy, handle) {
|
||||||
|
return object(std::forward<U>(src));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(handle src, bool) {
|
||||||
if(isinstance<T>(src)) {
|
if(isinstance<T>(src)) {
|
||||||
value = reinterpret_borrow<T>(src);
|
data = T(src.ptr(), object::realloc_t{});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename U>
|
T& value() { return data; }
|
||||||
static handle cast(U&& src, return_value_policy, handle) {
|
|
||||||
return std::forward<U>(src);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename>
|
constexpr inline static bool is_temporary_v = true;
|
||||||
struct type_caster {
|
|
||||||
value_wrapper<T> value;
|
|
||||||
|
|
||||||
using underlying_type = std::remove_pointer_t<decltype(value.pointer)>;
|
|
||||||
|
|
||||||
bool load(handle src, bool convert) {
|
|
||||||
if(isinstance<underlying_type>(src)) {
|
|
||||||
auto& i = _builtin_cast<instance>(src);
|
|
||||||
value.pointer = &i.cast<underlying_type>();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U>
|
|
||||||
static handle cast(U&& value, return_value_policy policy, const handle& parent = handle()) {
|
|
||||||
// TODO: support implicit cast
|
|
||||||
const auto& info = typeid(underlying_type);
|
|
||||||
bool existed = vm->_cxx_typeid_map.find(info) != vm->_cxx_typeid_map.end();
|
|
||||||
if(existed) {
|
|
||||||
auto type = vm->_cxx_typeid_map[info];
|
|
||||||
return instance::create(std::forward<U>(value), type, policy, parent.ptr());
|
|
||||||
}
|
|
||||||
vm->TypeError("type not registered");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct type_caster<T, std::enable_if_t<std::is_pointer_v<T> || std::is_reference_v<T>>> {
|
struct type_caster<T, std::enable_if_t<is_pointer_v<T> || std::is_reference_v<T>>> {
|
||||||
using underlying = std::conditional_t<std::is_pointer_v<T>, std::remove_pointer_t<T>, std::remove_reference_t<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;
|
||||||
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>
|
template <typename U>
|
||||||
static handle cast(U&& value, return_value_policy policy, const handle& parent) {
|
static object cast(U&& value, return_value_policy policy, handle parent) {
|
||||||
return type_caster<underlying>::cast(std::forward<U>(value), policy, parent);
|
return type_caster<underlying>::cast(std::forward<U>(value), policy, parent);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
} // namespace pybind11
|
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) { return caster.load(src, convert); }
|
||||||
|
|
||||||
|
T value() {
|
||||||
|
if constexpr(std::is_pointer_v<T>) {
|
||||||
|
return &caster.value();
|
||||||
|
} else {
|
||||||
|
return caster.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline static bool is_temporary_v = type_caster<underlying>::is_temporary_v;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pkbind
|
||||||
|
@ -1,75 +1,93 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "cpp_function.h"
|
#include "module.h"
|
||||||
|
#include "type_traits.h"
|
||||||
|
|
||||||
namespace pybind11 {
|
namespace pkbind {
|
||||||
|
|
||||||
class module : public object {
|
struct dynamic_attr {};
|
||||||
|
|
||||||
public:
|
template <typename T, typename Base = void>
|
||||||
using object::object;
|
|
||||||
|
|
||||||
static module import(const char* name) {
|
|
||||||
if(name == std::string_view{"__main__"}) {
|
|
||||||
return module{vm->_main, true};
|
|
||||||
} else {
|
|
||||||
return module{vm->py_import(name, false), true};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// 1. inheritance
|
|
||||||
// 2. virtual function
|
|
||||||
// 3. factory function
|
|
||||||
|
|
||||||
template <typename T, typename... Others>
|
|
||||||
class class_ : public type {
|
class class_ : public type {
|
||||||
|
protected:
|
||||||
|
handle m_scope;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using type::type;
|
using type::type;
|
||||||
|
using underlying_type = T;
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
class_(const handle& scope, const char* name, Args&&... args) :
|
class_(handle scope, const char* name, const Args&... args) :
|
||||||
type(vm->new_type_object(scope.ptr(), name, vm->tp_object, false, pkpy::PyTypeInfo::Vt::get<instance>()),
|
type(py_newtype(name,
|
||||||
true) {
|
std::is_same_v<Base, void> ? tp_object : type::of<Base>().index(),
|
||||||
pkpy::PyVar mod = scope.ptr();
|
scope.ptr(),
|
||||||
mod->attr().set(name, m_ptr);
|
[](void* data) {
|
||||||
vm->_cxx_typeid_map[typeid(T)] = _builtin_cast<pkpy::Type>(m_ptr);
|
static_cast<instance*>(data)->~instance();
|
||||||
vm->bind_func(m_ptr, "__new__", -1, [](pkpy::VM* vm, pkpy::ArgsView args) {
|
})),
|
||||||
auto cls = _builtin_cast<pkpy::Type>(args[0]);
|
m_scope(scope) {
|
||||||
return instance::create<T>(cls);
|
m_type_map->try_emplace(typeid(T), this->index());
|
||||||
});
|
|
||||||
|
auto& info = type_info::of<T>();
|
||||||
|
info.name = name;
|
||||||
|
|
||||||
|
py_newfunction(
|
||||||
|
py_tpgetmagic(this->index(), py_MagicNames::__new__),
|
||||||
|
"__new__(type, *args, **kwargs)",
|
||||||
|
[](int, py_Ref stack) {
|
||||||
|
auto cls = py_offset(stack, 0);
|
||||||
|
[[maybe_unused]] auto args = py_offset(stack, 1);
|
||||||
|
[[maybe_unused]] auto kwargs = py_offset(stack, 2);
|
||||||
|
|
||||||
|
auto info = &type_info::of<T>();
|
||||||
|
int slot = ((std::is_same_v<dynamic_attr, Args> || ...) ? -1 : 0);
|
||||||
|
void* data = py_newobject(retv, steal<type>(cls).index(), slot, sizeof(instance));
|
||||||
|
new (data) instance{instance::Flag::Own, operator new (info->size), info};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
nullptr,
|
||||||
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// bind constructor
|
/// bind constructor
|
||||||
template <typename... Args, typename... Extra>
|
template <typename... Args, typename... Extra>
|
||||||
class_& def(init<Args...>, const Extra&... extra) {
|
class_& def(impl::constructor<Args...>, const Extra&... extra) {
|
||||||
if constexpr(!std::is_constructible_v<T, Args...>) {
|
if constexpr(!std::is_constructible_v<T, Args...>) {
|
||||||
static_assert(std::is_constructible_v<T, Args...>, "Invalid constructor arguments");
|
static_assert(std::is_constructible_v<T, Args...>, "Invalid constructor arguments");
|
||||||
} else {
|
} else {
|
||||||
bind_function(
|
impl::bind_function<true, false>(
|
||||||
*this,
|
*this,
|
||||||
"__init__",
|
"__init__",
|
||||||
[](T* self, Args... args) {
|
[](T* self, Args... args) {
|
||||||
new (self) T(args...);
|
new (self) T(args...);
|
||||||
},
|
},
|
||||||
pkpy::BindType::DEFAULT,
|
|
||||||
extra...);
|
extra...);
|
||||||
return *this;
|
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, false>(*this, "__init__", factory.make(), extra...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// bind member function
|
/// bind member function
|
||||||
template <typename Fn, typename... Extra>
|
template <typename Fn, typename... Extra>
|
||||||
class_& def(const char* name, Fn&& f, const Extra&... extra) {
|
class_& def(const char* name, Fn&& f, const Extra&... extra) {
|
||||||
using first = std::tuple_element_t<0, callable_args_t<remove_cvref_t<Fn>>>;
|
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_reference_v<first> && std::is_base_of_v<T, remove_cvref_t<first>>;
|
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) {
|
if constexpr(!is_first_base_of_v) {
|
||||||
static_assert(is_first_base_of_v,
|
static_assert(
|
||||||
"If you want to bind member function, the first argument must be the base class");
|
is_first_base_of_v,
|
||||||
|
"If you want to bind member function, the first argument must be the base class");
|
||||||
} else {
|
} else {
|
||||||
bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::DEFAULT, extra...);
|
impl::bind_function<true, false>(*this, name, std::forward<Fn>(f), extra...);
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
@ -82,21 +100,29 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: factory function
|
|
||||||
|
|
||||||
/// bind static function
|
/// bind static function
|
||||||
template <typename Fn, typename... Extra>
|
template <typename Fn, typename... Extra>
|
||||||
class_& def_static(const char* name, Fn&& f, const Extra&... extra) {
|
class_& def_static(const char* name, Fn&& f, const Extra&... extra) {
|
||||||
bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::STATICMETHOD, extra...);
|
impl::bind_function<false, true>(*this, name, std::forward<Fn>(f), extra...);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename MP, typename... Extras>
|
template <typename MP, typename... Extras>
|
||||||
class_& def_readwrite(const char* name, MP mp, const Extras&... extras) {
|
class_& def_readwrite(const char* name, MP mp, const Extras&... extras) {
|
||||||
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
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");
|
static_assert(std::is_member_object_pointer_v<MP>,
|
||||||
|
"def_readwrite only supports pointer to data member");
|
||||||
} else {
|
} else {
|
||||||
bind_property(*this, name, mp, mp, extras...);
|
impl::bind_property(
|
||||||
|
*this,
|
||||||
|
name,
|
||||||
|
[mp](class_type_t<MP>& self) -> auto& {
|
||||||
|
return self.*mp;
|
||||||
|
},
|
||||||
|
[mp](class_type_t<MP>& self, const member_type_t<MP>& value) {
|
||||||
|
self.*mp = value;
|
||||||
|
},
|
||||||
|
extras...);
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -104,22 +130,34 @@ public:
|
|||||||
template <typename MP, typename... Extras>
|
template <typename MP, typename... Extras>
|
||||||
class_& def_readonly(const char* name, MP mp, const Extras&... extras) {
|
class_& def_readonly(const char* name, MP mp, const Extras&... extras) {
|
||||||
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
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");
|
static_assert(std::is_member_object_pointer_v<MP>,
|
||||||
|
"def_readonly only supports pointer to data member");
|
||||||
} else {
|
} else {
|
||||||
bind_property(*this, name, mp, nullptr, extras...);
|
impl::bind_property(
|
||||||
|
*this,
|
||||||
|
name,
|
||||||
|
[mp](class_type_t<MP>& self) -> auto& {
|
||||||
|
return self.*mp;
|
||||||
|
},
|
||||||
|
nullptr,
|
||||||
|
extras...);
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Getter, typename Setter, typename... Extras>
|
template <typename Getter, typename Setter, typename... Extras>
|
||||||
class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
||||||
bind_property(*this, name, std::forward<Getter>(g), std::forward<Setter>(s), extras...);
|
impl::bind_property(*this,
|
||||||
|
name,
|
||||||
|
std::forward<Getter>(g),
|
||||||
|
std::forward<Setter>(s),
|
||||||
|
extras...);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Getter, typename... Extras>
|
template <typename Getter, typename... Extras>
|
||||||
class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) {
|
class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) {
|
||||||
bind_property(*this, name, std::forward<Getter>(mp), nullptr, extras...);
|
impl::bind_property(*this, name, std::forward<Getter>(mp), nullptr, extras...);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,28 +188,41 @@ public:
|
|||||||
|
|
||||||
template <typename T, typename... Others>
|
template <typename T, typename... Others>
|
||||||
class enum_ : public class_<T, Others...> {
|
class enum_ : public class_<T, Others...> {
|
||||||
std::map<const char*, pkpy::PyVar> m_values;
|
std::vector<std::pair<const char*, object>> m_values;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using class_<T, Others...>::class_;
|
using Base = class_<T, Others...>;
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
enum_(const handle& scope, const char* name, Args&&... args) :
|
enum_(const handle& scope, const char* name, Args&&... args) :
|
||||||
class_<T, Others...>(scope, name, std::forward<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) {
|
enum_& value(const char* name, T value) {
|
||||||
handle var = type_caster<T>::cast(value, return_value_policy::copy);
|
auto var = pkbind::cast(value, return_value_policy::copy);
|
||||||
this->m_ptr->attr().set(name, var.ptr());
|
setattr(*this, name, var);
|
||||||
m_values[name] = var.ptr();
|
m_values.emplace_back(name, std::move(var));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum_& export_values() {
|
enum_& export_values() {
|
||||||
pkpy::PyVar mod = this->m_ptr->attr("__module__");
|
|
||||||
for(auto& [name, value]: m_values) {
|
for(auto& [name, value]: m_values) {
|
||||||
mod->attr().set(name, value);
|
setattr(Base::m_scope, name, value);
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace pybind11
|
|
||||||
|
} // namespace pkbind
|
||||||
|
@ -1,358 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "cast.h"
|
|
||||||
#include <bitset>
|
|
||||||
|
|
||||||
namespace pybind11 {
|
|
||||||
|
|
||||||
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");
|
|
||||||
};
|
|
||||||
|
|
||||||
// append the overload to the beginning of the overload list
|
|
||||||
struct prepend {};
|
|
||||||
|
|
||||||
template <typename... Args>
|
|
||||||
struct init {};
|
|
||||||
|
|
||||||
// TODO: support more customized tags
|
|
||||||
// struct kw_only {};
|
|
||||||
//
|
|
||||||
// struct pos_only {};
|
|
||||||
//
|
|
||||||
// struct default_arg {};
|
|
||||||
//
|
|
||||||
// struct arg {
|
|
||||||
// const char* name;
|
|
||||||
// const char* description;
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// struct default_arg {
|
|
||||||
// const char* name;
|
|
||||||
// const char* description;
|
|
||||||
// const char* value;
|
|
||||||
// };
|
|
||||||
|
|
||||||
template <typename Fn,
|
|
||||||
typename Extra,
|
|
||||||
typename Args = callable_args_t<std::decay_t<Fn>>,
|
|
||||||
typename IndexSequence = std::make_index_sequence<std::tuple_size_v<Args>>>
|
|
||||||
struct generator;
|
|
||||||
|
|
||||||
class function_record {
|
|
||||||
union {
|
|
||||||
void* data;
|
|
||||||
char buffer[16];
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: optimize the function_record size to reduce memory usage
|
|
||||||
const char* name;
|
|
||||||
function_record* next;
|
|
||||||
void (*destructor)(function_record*);
|
|
||||||
return_value_policy policy = return_value_policy::automatic;
|
|
||||||
handle (*wrapper)(function_record&, pkpy::ArgsView, bool convert, handle parent);
|
|
||||||
|
|
||||||
template <typename Fn, typename Extra, typename Args, typename IndexSequence>
|
|
||||||
friend struct generator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename Fn, typename... Extras>
|
|
||||||
function_record(Fn&& f, const char* name, const Extras&... extras) : name(name), next(nullptr) {
|
|
||||||
|
|
||||||
if constexpr(sizeof(f) <= sizeof(buffer)) {
|
|
||||||
new (buffer) auto(std::forward<Fn>(f));
|
|
||||||
destructor = [](function_record* self) {
|
|
||||||
reinterpret_cast<Fn*>(self->buffer)->~Fn();
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
data = new auto(std::forward<Fn>(f));
|
|
||||||
destructor = [](function_record* self) {
|
|
||||||
delete static_cast<Fn*>(self->data);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
using Generator = generator<std::decay_t<Fn>, std::tuple<Extras...>>;
|
|
||||||
Generator::initialize(*this, extras...);
|
|
||||||
wrapper = Generator::generate();
|
|
||||||
}
|
|
||||||
|
|
||||||
~function_record() { destructor(this); }
|
|
||||||
|
|
||||||
template <typename Fn>
|
|
||||||
auto& cast() {
|
|
||||||
if constexpr(sizeof(Fn) <= sizeof(buffer)) {
|
|
||||||
return *reinterpret_cast<Fn*>(buffer);
|
|
||||||
} else {
|
|
||||||
return *static_cast<Fn*>(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void append(function_record* record) {
|
|
||||||
function_record* p = this;
|
|
||||||
while(p->next != nullptr) {
|
|
||||||
p = p->next;
|
|
||||||
}
|
|
||||||
p->next = record;
|
|
||||||
}
|
|
||||||
|
|
||||||
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(*this, 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(*this, view, true, {});
|
|
||||||
if(result) { return result; }
|
|
||||||
p = p->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
vm->TypeError("no matching function found");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Fn, std::size_t... Is, typename... Args>
|
|
||||||
handle invoke(Fn&& fn,
|
|
||||||
std::index_sequence<Is...>,
|
|
||||||
std::tuple<type_caster<Args>...>& casters,
|
|
||||||
return_value_policy policy,
|
|
||||||
handle parent) {
|
|
||||||
using underlying_type = std::decay_t<Fn>;
|
|
||||||
using ret = callable_return_t<underlying_type>;
|
|
||||||
|
|
||||||
// if the return type is void, return None
|
|
||||||
if constexpr(std::is_void_v<ret>) {
|
|
||||||
// resolve the member function pointer
|
|
||||||
if constexpr(std::is_member_function_pointer_v<underlying_type>) {
|
|
||||||
[&](class_type_t<underlying_type>& self, auto&... args) {
|
|
||||||
(self.*fn)(args...);
|
|
||||||
}(std::get<Is>(casters).value...);
|
|
||||||
} else {
|
|
||||||
fn(std::get<Is>(casters).value...);
|
|
||||||
}
|
|
||||||
return vm->None;
|
|
||||||
} else {
|
|
||||||
// resolve the member function pointer
|
|
||||||
if constexpr(std::is_member_function_pointer_v<remove_cvref_t<Fn>>) {
|
|
||||||
return type_caster<ret>::cast(
|
|
||||||
[&](class_type_t<underlying_type>& self, auto&... args) {
|
|
||||||
return (self.*fn)(args...);
|
|
||||||
}(std::get<Is>(casters).value...),
|
|
||||||
policy,
|
|
||||||
parent);
|
|
||||||
} else {
|
|
||||||
return type_caster<ret>::cast(fn(std::get<Is>(casters).value...), policy, parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Fn, typename... Args, std::size_t... Is, typename... Extras>
|
|
||||||
struct generator<Fn, std::tuple<Extras...>, std::tuple<Args...>, std::index_sequence<Is...>> {
|
|
||||||
static void initialize(function_record& record, const Extras&... extras) {}
|
|
||||||
|
|
||||||
static auto generate() {
|
|
||||||
return +[](function_record& self, pkpy::ArgsView view, bool convert, handle parent) {
|
|
||||||
// FIXME:
|
|
||||||
// Temporarily, args and kwargs must be at the end of the arguments list
|
|
||||||
// Named arguments are not supported yet
|
|
||||||
constexpr bool has_args = types_count_v<args, remove_cvref_t<Args>...> != 0;
|
|
||||||
constexpr bool has_kwargs = types_count_v<kwargs, remove_cvref_t<Args>...> != 0;
|
|
||||||
constexpr std::size_t count = sizeof...(Args) - has_args - has_kwargs;
|
|
||||||
|
|
||||||
handle stack[sizeof...(Args)] = {};
|
|
||||||
|
|
||||||
// initialize the stack
|
|
||||||
|
|
||||||
if(!has_args && (view.size() != count)) { return handle(); }
|
|
||||||
|
|
||||||
if(has_args && (view.size() < count)) { return handle(); }
|
|
||||||
|
|
||||||
for(std::size_t i = 0; i < count; ++i) {
|
|
||||||
stack[i] = view[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// pack the args and kwargs
|
|
||||||
if constexpr(has_args) {
|
|
||||||
const auto n = view.size() - count;
|
|
||||||
pkpy::PyVar var = vm->new_object<pkpy::Tuple>(vm->tp_tuple, n);
|
|
||||||
auto& tuple = var.obj_get<pkpy::Tuple>();
|
|
||||||
for(std::size_t i = 0; i < n; ++i) {
|
|
||||||
tuple[i] = view[count + i];
|
|
||||||
}
|
|
||||||
stack[count] = var;
|
|
||||||
}
|
|
||||||
|
|
||||||
if constexpr(has_kwargs) {
|
|
||||||
const auto n = vm->s_data._sp - view.end();
|
|
||||||
pkpy::PyVar var = vm->new_object<pkpy::Dict>(vm->tp_dict);
|
|
||||||
auto& dict = var.obj_get<pkpy::Dict>();
|
|
||||||
|
|
||||||
for(std::size_t i = 0; i < n; i += 2) {
|
|
||||||
pkpy::i64 index = pkpy::_py_cast<pkpy::i64>(vm, view[count + i]);
|
|
||||||
pkpy::PyVar str = vm->new_object<pkpy::Str>(vm->tp_str, pkpy::StrName(index).sv());
|
|
||||||
dict.set(vm, str, view[count + i + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
stack[count + 1] = var;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if all the arguments are not valid
|
|
||||||
for(std::size_t i = 0; i < sizeof...(Args); ++i) {
|
|
||||||
if(!stack[i]) { return handle(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// ok, all the arguments are valid, call the function
|
|
||||||
std::tuple<type_caster<Args>...> casters;
|
|
||||||
|
|
||||||
// check type compatibility
|
|
||||||
if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
|
|
||||||
return invoke(self.cast<Fn>(), std::index_sequence<Is...>{}, casters, self.policy, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return handle();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr inline static auto _wrapper = +[](pkpy::VM*, pkpy::ArgsView view) {
|
|
||||||
auto& record = pkpy::lambda_get_userdata<function_record>(view.begin());
|
|
||||||
return record(view).ptr();
|
|
||||||
};
|
|
||||||
|
|
||||||
class cpp_function : public function {
|
|
||||||
public:
|
|
||||||
template <typename Fn, typename... Extras>
|
|
||||||
cpp_function(Fn&& f, const Extras&... extras) {
|
|
||||||
pkpy::any userdata = function_record(std::forward<Fn>(f), "anonymous", extras...);
|
|
||||||
m_ptr = vm->bind_func(nullptr, "", -1, _wrapper, std::move(userdata));
|
|
||||||
inc_ref();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <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();
|
|
||||||
pkpy::PyVar callable = var->attr().try_get(name);
|
|
||||||
|
|
||||||
// if the function is not bound yet, bind it
|
|
||||||
if(!callable) {
|
|
||||||
pkpy::any userdata = function_record(std::forward<Fn>(fn), name, extras...);
|
|
||||||
callable = vm->bind_func(var, name, -1, _wrapper, std::move(userdata));
|
|
||||||
} else {
|
|
||||||
auto& userdata = callable.obj_get<pkpy::NativeFunc>()._userdata;
|
|
||||||
function_record* record = new function_record(std::forward<Fn>(fn), name, extras...);
|
|
||||||
|
|
||||||
constexpr bool is_prepend = (types_count_v<prepend, Extras...> != 0);
|
|
||||||
if constexpr(is_prepend) {
|
|
||||||
// if prepend is specified, append the new record to the beginning of the list
|
|
||||||
function_record* last = (function_record*)userdata.data;
|
|
||||||
userdata.data = record;
|
|
||||||
record->append(last);
|
|
||||||
} else {
|
|
||||||
// otherwise, append the new record to the end of the list
|
|
||||||
function_record* last = (function_record*)userdata.data;
|
|
||||||
last->append(record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return callable;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Getter_, typename Setter_, typename... Extras>
|
|
||||||
handle
|
|
||||||
bind_property(const handle& obj, const char* name, Getter_&& getter_, Setter_&& setter_, const Extras&... extras) {
|
|
||||||
pkpy::PyVar var = obj.ptr();
|
|
||||||
pkpy::PyVar getter = vm->None;
|
|
||||||
pkpy::PyVar setter = vm->None;
|
|
||||||
using Getter = std::decay_t<Getter_>;
|
|
||||||
using Setter = std::decay_t<Setter_>;
|
|
||||||
|
|
||||||
getter = vm->new_object<pkpy::NativeFunc>(
|
|
||||||
vm->tp_native_func,
|
|
||||||
[](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
|
|
||||||
auto& getter = pkpy::lambda_get_userdata<Getter>(view.begin());
|
|
||||||
|
|
||||||
if constexpr(std::is_member_pointer_v<Getter>) {
|
|
||||||
using Self = class_type_t<Getter>;
|
|
||||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
|
||||||
|
|
||||||
if constexpr(std::is_member_object_pointer_v<Getter>) {
|
|
||||||
return type_caster<member_type_t<Getter>>::cast(self.*getter,
|
|
||||||
return_value_policy::reference_internal,
|
|
||||||
view[0])
|
|
||||||
.ptr();
|
|
||||||
} else {
|
|
||||||
return type_caster<callable_return_t<Getter>>::cast((self.*getter)(),
|
|
||||||
return_value_policy::reference_internal,
|
|
||||||
view[0])
|
|
||||||
.ptr();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
using Self = std::tuple_element_t<0, callable_args_t<Getter>>;
|
|
||||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
|
||||||
|
|
||||||
return type_caster<callable_return_t<Getter>>::cast(getter(self),
|
|
||||||
return_value_policy::reference_internal,
|
|
||||||
view[0])
|
|
||||||
.ptr();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
1,
|
|
||||||
std::forward<Getter_>(getter_));
|
|
||||||
|
|
||||||
if constexpr(!std::is_same_v<Setter, std::nullptr_t>) {
|
|
||||||
setter = vm->new_object<pkpy::NativeFunc>(
|
|
||||||
vm->tp_native_func,
|
|
||||||
[](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
|
|
||||||
auto& setter = pkpy::lambda_get_userdata<Setter>(view.begin());
|
|
||||||
|
|
||||||
if constexpr(std::is_member_pointer_v<Setter>) {
|
|
||||||
using Self = class_type_t<Setter>;
|
|
||||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
|
||||||
|
|
||||||
if constexpr(std::is_member_object_pointer_v<Setter>) {
|
|
||||||
type_caster<member_type_t<Setter>> caster;
|
|
||||||
if(caster.load(view[1], true)) {
|
|
||||||
self.*setter = caster.value;
|
|
||||||
return vm->None;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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 {
|
|
||||||
using Self = std::tuple_element_t<0, callable_args_t<Setter>>;
|
|
||||||
auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
|
|
||||||
|
|
||||||
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("invalid argument");
|
|
||||||
},
|
|
||||||
2,
|
|
||||||
std::forward<Setter_>(setter_));
|
|
||||||
}
|
|
||||||
|
|
||||||
pkpy::PyVar property = vm->new_object<pkpy::Property>(vm->tp_property, getter, setter);
|
|
||||||
var->attr().set(name, property);
|
|
||||||
return property;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pybind11
|
|
115
include/pybind11/internal/error.h
Normal file
115
include/pybind11/internal/error.h
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "object.h"
|
||||||
|
|
||||||
|
namespace pkbind {
|
||||||
|
|
||||||
|
/// represent a all exception raised by python.
|
||||||
|
class python_error : public std::exception {
|
||||||
|
public:
|
||||||
|
python_error(char* what, object exception) : m_what(what), m_exception(std::move(exception)) {}
|
||||||
|
|
||||||
|
const char* what() const noexcept override { return m_what; }
|
||||||
|
|
||||||
|
// get the python exception object
|
||||||
|
object& exception() { return m_exception; }
|
||||||
|
|
||||||
|
~python_error() { std::free(m_what); }
|
||||||
|
|
||||||
|
bool match(py_Type type) const { return py_isinstance(m_exception.ptr(), type); }
|
||||||
|
|
||||||
|
bool match(class type) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
char* m_what;
|
||||||
|
object m_exception;
|
||||||
|
};
|
||||||
|
|
||||||
|
using error_already_set = python_error;
|
||||||
|
|
||||||
|
template <auto Fn, typename... Args>
|
||||||
|
inline auto raise_call(Args&&... args) {
|
||||||
|
auto pc = py_peek(0);
|
||||||
|
auto result = Fn(std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
using type = decltype(result);
|
||||||
|
if constexpr(std::is_same_v<type, bool>) {
|
||||||
|
if(result != false) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else if constexpr(std::is_same_v<type, int>) {
|
||||||
|
if(result != -1) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
static_assert(dependent_false<type>, "invalid return type");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool o = py_matchexc(tp_Exception);
|
||||||
|
object e = object::from_ret();
|
||||||
|
auto what = py_formatexc();
|
||||||
|
py_clearexc(pc);
|
||||||
|
throw python_error(what, std::move(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
class stop_iteration {};
|
||||||
|
|
||||||
|
class cast_error : public std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class index_error : public std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class key_error : public std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class value_error : public std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class type_error : public std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class import_error : public std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class attribute_error : public std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline object::operator bool () const { return raise_call<py_bool>(m_ptr); }
|
||||||
|
|
||||||
|
#define PKBIND_BINARY_OPERATOR(name, lop, rop) \
|
||||||
|
inline object operator name (handle lhs, handle rhs) { \
|
||||||
|
raise_call<py_binaryop>(lhs.ptr(), rhs.ptr(), lop, rop); \
|
||||||
|
return object(retv, object::realloc_t{}); \
|
||||||
|
}
|
||||||
|
|
||||||
|
PKBIND_BINARY_OPERATOR(==, __eq__, __eq__)
|
||||||
|
PKBIND_BINARY_OPERATOR(!=, __ne__, __ne__)
|
||||||
|
PKBIND_BINARY_OPERATOR(<, __lt__, __gt__)
|
||||||
|
PKBIND_BINARY_OPERATOR(<=, __le__, __ge__)
|
||||||
|
PKBIND_BINARY_OPERATOR(>, __gt__, __lt__)
|
||||||
|
PKBIND_BINARY_OPERATOR(>=, __ge__, __le__)
|
||||||
|
|
||||||
|
PKBIND_BINARY_OPERATOR(+, __add__, __radd__)
|
||||||
|
PKBIND_BINARY_OPERATOR(-, __sub__, __rsub__)
|
||||||
|
PKBIND_BINARY_OPERATOR(*, __mul__, __rmul__)
|
||||||
|
PKBIND_BINARY_OPERATOR(/, __truediv__, __rtruediv__)
|
||||||
|
PKBIND_BINARY_OPERATOR(%, __mod__, __rmod__)
|
||||||
|
|
||||||
|
// FIXME: support __rand__ ...
|
||||||
|
PKBIND_BINARY_OPERATOR(&, __and__, 0)
|
||||||
|
PKBIND_BINARY_OPERATOR(|, __or__, 0)
|
||||||
|
PKBIND_BINARY_OPERATOR(^, __xor__, 0)
|
||||||
|
PKBIND_BINARY_OPERATOR(<<, __lshift__, 0)
|
||||||
|
PKBIND_BINARY_OPERATOR(>>, __rshift__, 0)
|
||||||
|
|
||||||
|
#undef PKBIND_BINARY_OPERATOR
|
||||||
|
|
||||||
|
} // namespace pkbind
|
686
include/pybind11/internal/function.h
Normal file
686
include/pybind11/internal/function.h
Normal file
@ -0,0 +1,686 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cast.h"
|
||||||
|
|
||||||
|
namespace pkbind {
|
||||||
|
|
||||||
|
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)};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct arg_with_default {
|
||||||
|
const char* name;
|
||||||
|
object value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct arg {
|
||||||
|
const char* name;
|
||||||
|
|
||||||
|
arg(const char* name) : name(name) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
arg_with_default operator= (T&& value) {
|
||||||
|
return arg_with_default{name, cast(std::forward<T>(value))};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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{handle(this->ptr())};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
template <return_value_policy policy, typename... Args>
|
||||||
|
object interface<Derived>::operator() (Args&&... args) const {
|
||||||
|
py_push(ptr());
|
||||||
|
py_pushnil();
|
||||||
|
|
||||||
|
int argc = 0;
|
||||||
|
int kwargsc = 0;
|
||||||
|
|
||||||
|
auto foreach = [&](auto&& argument) {
|
||||||
|
using type = std::decay_t<decltype(argument)>;
|
||||||
|
if constexpr(std::is_constructible_v<handle, type>) {
|
||||||
|
argc += 1;
|
||||||
|
py_push(handle(argument).ptr());
|
||||||
|
} else if constexpr(std::is_same_v<type, arg_with_default>) {
|
||||||
|
kwargsc += 1;
|
||||||
|
arg_with_default& default_ = argument;
|
||||||
|
py_pushname(name(default_.name).index());
|
||||||
|
py_push(default_.value.ptr());
|
||||||
|
} else if constexpr(std::is_same_v<type, args_proxy>) {
|
||||||
|
tuple args = argument.value.template cast<tuple>();
|
||||||
|
for(auto arg: args) {
|
||||||
|
argc += 1;
|
||||||
|
py_push(arg.ptr());
|
||||||
|
}
|
||||||
|
} else if constexpr(std::is_same_v<type, kwargs_proxy>) {
|
||||||
|
dict kwargs = argument.value.template cast<dict>();
|
||||||
|
kwargs.apply([&](handle key, handle value) {
|
||||||
|
kwargsc += 1;
|
||||||
|
name name = key.cast<std::string_view>();
|
||||||
|
py_pushname(name.index());
|
||||||
|
py_push(value.ptr());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
argc += 1;
|
||||||
|
py_push(pkbind::cast(std::forward<decltype(argument)>(argument), policy).ptr());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(foreach(std::forward<Args>(args)), ...);
|
||||||
|
|
||||||
|
raise_call<py_vectorcall>(argc, kwargsc);
|
||||||
|
|
||||||
|
return object::from_ret();
|
||||||
|
}
|
||||||
|
|
||||||
|
class function : public object {
|
||||||
|
PKBIND_TYPE_IMPL(object, function, tp_function);
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace 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 {
|
||||||
|
template <typename C, typename E, typename A, typename I>
|
||||||
|
friend struct template_parser;
|
||||||
|
|
||||||
|
using destructor_t = void (*)(function_record*);
|
||||||
|
using wrapper_t = bool (*)(function_record&,
|
||||||
|
std::vector<handle>&,
|
||||||
|
std::vector<std::pair<handle, handle>>&,
|
||||||
|
bool convert,
|
||||||
|
handle parent);
|
||||||
|
|
||||||
|
struct arguments_t {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
std::vector<object> defaults;
|
||||||
|
};
|
||||||
|
|
||||||
|
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::call;
|
||||||
|
}
|
||||||
|
|
||||||
|
function_record(const function_record&) = delete;
|
||||||
|
|
||||||
|
function_record& operator= (const 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& operator= (function_record&&) = delete;
|
||||||
|
|
||||||
|
~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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function_record& from(handle h) {
|
||||||
|
auto slot = py_getslot(h.ptr(), 0);
|
||||||
|
return *static_cast<function_record*>(py_touserdata(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator() (int argc, handle stack) {
|
||||||
|
function_record* p = this;
|
||||||
|
|
||||||
|
bool has_self = argc == 3;
|
||||||
|
std::vector<handle> args;
|
||||||
|
handle self = py_offset(stack.ptr(), 0);
|
||||||
|
if(has_self) { args.push_back(self); }
|
||||||
|
|
||||||
|
auto tuple = py_offset(stack.ptr(), 0 + has_self);
|
||||||
|
for(int i = 0; i < py_tuple_len(tuple); ++i) {
|
||||||
|
args.push_back(py_tuple_getitem(tuple, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dict = steal<pkbind::dict>(py_offset(stack.ptr(), 1 + has_self));
|
||||||
|
|
||||||
|
std::vector<std::pair<handle, handle>> kwargs;
|
||||||
|
dict.apply([&](handle key, handle value) {
|
||||||
|
kwargs.emplace_back(key, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// foreach function record and call the function with not convert
|
||||||
|
while(p != nullptr) {
|
||||||
|
auto result = p->wrapper(*p, args, kwargs, false, self);
|
||||||
|
if(result) { return; }
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = this;
|
||||||
|
// foreach function record and call the function with convert
|
||||||
|
while(p != nullptr) {
|
||||||
|
auto result = p->wrapper(*p, args, kwargs, true, self);
|
||||||
|
if(result) { return; }
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string msg = "no matching function found, function signature:\n";
|
||||||
|
p = this;
|
||||||
|
while(p != nullptr) {
|
||||||
|
msg += " ";
|
||||||
|
msg += p->signature;
|
||||||
|
msg += "\n";
|
||||||
|
p = p->next;
|
||||||
|
}
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Fn, std::size_t... Is, typename... Args>
|
||||||
|
void invoke(Fn&& fn,
|
||||||
|
std::index_sequence<Is...>,
|
||||||
|
std::tuple<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) {
|
||||||
|
py_assign(py_retval(),
|
||||||
|
pkbind::cast(unpack(std::get<Is>(casters).value()...), policy, parent).ptr());
|
||||||
|
} else {
|
||||||
|
unpack(std::get<Is>(casters).value()...);
|
||||||
|
py_newnone(py_retval());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if constexpr(!is_void) {
|
||||||
|
py_assign(py_retval(),
|
||||||
|
pkbind::cast(fn(std::get<Is>(casters).value()...), policy, parent).ptr());
|
||||||
|
} else {
|
||||||
|
fn(std::get<Is>(casters).value()...);
|
||||||
|
py_newnone(py_retval());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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...>> {
|
||||||
|
using types = type_list<Args...>;
|
||||||
|
|
||||||
|
/// count of the Callable parameters.
|
||||||
|
constexpr inline static auto argc = types::size;
|
||||||
|
|
||||||
|
// count the number of py::args and py::kwargs
|
||||||
|
constexpr inline static auto args_count = types::template count<pkbind::args>;
|
||||||
|
constexpr inline static auto kwargs_count = types::template count<pkbind::kwargs>;
|
||||||
|
static_assert(args_count <= 1, "py::args can occur at most once");
|
||||||
|
static_assert(kwargs_count <= 1, "py::kwargs can occur at most once");
|
||||||
|
|
||||||
|
/// find the position of py::args and py::kwargs
|
||||||
|
constexpr inline static auto args_pos = types::template find<pkbind::args>;
|
||||||
|
constexpr inline static auto kwargs_pos = types::template find<pkbind::kwargs>;
|
||||||
|
|
||||||
|
// FIXME: temporarily, args and kwargs must be at the end of the arguments list
|
||||||
|
/// if have py::kwargs, it must be at the end of the arguments list.
|
||||||
|
static_assert(kwargs_count == 0 || kwargs_pos == argc - 1,
|
||||||
|
"py::kwargs must be the last parameter");
|
||||||
|
/// if have py::args, it must be before py::kwargs or at the end of the arguments list.
|
||||||
|
static_assert(args_count == 0 || args_pos == kwargs_pos - 1 || args_pos == argc - 1,
|
||||||
|
"py::args must be before py::kwargs or at the end of the parameter list");
|
||||||
|
|
||||||
|
using extras = type_list<Extras...>;
|
||||||
|
|
||||||
|
// count the number of py::doc and py::return_value_policy
|
||||||
|
constexpr inline static auto doc_count = extras::template count<const char*>;
|
||||||
|
constexpr inline static auto policy_count = extras::template count<pkbind::return_value_policy>;
|
||||||
|
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 inline static auto policy_pos = extras::template find<pkbind::return_value_policy>;
|
||||||
|
|
||||||
|
constexpr inline static auto last_arg_without_default_pos =
|
||||||
|
types::template find_last<pkbind::arg>;
|
||||||
|
constexpr inline static auto first_arg_with_default_pos =
|
||||||
|
types::template find<pkbind::arg_with_default>;
|
||||||
|
static_assert(last_arg_without_default_pos < first_arg_with_default_pos ||
|
||||||
|
first_arg_with_default_pos == -1,
|
||||||
|
"parameter with default value must be after parameter without default value");
|
||||||
|
|
||||||
|
/// count of named parameters(explicit with name).
|
||||||
|
constexpr inline static auto named_only_argc = extras::template count<pkbind::arg>;
|
||||||
|
constexpr inline static auto named_default_argc =
|
||||||
|
extras::template count<pkbind::arg_with_default>;
|
||||||
|
constexpr inline static auto named_argc = named_only_argc + named_default_argc;
|
||||||
|
|
||||||
|
/// count of normal parameters(which are not py::args or py::kwargs).
|
||||||
|
constexpr inline static auto normal_argc = argc - (args_pos != -1) - (kwargs_pos != -1);
|
||||||
|
|
||||||
|
/// all parameters must either have no names or all must have names.
|
||||||
|
static_assert(named_argc == 0 || named_argc == normal_argc,
|
||||||
|
"all parameters must either have no names or all must have names.");
|
||||||
|
|
||||||
|
static void initialize(function_record& record, const Extras&... extras) {
|
||||||
|
auto extras_tuple = std::make_tuple(extras...);
|
||||||
|
constexpr static bool has_named_args = (named_argc > 0);
|
||||||
|
if constexpr(policy_pos != -1) { record.policy = std::get<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& default_) {
|
||||||
|
using type = remove_cvref_t<decltype(default_)>;
|
||||||
|
if constexpr(std::is_same_v<arg, type>) {
|
||||||
|
auto& arguments = *record.arguments;
|
||||||
|
arguments.names.emplace_back(default_.name);
|
||||||
|
arguments.defaults.emplace_back();
|
||||||
|
} else if constexpr(std::is_same_v<arg_with_default, type>) {
|
||||||
|
auto& arguments = *record.arguments;
|
||||||
|
arguments.names.emplace_back(default_.name);
|
||||||
|
arguments.defaults.emplace_back(std::move(default_.value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(add_arguments(extras), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set signature
|
||||||
|
{
|
||||||
|
std::string sig = "(";
|
||||||
|
std::size_t index = 0;
|
||||||
|
auto append = [&](auto _t) {
|
||||||
|
using T = remove_cvref_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].empty()) {
|
||||||
|
sig += " = ";
|
||||||
|
sig += record.arguments->defaults[index]
|
||||||
|
.attr("__repr__")()
|
||||||
|
.cast<std::string_view>();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sig += "_: ";
|
||||||
|
sig += type_info::of<T>().name;
|
||||||
|
}
|
||||||
|
if(index + 1 < 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// try to call a C++ function(store in function_record) with the arguments which are from
|
||||||
|
/// Python. if success, return true, otherwise return false.
|
||||||
|
static bool call(function_record& record,
|
||||||
|
std::vector<handle>& args,
|
||||||
|
std::vector<std::pair<handle, handle>>& kwargs,
|
||||||
|
bool convert,
|
||||||
|
handle parent) {
|
||||||
|
// first, we try to load arguments into the stack.
|
||||||
|
// use argc + 1 to avoid compile error when argc is 0.
|
||||||
|
handle stack[argc + 1] = {};
|
||||||
|
|
||||||
|
// if have default arguments, load them
|
||||||
|
if constexpr(named_default_argc > 0) {
|
||||||
|
auto& defaults = record.arguments->defaults;
|
||||||
|
for(std::size_t i = named_only_argc; i < named_argc; ++i) {
|
||||||
|
stack[i] = defaults[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// load arguments from call arguments
|
||||||
|
if(args.size() > normal_argc) {
|
||||||
|
if constexpr(args_pos == -1) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
for(std::size_t i = 0; i < std::min(normal_argc, (int)args.size()); ++i) {
|
||||||
|
stack[i] = args[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
object repack_args;
|
||||||
|
// pack the args
|
||||||
|
if constexpr(args_pos != -1) {
|
||||||
|
const auto n =
|
||||||
|
static_cast<int>(args.size() > normal_argc ? args.size() - normal_argc : 0);
|
||||||
|
auto pack = tuple(n);
|
||||||
|
for(int i = 0; i < n; ++i) {
|
||||||
|
pack[i] = args[normal_argc + i];
|
||||||
|
}
|
||||||
|
repack_args = std::move(pack);
|
||||||
|
stack[args_pos] = repack_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack the kwargs
|
||||||
|
int index = 0;
|
||||||
|
if constexpr(named_argc != 0) {
|
||||||
|
int arg_index = 0;
|
||||||
|
while(arg_index < named_argc && index < kwargs.size()) {
|
||||||
|
const auto name = kwargs[index].first;
|
||||||
|
const auto value = kwargs[index].second;
|
||||||
|
if(name.cast<std::string_view>() == record.arguments->names[arg_index]) {
|
||||||
|
stack[arg_index] = value;
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
arg_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object repacked_kwargs;
|
||||||
|
if constexpr(kwargs_pos != -1) {
|
||||||
|
auto pack = dict();
|
||||||
|
while(index < kwargs.size()) {
|
||||||
|
pack[kwargs[index].first] = kwargs[index].second;
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
repacked_kwargs = std::move(pack);
|
||||||
|
stack[kwargs_pos] = repacked_kwargs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if all the arguments are valid
|
||||||
|
for(std::size_t i = 0; i < argc; ++i) {
|
||||||
|
if(!stack[i]) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ok, all the arguments are valid, call the function
|
||||||
|
std::tuple<type_caster<Args>...> casters;
|
||||||
|
|
||||||
|
if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
|
||||||
|
invoke(record.as<Callable>(),
|
||||||
|
std::index_sequence<Is...>{},
|
||||||
|
casters,
|
||||||
|
record.policy,
|
||||||
|
parent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
class cpp_function : public function {
|
||||||
|
inline static py_Type m_type = 0;
|
||||||
|
|
||||||
|
PKBIND_TYPE_IMPL(function, cpp_function, tp_function);
|
||||||
|
|
||||||
|
static void register_() {
|
||||||
|
m_type = py_newtype("function_record", tp_object, nullptr, [](void* data) {
|
||||||
|
static_cast<impl::function_record*>(data)->~function_record();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_function_record(handle h) {
|
||||||
|
if(isinstance<function>(h)) {
|
||||||
|
auto slot = py_getslot(h.ptr(), 0);
|
||||||
|
if(slot) { return py_typeof(slot) == m_type; }
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Fn, typename... Extras>
|
||||||
|
cpp_function(bool is_method, const char* name, Fn&& fn, const Extras&... extras) :
|
||||||
|
function(alloc_t{}) {
|
||||||
|
// bind the function
|
||||||
|
std::string sig = name;
|
||||||
|
sig += is_method ? "(self, *args, **kwargs)" : "(*args, **kwargs)";
|
||||||
|
auto call = [](int argc, py_Ref stack) {
|
||||||
|
handle func = py_inspect_currentfunction();
|
||||||
|
auto data = py_touserdata(py_getslot(func.ptr(), 0));
|
||||||
|
auto& record = *static_cast<impl::function_record*>(data);
|
||||||
|
try {
|
||||||
|
record(argc, stack);
|
||||||
|
return true;
|
||||||
|
} catch(std::domain_error& e) {
|
||||||
|
py_exception(tp_ValueError, e.what());
|
||||||
|
} catch(std::invalid_argument& e) {
|
||||||
|
py_exception(tp_ValueError, e.what());
|
||||||
|
} catch(std::length_error& e) {
|
||||||
|
py_exception(tp_ValueError, e.what());
|
||||||
|
} catch(std::out_of_range& e) {
|
||||||
|
py_exception(tp_IndexError, e.what());
|
||||||
|
} catch(std::range_error& e) {
|
||||||
|
py_exception(tp_ValueError, e.what());
|
||||||
|
} catch(stop_iteration&) { StopIteration(); } catch(index_error& e) {
|
||||||
|
py_exception(tp_IndexError, e.what());
|
||||||
|
} catch(key_error& e) { py_exception(tp_KeyError, e.what()); } catch(value_error& e) {
|
||||||
|
py_exception(tp_ValueError, e.what());
|
||||||
|
} catch(type_error& e) {
|
||||||
|
py_exception(tp_TypeError, e.what());
|
||||||
|
} catch(import_error& e) {
|
||||||
|
py_exception(tp_ImportError, e.what());
|
||||||
|
} catch(attribute_error& e) {
|
||||||
|
py_exception(tp_AttributeError, e.what());
|
||||||
|
} catch(std::exception& e) { py_exception(tp_RuntimeError, e.what()); }
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
py_newfunction(m_ptr, sig.c_str(), call, nullptr, 1);
|
||||||
|
|
||||||
|
assert(m_type != 0 && "function record type not registered");
|
||||||
|
auto slot = py_getslot(m_ptr, 0);
|
||||||
|
void* data = py_newobject(slot, m_type, 0, sizeof(impl::function_record));
|
||||||
|
new (data) impl::function_record(std::forward<Fn>(fn), extras...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Fn, typename... Extras>
|
||||||
|
cpp_function(Fn&& fn, const Extras&... extras) :
|
||||||
|
cpp_function("lambda", std::forward<Fn>(fn), extras...) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class property : public object {
|
||||||
|
PKBIND_TYPE_IMPL(object, property, tp_property);
|
||||||
|
|
||||||
|
property(handle getter, handle setter = none()) : object() {
|
||||||
|
auto start = py_peek(0);
|
||||||
|
py_push(getter.ptr());
|
||||||
|
py_push(setter.ptr());
|
||||||
|
raise_call<py_tpcall>(type::of<property>().index(), 2, start);
|
||||||
|
*this = object::from_ret();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class staticmethod : public object {
|
||||||
|
PKBIND_TYPE_IMPL(object, staticmethod, tp_staticmethod);
|
||||||
|
|
||||||
|
staticmethod(handle method) : object() {
|
||||||
|
auto start = py_peek(0);
|
||||||
|
py_push(method.ptr());
|
||||||
|
raise_call<py_tpcall>(type::of<staticmethod>().index(), 1, start);
|
||||||
|
*this = object::from_ret();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
template <bool is_method, bool is_static, typename Fn, typename... Extras>
|
||||||
|
void bind_function(handle obj, const char* name_, Fn&& fn, const Extras&... extras) {
|
||||||
|
constexpr bool has_named_args =
|
||||||
|
((std::is_same_v<Extras, arg> || std::is_same_v<Extras, arg_with_default>) || ...);
|
||||||
|
auto name = py_name(name_);
|
||||||
|
auto func = py_getdict(obj.ptr(), name);
|
||||||
|
|
||||||
|
if(func && cpp_function::is_function_record(func)) {
|
||||||
|
auto slot = py_getslot(func, 0);
|
||||||
|
auto& record = *static_cast<function_record*>(py_touserdata(slot));
|
||||||
|
if constexpr(has_named_args && is_method) {
|
||||||
|
record.append(new function_record(std::forward<Fn>(fn), arg("self"), extras...));
|
||||||
|
} else {
|
||||||
|
record.append(new function_record(std::forward<Fn>(fn), extras...));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if constexpr(is_static) {
|
||||||
|
py_setdict(
|
||||||
|
obj.ptr(),
|
||||||
|
name,
|
||||||
|
staticmethod(cpp_function(is_method, name_, std::forward<Fn>(fn), extras...).ptr())
|
||||||
|
.ptr());
|
||||||
|
} else {
|
||||||
|
if constexpr(has_named_args && is_method) {
|
||||||
|
py_setdict(
|
||||||
|
obj.ptr(),
|
||||||
|
name,
|
||||||
|
cpp_function(is_method, name_, std::forward<Fn>(fn), arg("self"), extras...)
|
||||||
|
.ptr());
|
||||||
|
} else {
|
||||||
|
py_setdict(obj.ptr(),
|
||||||
|
name,
|
||||||
|
cpp_function(is_method, name_, std::forward<Fn>(fn), extras...).ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Getter, typename Setter, typename... Extras>
|
||||||
|
void bind_property(handle obj,
|
||||||
|
const char* name,
|
||||||
|
Getter&& getter_,
|
||||||
|
Setter&& setter_,
|
||||||
|
const Extras&... extras) {
|
||||||
|
if constexpr(std::is_same_v<std::decay_t<Setter>, std::nullptr_t>) {
|
||||||
|
cpp_function getter(true,
|
||||||
|
name,
|
||||||
|
std::forward<Getter>(getter_),
|
||||||
|
return_value_policy::reference_internal,
|
||||||
|
extras...);
|
||||||
|
property prop(getter.ptr());
|
||||||
|
setattr(obj, name, prop);
|
||||||
|
} else {
|
||||||
|
cpp_function getter(true,
|
||||||
|
name,
|
||||||
|
std::forward<Getter>(getter_),
|
||||||
|
return_value_policy::reference_internal,
|
||||||
|
extras...);
|
||||||
|
cpp_function setter(true,
|
||||||
|
name,
|
||||||
|
std::forward<Setter>(setter_),
|
||||||
|
return_value_policy::reference_internal,
|
||||||
|
extras...);
|
||||||
|
property prop(getter.ptr(), setter.ptr());
|
||||||
|
setattr(obj, name, prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
inline dict::dict(std::initializer_list<arg_with_default> args) : dict() {
|
||||||
|
for(auto& arg: args) {
|
||||||
|
this->operator[] (arg.name) = arg.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pkbind
|
@ -1,15 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "kernel.h"
|
#include "accessor.h"
|
||||||
|
|
||||||
|
namespace pkbind {
|
||||||
|
|
||||||
namespace pybind11 {
|
|
||||||
struct type_info {
|
struct type_info {
|
||||||
const char* name;
|
std::string_view name;
|
||||||
std::size_t size;
|
std::size_t size;
|
||||||
std::size_t alignment;
|
std::size_t alignment;
|
||||||
void (*destructor)(void*);
|
void (*destructor)(void*);
|
||||||
void (*copy)(void*, const void*);
|
|
||||||
void (*move)(void*, void*);
|
|
||||||
const std::type_info* type;
|
const std::type_info* type;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -17,18 +16,11 @@ struct type_info {
|
|||||||
static_assert(!std::is_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
|
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.");
|
"T must not be a reference type or const type.");
|
||||||
static type_info info = {
|
static type_info info = {
|
||||||
typeid(T).name(),
|
type_name<T>(),
|
||||||
sizeof(T),
|
sizeof(T),
|
||||||
alignof(T),
|
alignof(T),
|
||||||
[](void* ptr) {
|
[](void* ptr) {
|
||||||
((T*)ptr)->~T();
|
delete static_cast<T*>(ptr);
|
||||||
operator delete (ptr);
|
|
||||||
},
|
|
||||||
[](void* dst, const void* src) {
|
|
||||||
new (dst) T(*(const T*)src);
|
|
||||||
},
|
|
||||||
[](void* dst, void* src) {
|
|
||||||
new (dst) T(std::move(*(T*)src));
|
|
||||||
},
|
},
|
||||||
&typeid(T),
|
&typeid(T),
|
||||||
};
|
};
|
||||||
@ -37,11 +29,8 @@ struct type_info {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// all registered C++ class will be ensured as instance type.
|
// all registered C++ class will be ensured as instance type.
|
||||||
class instance {
|
struct instance {
|
||||||
public:
|
|
||||||
// use to record the type information of C++ class.
|
// use to record the type information of C++ class.
|
||||||
|
|
||||||
private:
|
|
||||||
enum Flag {
|
enum Flag {
|
||||||
None = 0,
|
None = 0,
|
||||||
Own = 1 << 0, // if the instance is owned by C++ side.
|
Own = 1 << 0, // if the instance is owned by C++ side.
|
||||||
@ -50,96 +39,78 @@ private:
|
|||||||
|
|
||||||
Flag flag;
|
Flag flag;
|
||||||
void* data;
|
void* data;
|
||||||
const type_info* type;
|
const type_info* info;
|
||||||
pkpy::PyVar parent;
|
object parent;
|
||||||
// pkpy::PyVar
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
instance() noexcept : flag(Flag::None), data(nullptr), type(nullptr), parent(nullptr) {}
|
template <typename Value>
|
||||||
|
static object create(type type, Value&& value_, handle parent_, return_value_policy policy) {
|
||||||
|
using underlying_type = remove_cvref_t<Value>;
|
||||||
|
|
||||||
instance(const instance&) = delete;
|
auto& value = [&]() -> auto& {
|
||||||
|
// note that, pybind11 will ignore the const qualifier.
|
||||||
instance(instance&& other) noexcept : flag(other.flag), data(other.data), type(other.type), parent(other.parent) {
|
// in fact, try to modify a const value will result in undefined behavior.
|
||||||
other.flag = Flag::None;
|
|
||||||
other.data = nullptr;
|
|
||||||
other.type = nullptr;
|
|
||||||
other.parent = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
static pkpy::PyVar create(pkpy::Type type) {
|
|
||||||
instance instance;
|
|
||||||
instance.type = &type_info::of<T>();
|
|
||||||
instance.data = operator new (sizeof(T));
|
|
||||||
instance.flag = Flag::Own;
|
|
||||||
return vm->new_object<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 = std::remove_cv_t<std::remove_reference_t<T>>;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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>) {
|
if constexpr(std::is_pointer_v<underlying_type>) {
|
||||||
return *reinterpret_cast<underlying_type*>(value);
|
return *reinterpret_cast<underlying_type*>(value_);
|
||||||
} else {
|
} else {
|
||||||
return const_cast<underlying_type&>(value);
|
return const_cast<underlying_type&>(value_);
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
instance instance;
|
using primary = std::remove_pointer_t<underlying_type>;
|
||||||
instance.type = &type_info::of<underlying_type>();
|
|
||||||
|
auto info = &type_info::of<primary>();
|
||||||
|
|
||||||
|
void* data = nullptr;
|
||||||
|
Flag flag = Flag::None;
|
||||||
|
object parent;
|
||||||
|
|
||||||
if(policy == return_value_policy::take_ownership) {
|
if(policy == return_value_policy::take_ownership) {
|
||||||
instance.data = &_value;
|
data = &value;
|
||||||
instance.flag = Flag::Own;
|
flag = Flag::Own;
|
||||||
} else if(policy == return_value_policy::copy) {
|
} else if(policy == return_value_policy::copy) {
|
||||||
instance.data = ::new auto(_value);
|
if constexpr(std::is_copy_constructible_v<primary>) {
|
||||||
instance.flag = Flag::Own;
|
data = new auto(value);
|
||||||
|
flag = Flag::Own;
|
||||||
|
} else {
|
||||||
|
std::string msg = "cannot use copy policy on non-copyable type: ";
|
||||||
|
msg += type_name<primary>();
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
} else if(policy == return_value_policy::move) {
|
} else if(policy == return_value_policy::move) {
|
||||||
instance.data = ::new auto(std::move(_value));
|
if constexpr(std::is_move_constructible_v<primary>) {
|
||||||
instance.flag = Flag::Own;
|
data = new auto(std::move(value));
|
||||||
|
flag = Flag::Own;
|
||||||
|
} else {
|
||||||
|
std::string msg = "cannot use move policy on non-moveable type: ";
|
||||||
|
msg += type_name<primary>();
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
} else if(policy == return_value_policy::reference) {
|
} else if(policy == return_value_policy::reference) {
|
||||||
instance.data = &_value;
|
data = &value;
|
||||||
instance.flag = Flag::None;
|
flag = Flag::None;
|
||||||
} else if(policy == return_value_policy::reference_internal) {
|
} else if(policy == return_value_policy::reference_internal) {
|
||||||
instance.data = &_value;
|
data = &value;
|
||||||
instance.flag = Flag::Ref;
|
flag = Flag::Ref;
|
||||||
instance.parent = parent;
|
parent = borrow(parent_);
|
||||||
}
|
}
|
||||||
|
|
||||||
return vm->new_object<pybind11::instance>(type, std::move(instance));
|
object result(object::alloc_t{});
|
||||||
|
void* temp = py_newobject(result.ptr(), type.index(), 1, sizeof(instance));
|
||||||
|
new (temp) instance{flag, data, info, std::move(parent)};
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
~instance() {
|
~instance() {
|
||||||
if(flag & Flag::Own) { type->destructor(data); }
|
if(flag & Flag::Own) {
|
||||||
}
|
info->destructor(data);
|
||||||
|
}
|
||||||
void _gc_mark(pkpy::VM* vm) const noexcept {
|
|
||||||
if(parent && (flag & Flag::Ref)) { PK_OBJ_MARK(parent); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T& cast() noexcept {
|
T& as() noexcept {
|
||||||
return *static_cast<T*>(data);
|
return *static_cast<T*>(data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace pybind11
|
|
||||||
|
} // namespace pkbind
|
||||||
|
@ -1,99 +1,150 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <pocketpy.h>
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cassert>
|
||||||
|
#include <typeindex>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace pybind11 {
|
#include "pocketpy.h"
|
||||||
inline pkpy::VM* vm = nullptr;
|
#include "type_traits.h"
|
||||||
inline std::map<pkpy::PyVar, int*>* _ref_counts_map = nullptr;
|
|
||||||
|
|
||||||
inline void initialize(bool enable_os = true) {
|
namespace pkbind {
|
||||||
vm = new pkpy::VM(enable_os);
|
|
||||||
_ref_counts_map = new std::map<pkpy::PyVar, int*>();
|
|
||||||
|
|
||||||
// use to keep alive PyObject, when the object is hold by C++ side.
|
class handle;
|
||||||
vm->heap._gc_marker_ex = [](pkpy::VM* vm) {
|
|
||||||
for(auto iter = _ref_counts_map->begin(); iter != _ref_counts_map->end();) {
|
/// hold the object temporarily
|
||||||
auto ref_count = iter->second;
|
template <int N>
|
||||||
if(*ref_count != 0) {
|
struct reg_t {
|
||||||
// if ref count is not zero, then mark it.
|
py_Ref value;
|
||||||
PK_OBJ_MARK(iter->first);
|
|
||||||
++iter;
|
void operator= (py_Ref ref) & { py_setreg(N, ref); }
|
||||||
} else {
|
|
||||||
// if ref count is zero, then delete it.
|
operator py_Ref () & {
|
||||||
iter = _ref_counts_map->erase(iter);
|
assert(value && "register is not initialized");
|
||||||
delete ref_count;
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator= (handle value) &;
|
||||||
|
|
||||||
|
operator handle () &;
|
||||||
|
|
||||||
|
// pkpy provide user 8 registers.
|
||||||
|
// 8th register is used for object pool, so N is limited to [0, 7).
|
||||||
|
static_assert(N >= 0 && N <= 6, "N must be in [0, 7)");
|
||||||
|
};
|
||||||
|
|
||||||
|
struct retv_t {
|
||||||
|
py_Ref value;
|
||||||
|
|
||||||
|
void operator= (py_Ref ref) & { py_assign(value, ref); }
|
||||||
|
|
||||||
|
operator py_Ref () & {
|
||||||
|
assert(value && "return value is not initialized");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator= (handle value) &;
|
||||||
|
|
||||||
|
operator handle () &;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// hold the object long time.
|
||||||
|
struct object_pool {
|
||||||
|
inline static int cache = -1;
|
||||||
|
inline static py_Ref pool = nullptr;
|
||||||
|
inline static std::vector<int>* indices_ = nullptr;
|
||||||
|
|
||||||
|
struct object_ref {
|
||||||
|
py_Ref data;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void initialize(int size) noexcept {
|
||||||
|
// use 8th register.
|
||||||
|
pool = py_getreg(7);
|
||||||
|
py_newtuple(pool, size);
|
||||||
|
indices_ = new std::vector<int>(size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void finalize() noexcept {
|
||||||
|
delete indices_;
|
||||||
|
indices_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// alloc an object from pool, note that the object is uninitialized.
|
||||||
|
static object_ref alloc() {
|
||||||
|
auto& indices = *indices_;
|
||||||
|
if(cache != -1) {
|
||||||
|
auto index = cache;
|
||||||
|
cache = -1;
|
||||||
|
indices[index] = 1;
|
||||||
|
return {py_tuple_getitem(pool, index), index};
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < indices.size(); ++i) {
|
||||||
|
if(indices[i] == 0) {
|
||||||
|
indices[i] = 1;
|
||||||
|
return {py_tuple_getitem(pool, i), i};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void finalize() {
|
throw std::runtime_error("object pool is full");
|
||||||
delete _ref_counts_map;
|
}
|
||||||
delete vm;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class return_value_policy : uint8_t {
|
/// alloc an object from pool, the object is initialized with ref.
|
||||||
/**
|
static object_ref realloc(py_Ref ref) {
|
||||||
* This is the default return value policy, which falls back to the policy
|
auto result = alloc();
|
||||||
* return_value_policy::take_ownership when the return value is a pointer.
|
py_assign(result.data, ref);
|
||||||
* Otherwise, it uses return_value::move or return_value::copy for rvalue
|
return result;
|
||||||
* and lvalue references, respectively. See below for a description of what
|
}
|
||||||
* all of these different policies do.
|
|
||||||
*/
|
|
||||||
automatic = 0,
|
|
||||||
|
|
||||||
/**
|
static void inc_ref(object_ref ref) {
|
||||||
* As above, but use policy return_value_policy::reference when the return
|
if(!indices_) { return; }
|
||||||
* value is a pointer. This is the default conversion policy for function
|
if(ref.data == py_tuple_getitem(pool, ref.index)) {
|
||||||
* arguments when calling Python functions manually from C++ code (i.e. via
|
auto& indices = *indices_;
|
||||||
* handle::operator()). You probably won't need to use this.
|
indices[ref.index] += 1;
|
||||||
*/
|
} else {
|
||||||
automatic_reference,
|
throw std::runtime_error("object_ref is invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
static void dec_ref(object_ref ref) {
|
||||||
* Reference an existing object (i.e. do not create a new copy) and take
|
if(!indices_) { return; }
|
||||||
* ownership. Python will call the destructor and delete operator when the
|
if(ref.data == py_tuple_getitem(pool, ref.index)) {
|
||||||
* object's reference count reaches zero. Undefined behavior ensues when
|
auto& indices = *indices_;
|
||||||
* the C++ side does the same..
|
indices[ref.index] -= 1;
|
||||||
*/
|
assert(indices[ref.index] >= 0 && "ref count is negative");
|
||||||
take_ownership,
|
if(indices[ref.index] == 0) { cache = ref.index; }
|
||||||
|
} else {
|
||||||
/**
|
throw std::runtime_error("object_ref is invalid");
|
||||||
* 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
|
|
||||||
};
|
};
|
||||||
} // namespace pybind11
|
|
||||||
|
struct action {
|
||||||
|
using function = void (*)();
|
||||||
|
inline static std::vector<function> starts;
|
||||||
|
|
||||||
|
static void initialize() noexcept {
|
||||||
|
for(auto func: starts) {
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// register a function to be called at the start of the vm.
|
||||||
|
static void register_start(function func) { starts.push_back(func); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
inline reg_t<N> reg;
|
||||||
|
|
||||||
|
inline retv_t retv;
|
||||||
|
|
||||||
|
inline std::unordered_map<std::type_index, py_Type>* m_type_map = nullptr;
|
||||||
|
|
||||||
|
} // namespace pkbind
|
||||||
|
47
include/pybind11/internal/module.h
Normal file
47
include/pybind11/internal/module.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "function.h"
|
||||||
|
|
||||||
|
namespace pkbind {
|
||||||
|
|
||||||
|
class module : public object {
|
||||||
|
PKBIND_TYPE_IMPL(object, module, tp_module)
|
||||||
|
|
||||||
|
static module __main__() { return module(py_getmodule("__main__"), object::ref_t{}); }
|
||||||
|
|
||||||
|
static module import(const char* name) {
|
||||||
|
raise_call<py_import>(name);
|
||||||
|
return module(py_retval(), object::realloc_t{});
|
||||||
|
}
|
||||||
|
|
||||||
|
module def_submodule(const char* name, const char* doc = nullptr) {
|
||||||
|
// auto package = (attr("__package__").cast<std::string>() += ".") += attr("__name__").cast<std::string_view>();
|
||||||
|
auto fname = (attr("__name__").cast<std::string>() += ".") += name;
|
||||||
|
auto m = py_newmodule(fname.c_str());
|
||||||
|
setattr(*this, name, m);
|
||||||
|
return module(m, object::ref_t{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Fn, typename... Extras>
|
||||||
|
module& def(const char* name, Fn&& fn, const Extras... extras) {
|
||||||
|
impl::bind_function<false, false>(*this, name, std::forward<Fn>(fn), extras...);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using module_ = module;
|
||||||
|
|
||||||
|
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
|
||||||
|
static void _pkbind_register_##name(::pkbind::module& variable); \
|
||||||
|
namespace pkbind::impl { \
|
||||||
|
auto _module_##name = [] { \
|
||||||
|
::pkbind::action::register_start([] { \
|
||||||
|
auto m = ::pkbind::module(py_newmodule(#name), ::pkbind::object::ref_t{}); \
|
||||||
|
_pkbind_register_##name(m); \
|
||||||
|
}); \
|
||||||
|
return 1; \
|
||||||
|
}(); \
|
||||||
|
} \
|
||||||
|
static void _pkbind_register_##name(::pkbind::module& variable)
|
||||||
|
|
||||||
|
} // namespace pkbind
|
@ -2,251 +2,304 @@
|
|||||||
|
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
|
|
||||||
namespace pybind11 {
|
namespace pkbind {
|
||||||
|
|
||||||
class handle;
|
class handle;
|
||||||
class object;
|
class object;
|
||||||
class attr_accessor;
|
|
||||||
class item_accessor;
|
|
||||||
class iterator;
|
class iterator;
|
||||||
class str;
|
class str;
|
||||||
class bytes;
|
|
||||||
class iterable;
|
|
||||||
class tuple;
|
|
||||||
class dict;
|
|
||||||
class list;
|
|
||||||
class set;
|
|
||||||
class function;
|
|
||||||
class module;
|
|
||||||
class type;
|
|
||||||
class bool_;
|
|
||||||
class int_;
|
|
||||||
class float_;
|
|
||||||
class str;
|
|
||||||
class bytes;
|
|
||||||
|
|
||||||
template <typename T>
|
struct arg;
|
||||||
T& _builtin_cast(const handle& obj);
|
struct arg_with_default;
|
||||||
|
struct args_proxy;
|
||||||
|
|
||||||
template <typename T>
|
enum class return_value_policy : uint8_t {
|
||||||
T reinterpret_borrow(const handle& h);
|
/**
|
||||||
|
* 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,
|
||||||
|
|
||||||
template <typename T>
|
/**
|
||||||
T reinterpret_steal(const handle& h);
|
* 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,
|
||||||
|
|
||||||
class handle {
|
/**
|
||||||
protected:
|
* Reference an existing object (i.e. do not create a new copy) and take
|
||||||
pkpy::PyVar m_ptr = nullptr;
|
* ownership. Python will call the destructor and delete operator when the
|
||||||
mutable int* ref_count = nullptr;
|
* 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
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename policy>
|
||||||
|
class accessor;
|
||||||
|
|
||||||
|
namespace policy {
|
||||||
|
|
||||||
|
struct attr;
|
||||||
|
template <typename Key>
|
||||||
|
struct item;
|
||||||
|
struct tuple;
|
||||||
|
struct list;
|
||||||
|
template <typename Key>
|
||||||
|
struct dict;
|
||||||
|
|
||||||
|
} // namespace policy
|
||||||
|
|
||||||
|
using attr_accessor = accessor<policy::attr>;
|
||||||
|
template <typename Key>
|
||||||
|
using item_accessor = accessor<policy::item<Key>>;
|
||||||
|
using tuple_accessor = accessor<policy::tuple>;
|
||||||
|
using list_accessor = accessor<policy::list>;
|
||||||
|
template <typename Key>
|
||||||
|
using dict_accessor = accessor<policy::dict<Key>>;
|
||||||
|
|
||||||
|
/// call a pkpy function which may raise a python exception.
|
||||||
|
template <auto Fn, typename... Args>
|
||||||
|
auto raise_call(Args&&... args);
|
||||||
|
|
||||||
|
/// an effective representation of a python small string.
|
||||||
|
class name {
|
||||||
public:
|
public:
|
||||||
handle() = default;
|
name() = default;
|
||||||
handle(const handle& h) = default;
|
|
||||||
handle& operator= (const handle& other) = default;
|
|
||||||
|
|
||||||
handle(pkpy::PyVar ptr) : m_ptr(ptr) {}
|
name(const name&) = default;
|
||||||
|
|
||||||
pkpy::PyVar ptr() const { return m_ptr; }
|
name& operator= (const name&) = default;
|
||||||
|
|
||||||
int reference_count() const { return ref_count == nullptr ? 0 : *ref_count; }
|
explicit name(py_Name data) : data(data) {}
|
||||||
|
|
||||||
const handle& inc_ref() const {
|
name(const char* str) : data(py_name(str)) {}
|
||||||
assert(m_ptr != nullptr);
|
|
||||||
if(ref_count == nullptr) {
|
name(const char* data, int size) : data(py_namev({data, size})) {}
|
||||||
auto iter = _ref_counts_map->find(m_ptr);
|
|
||||||
if(iter == _ref_counts_map->end()) {
|
name(std::string_view str) : name(str.data(), static_cast<int>(str.size())) {}
|
||||||
ref_count = ::new int(1);
|
|
||||||
_ref_counts_map->insert({m_ptr, ref_count});
|
name(handle h);
|
||||||
} else {
|
|
||||||
ref_count = iter->second;
|
py_Name index() const { return data; }
|
||||||
*ref_count += 1;
|
|
||||||
}
|
const char* c_str() const { return py_name2str(data); }
|
||||||
} else {
|
|
||||||
*ref_count += 1;
|
operator std::string_view () const {
|
||||||
}
|
auto temp = py_name2sv(data);
|
||||||
return *this;
|
return std::string_view(temp.data, temp.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handle& dec_ref() const {
|
private:
|
||||||
assert(m_ptr != nullptr);
|
py_Name data;
|
||||||
assert(ref_count != nullptr);
|
};
|
||||||
|
|
||||||
*ref_count -= 1;
|
|
||||||
try {
|
|
||||||
if(*ref_count == 0) {
|
|
||||||
_ref_counts_map->erase(m_ptr);
|
|
||||||
::delete ref_count;
|
|
||||||
ref_count = nullptr;
|
|
||||||
}
|
|
||||||
} catch(std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; }
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
template <typename Derived>
|
||||||
|
class interface {
|
||||||
public:
|
public:
|
||||||
template <typename T>
|
/// equal to `self is None` in python.
|
||||||
T cast() const;
|
bool is_none() const { return py_isnone(ptr()); }
|
||||||
|
|
||||||
explicit operator bool () const { return m_ptr.operator bool (); }
|
/// equal to `self is other` in python.
|
||||||
|
bool is(const interface& other) const { return py_isidentical(ptr(), other.ptr()); }
|
||||||
|
|
||||||
bool is(const handle& other) const { return m_ptr == other.m_ptr; }
|
void assign(const interface& other) { py_assign(ptr(), other.ptr()); }
|
||||||
|
|
||||||
bool is_none() const { return m_ptr == vm->None; }
|
|
||||||
|
|
||||||
bool in(const handle& other) const {
|
|
||||||
return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), other.m_ptr, m_ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool contains(const handle& other) const {
|
|
||||||
return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), m_ptr, other.m_ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator begin() const;
|
iterator begin() const;
|
||||||
iterator end() const;
|
iterator end() const;
|
||||||
|
|
||||||
str doc() const;
|
attr_accessor attr(name key) const;
|
||||||
|
|
||||||
attr_accessor attr(const char* name) const;
|
|
||||||
attr_accessor attr(const handle& name) const;
|
|
||||||
attr_accessor attr(object&& name) const;
|
|
||||||
|
|
||||||
item_accessor operator[] (int64_t key) const;
|
|
||||||
item_accessor operator[] (const char* key) const;
|
|
||||||
item_accessor operator[] (const handle& key) const;
|
|
||||||
item_accessor operator[] (object&& key) const;
|
|
||||||
|
|
||||||
object operator- () const;
|
object operator- () const;
|
||||||
object operator~() const;
|
object operator~() const;
|
||||||
|
args_proxy operator* () const;
|
||||||
|
|
||||||
|
item_accessor<int> operator[] (int index) const;
|
||||||
|
item_accessor<name> operator[] (name key) const;
|
||||||
|
item_accessor<handle> operator[] (handle key) const;
|
||||||
|
|
||||||
template <return_value_policy policy = return_value_policy::automatic, typename... Args>
|
template <return_value_policy policy = return_value_policy::automatic, typename... Args>
|
||||||
object operator() (Args&&... args) const;
|
object operator() (Args&&... args) const;
|
||||||
|
|
||||||
private:
|
auto doc() const { return attr("__doc__"); }
|
||||||
friend object operator+ (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator- (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator* (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator% (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator/ (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator| (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator& (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator^ (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator<< (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator>> (const handle& lhs, const handle& rhs);
|
|
||||||
|
|
||||||
friend object operator+= (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator-= (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator*= (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator/= (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator%= (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator|= (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator&= (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator^= (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator<<= (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator>>= (const handle& lhs, const handle& rhs);
|
|
||||||
|
|
||||||
friend object operator== (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator!= (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator< (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator> (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator<= (const handle& lhs, const handle& rhs);
|
|
||||||
friend object operator>= (const handle& lhs, const handle& rhs);
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
friend T& _builtin_cast(const handle& obj) {
|
T cast() const;
|
||||||
// FIXME: 2.0 does not use Py_<T> anymore
|
|
||||||
static_assert(!std::is_reference_v<T>, "T must not be a reference type.");
|
private:
|
||||||
return obj.ptr().obj_get<T>();
|
py_Ref ptr() const { return static_cast<const Derived*>(this)->ptr(); }
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable_v<handle>);
|
/// a simple wrapper to py_Ref.
|
||||||
|
/// Note that it does not manage the lifetime of the object.
|
||||||
|
class handle : public interface<handle> {
|
||||||
|
public:
|
||||||
|
handle() = default;
|
||||||
|
|
||||||
|
handle(const handle&) = default;
|
||||||
|
|
||||||
|
handle& operator= (const handle&) = default;
|
||||||
|
|
||||||
|
handle(py_Ref ptr) : m_ptr(ptr) {}
|
||||||
|
|
||||||
|
auto ptr() const { return m_ptr; }
|
||||||
|
|
||||||
|
explicit operator bool () const { return m_ptr != nullptr; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
py_Ref m_ptr = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
class object : public handle {
|
class object : public handle {
|
||||||
public:
|
public:
|
||||||
object(const object& other) : handle(other) { inc_ref(); }
|
object() = default;
|
||||||
|
|
||||||
object(object&& other) noexcept : handle(other) {
|
object(const object& other) : handle(other), m_index(other.m_index) {
|
||||||
|
if(other.in_pool()) { object_pool::inc_ref(other); }
|
||||||
|
}
|
||||||
|
|
||||||
|
object(object&& other) : handle(other), m_index(other.m_index) {
|
||||||
other.m_ptr = nullptr;
|
other.m_ptr = nullptr;
|
||||||
other.ref_count = nullptr;
|
other.m_index = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
object& operator= (const object& other) {
|
object& operator= (const object& other) {
|
||||||
if(this != &other) {
|
if(this != &other) {
|
||||||
dec_ref();
|
if(in_pool()) { object_pool::dec_ref(*this); }
|
||||||
|
if(other.in_pool()) { object_pool::inc_ref(other); }
|
||||||
m_ptr = other.m_ptr;
|
m_ptr = other.m_ptr;
|
||||||
ref_count = other.ref_count;
|
m_index = other.m_index;
|
||||||
inc_ref();
|
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
object& operator= (object&& other) noexcept {
|
object& operator= (object&& other) {
|
||||||
if(this != &other) {
|
if(this != &other) {
|
||||||
dec_ref();
|
if(in_pool()) { object_pool::dec_ref(*this); }
|
||||||
m_ptr = other.m_ptr;
|
m_ptr = other.m_ptr;
|
||||||
ref_count = other.ref_count;
|
m_index = other.m_index;
|
||||||
other.m_ptr = nullptr;
|
other.m_ptr = nullptr;
|
||||||
other.ref_count = nullptr;
|
other.m_index = -1;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
~object() {
|
~object() {
|
||||||
if(m_ptr != nullptr) { dec_ref(); }
|
if(in_pool()) { object_pool::dec_ref(*this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_singleton() const { return m_ptr && m_index == -1; }
|
||||||
|
|
||||||
|
bool empty() const { return m_ptr == nullptr && m_index == -1; }
|
||||||
|
|
||||||
|
/// return whether the object is in the object pool.
|
||||||
|
bool in_pool() const { return m_ptr && m_index != -1; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
static auto type_or_check() { return tp_object; }
|
||||||
|
|
||||||
|
struct alloc_t {};
|
||||||
|
|
||||||
|
struct realloc_t {};
|
||||||
|
|
||||||
|
struct ref_t {};
|
||||||
|
|
||||||
|
object(alloc_t) {
|
||||||
|
auto ref = object_pool::alloc();
|
||||||
|
m_ptr = ref.data;
|
||||||
|
m_index = ref.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
object(handle h, realloc_t) {
|
||||||
|
auto ref = object_pool::realloc(h.ptr());
|
||||||
|
m_ptr = ref.data;
|
||||||
|
m_index = ref.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
object(handle h, ref_t) : handle(h) {}
|
||||||
|
|
||||||
|
static object from_ret() { return object(retv, realloc_t{}); }
|
||||||
|
|
||||||
|
operator object_pool::object_ref () const { return {m_ptr, m_index}; }
|
||||||
|
|
||||||
|
explicit operator bool () const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
object(const handle& h, bool borrow) : handle(h) {
|
int m_index = -1;
|
||||||
if(borrow) { inc_ref(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
friend T reinterpret_borrow(const handle& h) {
|
|
||||||
return {h, true};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
friend T reinterpret_steal(const handle& h) {
|
|
||||||
return {h, false};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void setattr(const handle& obj, const handle& name, const handle& value);
|
template <typename T = object>
|
||||||
inline void setitem(const handle& obj, const handle& key, const handle& value);
|
T steal(handle h) {
|
||||||
|
return T(h, object::ref_t{});
|
||||||
|
}
|
||||||
|
|
||||||
#define PYBIND11_BINARY_OPERATOR(OP, NAME) \
|
template <typename T = object>
|
||||||
inline object operator OP (const handle& lhs, const handle& rhs) { \
|
T borrow(handle h) {
|
||||||
return reinterpret_borrow<object>(vm->call(vm->py_op(NAME), lhs.m_ptr, rhs.m_ptr)); \
|
return T(h, object::realloc_t{});
|
||||||
}
|
}
|
||||||
|
|
||||||
PYBIND11_BINARY_OPERATOR(+, "add");
|
template <int N>
|
||||||
PYBIND11_BINARY_OPERATOR(-, "sub");
|
void reg_t<N>::operator= (handle h) & {
|
||||||
PYBIND11_BINARY_OPERATOR(*, "mul");
|
py_setreg(N, h.ptr());
|
||||||
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_BINARY_OPERATOR(+=, "iadd");
|
template <int N>
|
||||||
PYBIND11_BINARY_OPERATOR(-=, "isub");
|
reg_t<N>::operator handle () & {
|
||||||
PYBIND11_BINARY_OPERATOR(*=, "imul");
|
assert(value && "register is not initialized");
|
||||||
PYBIND11_BINARY_OPERATOR(/=, "itruediv");
|
return value;
|
||||||
PYBIND11_BINARY_OPERATOR(%=, "imod");
|
}
|
||||||
PYBIND11_BINARY_OPERATOR(|=, "ior");
|
|
||||||
PYBIND11_BINARY_OPERATOR(&=, "iand");
|
|
||||||
PYBIND11_BINARY_OPERATOR(^=, "ixor");
|
|
||||||
PYBIND11_BINARY_OPERATOR(<<=, "ilshift");
|
|
||||||
PYBIND11_BINARY_OPERATOR(>>=, "irshift");
|
|
||||||
|
|
||||||
PYBIND11_BINARY_OPERATOR(==, "eq");
|
inline void retv_t::operator= (handle h) & { py_assign(value, h.ptr()); }
|
||||||
PYBIND11_BINARY_OPERATOR(!=, "ne");
|
|
||||||
PYBIND11_BINARY_OPERATOR(<, "lt");
|
|
||||||
PYBIND11_BINARY_OPERATOR(>, "gt");
|
|
||||||
PYBIND11_BINARY_OPERATOR(<=, "le");
|
|
||||||
PYBIND11_BINARY_OPERATOR(>=, "ge");
|
|
||||||
|
|
||||||
#undef PYBIND11_BINARY_OPERATOR
|
inline retv_t::operator handle () & {
|
||||||
|
assert(value && "return value is not initialized");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace pybind11
|
static_assert(std::is_trivially_copyable_v<name>);
|
||||||
|
static_assert(std::is_trivially_copyable_v<handle>);
|
||||||
|
|
||||||
|
} // namespace pkbind
|
||||||
|
@ -1,12 +1,86 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <memory>
|
||||||
|
#include <string_view>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace pybind11 {
|
namespace pkbind {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct type_identity {
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr bool dependent_false = false;
|
constexpr bool dependent_false = false;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||||
|
|
||||||
|
/// check if T is one of Ts...
|
||||||
|
template <typename T, typename... Ts>
|
||||||
|
constexpr inline bool is_one_of = (std::is_same_v<T, Ts> || ...);
|
||||||
|
|
||||||
|
using std::is_member_function_pointer_v;
|
||||||
|
using std::is_member_object_pointer_v;
|
||||||
|
|
||||||
|
/// check if T is a function pointer type
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline bool is_function_pointer_v = std::is_function_v<std::remove_pointer_t<T>>;
|
||||||
|
|
||||||
|
/// check if T is a functor type(has a unique operator())
|
||||||
|
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>
|
||||||
|
constexpr inline bool is_integer_v = std::is_integral_v<T> && !is_one_of<T, bool, char, wchar_t>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline bool is_floating_point_v = std::is_floating_point_v<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline bool is_unique_pointer_v = false;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline bool is_unique_pointer_v<std::unique_ptr<T>> = true;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool is_pointer_v = std::is_pointer_v<T> && !std::is_same_v<T, const char*>;
|
||||||
|
|
||||||
|
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>
|
||||||
|
constexpr auto type_name() {
|
||||||
|
#if __GNUC__ || __clang__
|
||||||
|
std::string_view name = __PRETTY_FUNCTION__;
|
||||||
|
// format is "auto type_name() [T = int]"
|
||||||
|
std::size_t start = name.find("= ") + 2;
|
||||||
|
std::size_t end = name.rfind(']');
|
||||||
|
return name.substr(start, end - start);
|
||||||
|
#elif _MSC_VER
|
||||||
|
std::string_view name = __FUNCSIG__;
|
||||||
|
// format is possible one of three following:
|
||||||
|
// - "auto __cdecl type_name<int>(void)"
|
||||||
|
// - "auto __cdecl type_name<struct X>(void)"
|
||||||
|
// - "auto __cdecl type_name<class X>(void)"
|
||||||
|
std::size_t start = name.find('<') + 1;
|
||||||
|
std::size_t end = name.rfind(">(");
|
||||||
|
name = name.substr(start, end - start);
|
||||||
|
auto space = name.find(' ');
|
||||||
|
return space == std::string_view::npos ? name : name.substr(space + 1);
|
||||||
|
#else
|
||||||
|
static_assert(false, "current compiler is not supported");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(type_name<int>() == "int" && type_name<double>() == "double",
|
||||||
|
"type_name() test failed, please report this issue");
|
||||||
|
|
||||||
template <typename T, typename Tuple>
|
template <typename T, typename Tuple>
|
||||||
struct tuple_push_front;
|
struct tuple_push_front;
|
||||||
|
|
||||||
@ -24,9 +98,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); \
|
||||||
@ -70,17 +144,6 @@ using class_type_t = typename member_traits<T>::class_type;
|
|||||||
|
|
||||||
// some traits for distinguishing between function pointers, member function pointers and
|
// some traits for distinguishing between function pointers, member function pointers and
|
||||||
// functors
|
// 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>
|
template <typename T, typename SFINAE = void>
|
||||||
struct callable_traits;
|
struct callable_traits;
|
||||||
@ -93,8 +156,8 @@ struct callable_traits<T, std::enable_if_t<is_member_function_pointer_v<T>>> {
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct callable_traits<T, std::enable_if_t<is_function_pointer_v<T>>> {
|
struct callable_traits<T, std::enable_if_t<is_function_pointer_v<T>>> {
|
||||||
using args_type = function_args_t<std::remove_pointer<T>>;
|
using args_type = function_args_t<std::remove_pointer_t<T>>;
|
||||||
using return_type = function_return_t<std::remove_pointer<T>>;
|
using return_type = function_return_t<std::remove_pointer_t<T>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -112,46 +175,64 @@ using callable_return_t = typename callable_traits<Callable>::return_type;
|
|||||||
template <typename Callable>
|
template <typename Callable>
|
||||||
constexpr std::size_t callable_args_count_v = std::tuple_size_v<callable_args_t<Callable>>;
|
constexpr std::size_t callable_args_count_v = std::tuple_size_v<callable_args_t<Callable>>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename... Ts>
|
||||||
struct type_identity {
|
struct type_list {
|
||||||
using type = T;
|
constexpr inline static int size = sizeof...(Ts);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline static int count = [] {
|
||||||
|
int count = 0;
|
||||||
|
((count += std::is_same_v<T, Ts>), ...);
|
||||||
|
return count;
|
||||||
|
}();
|
||||||
|
|
||||||
|
/// find first index of T in type_list.
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline static int find = [] {
|
||||||
|
bool arr[size + 1] = {std::is_same_v<T, Ts>...};
|
||||||
|
for(int i = 0; i < size; ++i) {
|
||||||
|
if(arr[i]) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr inline static int find_last = [] {
|
||||||
|
bool arr[size + 1] = {std::is_same_v<T, Ts>...};
|
||||||
|
for(int i = size - 1; i >= 0; --i) {
|
||||||
|
if(arr[i]) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}();
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename... Args>
|
||||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
struct overload_cast_t {
|
||||||
|
template <typename Return>
|
||||||
|
constexpr auto operator() (Return (*pf)(Args...)) const noexcept -> decltype(pf) {
|
||||||
|
return pf;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, typename... Ts>
|
template <typename Return, typename Class>
|
||||||
constexpr inline std::size_t types_count_v = (std::is_same_v<T, Ts> + ...);
|
constexpr auto operator() (Return (Class::* pmf)(Args...), std::false_type = {}) const noexcept -> decltype(pmf) {
|
||||||
|
return pmf;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename Return, typename Class>
|
||||||
constexpr inline std::size_t types_count_v<T> = 0;
|
constexpr auto operator() (Return (Class::* pmf)(Args...) const, std::true_type) const noexcept -> decltype(pmf) {
|
||||||
|
return pmf;
|
||||||
template <typename T>
|
}
|
||||||
struct value_wrapper {
|
|
||||||
T* pointer;
|
|
||||||
|
|
||||||
operator T& () { return *pointer; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
/// Syntax sugar for resolving overloaded function pointers:
|
||||||
struct value_wrapper<T*> {
|
/// - regular: static_cast<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
|
||||||
T* pointer;
|
/// - sweet: overload_cast<Arg0, Arg1, Arg2>(&Class::func)
|
||||||
|
template <typename... Args>
|
||||||
|
constexpr inline overload_cast_t<Args...> overload_cast;
|
||||||
|
|
||||||
operator T* () { return pointer; }
|
/// Const member function selector for overload_cast
|
||||||
};
|
/// - regular: static_cast<Return (Class::*)(Arg) const>(&Class::func)
|
||||||
|
/// - sweet: overload_cast<Arg>(&Class::func, const_)
|
||||||
|
constexpr inline auto const_ = std::true_type{};
|
||||||
|
|
||||||
template <typename T>
|
} // namespace pkbind
|
||||||
struct value_wrapper<T&> {
|
|
||||||
T* pointer;
|
|
||||||
|
|
||||||
operator T& () { return *pointer; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct value_wrapper<T&&> {
|
|
||||||
T* pointer;
|
|
||||||
|
|
||||||
operator T&& () { return std::move(*pointer); }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace pybind11
|
|
||||||
|
@ -1,208 +1,362 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "object.h"
|
#include "error.h"
|
||||||
|
|
||||||
|
namespace pkbind {
|
||||||
|
|
||||||
|
#define PKBIND_TYPE_IMPL(parent, child, expr) \
|
||||||
|
\
|
||||||
|
private: \
|
||||||
|
friend class type; \
|
||||||
|
static auto type_or_check() { return expr; } \
|
||||||
|
\
|
||||||
|
public: \
|
||||||
|
using parent::parent; \
|
||||||
|
using parent::operator=; \
|
||||||
|
child(const object& o) : parent(type::isinstance<child>(o) ? o : type::of<child>()(o)) {} \
|
||||||
|
child(object&& o) : \
|
||||||
|
parent(type::isinstance<child>(o) ? std::move(o) : type::of<child>()(std::move(o))) {}
|
||||||
|
|
||||||
namespace pybind11 {
|
|
||||||
class type : public object {
|
class type : public object {
|
||||||
public:
|
protected:
|
||||||
using object::object;
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static handle handle_of();
|
constexpr inline static bool is_check_v =
|
||||||
};
|
std::is_invocable_r_v<bool, decltype(T::type_or_check()), handle>;
|
||||||
|
|
||||||
class iterable : public object {
|
static auto type_or_check() { return tp_type; }
|
||||||
public:
|
|
||||||
using object::object;
|
|
||||||
iterable() = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
class iterator : public object {
|
|
||||||
public:
|
|
||||||
using object::object;
|
|
||||||
iterator() = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
class list : public object {
|
|
||||||
public:
|
|
||||||
using object::object;
|
|
||||||
|
|
||||||
list() : object(vm->new_object<pkpy::List>(pkpy::VM::tp_list), true) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class tuple : public object {
|
|
||||||
public:
|
|
||||||
using object::object;
|
|
||||||
|
|
||||||
tuple(int n) : object(vm->new_object<pkpy::Tuple>(pkpy::VM::tp_tuple, n), true) {}
|
|
||||||
|
|
||||||
//& operator[](int i){ return _args[i]; }
|
|
||||||
// PyVar operator[](int i) const { return _args[i]; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class set : public object {
|
|
||||||
public:
|
|
||||||
using object::object;
|
|
||||||
// set() : object(vm->new_object<pkpy::Se>(pkpy::VM::tp_set), true) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class dict : public object {
|
|
||||||
public:
|
|
||||||
using object::object;
|
|
||||||
|
|
||||||
dict() : object(vm->new_object<pkpy::Dict>(pkpy::VM::tp_dict), true) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class str : public object {
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using object::object;
|
using object ::object;
|
||||||
str(const char* c, int len) :
|
using object ::operator=;
|
||||||
object(vm->new_object<pkpy::Str>(pkpy::VM::tp_str, c, len), true) {
|
|
||||||
|
|
||||||
};
|
// note: type is global instance, so we use ref_t.
|
||||||
|
explicit type(py_Type type) : object(py_tpobject(type), ref_t{}) {}
|
||||||
|
|
||||||
str(const char* c = "") : str(c, strlen(c)) {}
|
py_Type index() const { return py_totype(ptr()); }
|
||||||
|
|
||||||
str(const std::string& s) : str(s.data(), s.size()) {}
|
const char* name() const { return py_tpname(index()); }
|
||||||
|
|
||||||
str(std::string_view sv) : str(sv.data(), sv.size()) {}
|
static type of(handle h) { return type(py_typeof(h.ptr())); }
|
||||||
|
|
||||||
explicit str(const bytes& b);
|
template <typename T>
|
||||||
explicit str(handle h);
|
static type of();
|
||||||
operator std::string () const;
|
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename T>
|
||||||
str format(Args&&... args) const;
|
static bool isinstance(handle obj);
|
||||||
};
|
};
|
||||||
|
|
||||||
class int_ : public object {
|
class none : public object {
|
||||||
public:
|
PKBIND_TYPE_IMPL(object, none, tp_NoneType);
|
||||||
using object::object;
|
|
||||||
|
|
||||||
int_(int64_t value) : object(pkpy::py_var(vm, value), true) {}
|
// note: none is global instance, so we use ref_t.
|
||||||
};
|
none() : object(py_None, ref_t{}) {}
|
||||||
|
|
||||||
class float_ : public object {
|
|
||||||
public:
|
|
||||||
using object::object;
|
|
||||||
|
|
||||||
float_(double value) : object(pkpy::py_var(vm, value), true) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class bool_ : public object {
|
class bool_ : public object {
|
||||||
public:
|
PKBIND_TYPE_IMPL(object, bool_, tp_bool);
|
||||||
using object::object;
|
|
||||||
|
|
||||||
bool_(bool value) : object(pkpy::py_var(vm, value), true) {}
|
// same as none, bool is a singleton.
|
||||||
|
bool_(bool value) : object(value ? py_True : py_False, ref_t{}) {}
|
||||||
|
|
||||||
|
explicit operator bool () { return py_tobool(ptr()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class function : public object {
|
class int_ : public object {
|
||||||
public:
|
PKBIND_TYPE_IMPL(object, int_, tp_int);
|
||||||
using object::object;
|
|
||||||
|
int_(py_i64 value) : object(alloc_t{}) { py_newint(m_ptr, value); }
|
||||||
|
|
||||||
|
explicit operator py_i64 () { return py_toint(ptr()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class attr_accessor : public object {
|
class float_ : public object {
|
||||||
|
PKBIND_TYPE_IMPL(object, float_, tp_float);
|
||||||
|
|
||||||
|
float_(py_f64 value) : object(alloc_t{}) { py_newfloat(m_ptr, value); }
|
||||||
|
|
||||||
|
explicit operator py_f64 () { return py_tofloat(ptr()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
bool hasattr(handle obj, name name);
|
||||||
|
|
||||||
|
class iterable : public object {
|
||||||
|
PKBIND_TYPE_IMPL(object, iterable, [](handle h) {
|
||||||
|
return hasattr(h, "__iter__");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
class iterator : public object {
|
||||||
|
template <typename Dervied>
|
||||||
|
friend class interface;
|
||||||
|
|
||||||
|
iterator() : object(), m_value() {}
|
||||||
|
|
||||||
|
iterator(handle h) : object(h, realloc_t{}), m_value() { operator++ (); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
PKBIND_TYPE_IMPL(object, iterator, [](handle h) {
|
||||||
|
return hasattr(h, "__iter__") && hasattr(h, "__next__");
|
||||||
|
});
|
||||||
|
|
||||||
|
iterator& operator++ () {
|
||||||
|
int result = raise_call<py_next>(m_ptr);
|
||||||
|
if(result == 1) {
|
||||||
|
m_value = object(retv, realloc_t{});
|
||||||
|
} else if(result == 0) {
|
||||||
|
m_value = object();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
object operator* () const { return m_value; }
|
||||||
|
|
||||||
|
friend bool operator== (const iterator& lhs, const iterator& rhs) {
|
||||||
|
return lhs.m_value.ptr() == rhs.m_value.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!= (const iterator& lhs, const iterator& rhs) { return !(lhs == rhs); }
|
||||||
|
|
||||||
|
static iterator sentinel() { return iterator(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
object key;
|
object m_value;
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename T>
|
|
||||||
attr_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
attr_accessor& operator= (T&& value) & {
|
|
||||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
|
|
||||||
m_ptr = std::forward<T>(value);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
attr_accessor& operator= (T&& value) && {
|
|
||||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
|
|
||||||
setattr(*this, key, std::forward<T>(value));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline attr_accessor handle::attr(const char* name) const {
|
template <typename Dervied>
|
||||||
return attr_accessor(reinterpret_borrow<object>(*this), str(name));
|
iterator interface<Dervied>::begin() const {
|
||||||
|
raise_call<py_iter>(ptr());
|
||||||
|
return iterator(retv);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline attr_accessor handle::attr(const handle& name) const {
|
template <typename Dervied>
|
||||||
return attr_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(name));
|
iterator interface<Dervied>::end() const {
|
||||||
|
return iterator::sentinel();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline attr_accessor handle::attr(object&& name) const {
|
class str : public object {
|
||||||
return attr_accessor(reinterpret_borrow<object>(*this), std::move(name));
|
PKBIND_TYPE_IMPL(object, str, tp_str);
|
||||||
}
|
|
||||||
|
|
||||||
class item_accessor : public object {
|
str(const char* data, int size) : object(alloc_t{}) { py_newstrn(m_ptr, data, size); }
|
||||||
public:
|
|
||||||
object key;
|
|
||||||
|
|
||||||
public:
|
str(const char* data) : str(data, static_cast<int>(strlen(data))) {}
|
||||||
template <typename T>
|
|
||||||
item_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
|
|
||||||
|
|
||||||
template <typename T>
|
str(std::string_view s) : str(s.data(), static_cast<int>(s.size())) {}
|
||||||
item_accessor& operator= (T&& value) & {
|
|
||||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
|
|
||||||
m_ptr = std::forward<T>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename... Args>
|
||||||
item_accessor& operator= (object&& value) && {
|
object format(Args&&... args);
|
||||||
static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
|
|
||||||
setitem(*this, key, std::forward<T>(value));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline item_accessor handle::operator[] (int64_t key) const {
|
class tuple : public object {
|
||||||
return item_accessor(reinterpret_borrow<object>(*this), int_(key));
|
PKBIND_TYPE_IMPL(object, tuple, tp_tuple);
|
||||||
}
|
|
||||||
|
|
||||||
inline item_accessor handle::operator[] (const char* key) const {
|
tuple(int size) : object(alloc_t{}) { py_newtuple(m_ptr, size); }
|
||||||
return item_accessor(reinterpret_borrow<object>(*this), str(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline item_accessor handle::operator[] (const handle& key) const {
|
tuple(std::initializer_list<handle> args) : tuple(static_cast<int>(args.size())) {
|
||||||
return item_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(key));
|
int index = 0;
|
||||||
}
|
for(auto& arg: args) {
|
||||||
|
py_tuple_setitem(m_ptr, index++, arg.ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline item_accessor handle::operator[] (object&& key) const {
|
tuple_accessor operator[] (int index) const;
|
||||||
return item_accessor(reinterpret_borrow<object>(*this), std::move(key));
|
|
||||||
}
|
int size() const { return py_tuple_len(m_ptr); }
|
||||||
|
|
||||||
|
bool empty() const { return size() == 0; }
|
||||||
|
|
||||||
|
class iterator {
|
||||||
|
friend tuple;
|
||||||
|
|
||||||
|
iterator(py_Ref ptr, int index) : ptr(ptr), index(index) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool operator== (const iterator& other) const { return index == other.index; }
|
||||||
|
|
||||||
|
bool operator!= (const iterator& other) const { return index != other.index; }
|
||||||
|
|
||||||
|
iterator& operator++ () {
|
||||||
|
index++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
object operator* () const { return borrow(py_tuple_getitem(ptr, index)); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
py_Ref ptr;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() const { return iterator(m_ptr, 0); }
|
||||||
|
|
||||||
|
iterator end() const { return iterator(m_ptr, size()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class list : public object {
|
||||||
|
PKBIND_TYPE_IMPL(object, list, tp_list);
|
||||||
|
|
||||||
|
list() : object(alloc_t{}) { py_newlist(m_ptr); }
|
||||||
|
|
||||||
|
list(int size) : object(alloc_t{}) { py_newlistn(m_ptr, size); }
|
||||||
|
|
||||||
|
list(std::initializer_list<handle> args) : list(static_cast<int>(args.size())) {
|
||||||
|
int index = 0;
|
||||||
|
for(auto& arg: args) {
|
||||||
|
py_list_setitem(m_ptr, index++, arg.ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list_accessor operator[] (int index) const;
|
||||||
|
|
||||||
|
int size() const { return py_list_len(m_ptr); }
|
||||||
|
|
||||||
|
bool empty() const { return size() == 0; }
|
||||||
|
|
||||||
|
void swap(int i, int j) { py_list_swap(m_ptr, i, j); }
|
||||||
|
|
||||||
|
void append(handle item) { py_list_append(m_ptr, item.ptr()); }
|
||||||
|
|
||||||
|
void insert(int index, handle item) { py_list_insert(m_ptr, index, item.ptr()); }
|
||||||
|
|
||||||
|
void remove(int index) { py_list_delitem(m_ptr, index); }
|
||||||
|
|
||||||
|
void clear() { py_list_clear(m_ptr); }
|
||||||
|
|
||||||
|
class iterator {
|
||||||
|
friend list;
|
||||||
|
|
||||||
|
iterator(py_Ref ptr, int index) : ptr(ptr), index(index) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
iterator(const iterator&) = default;
|
||||||
|
|
||||||
|
bool operator== (const iterator& other) const { return index == other.index; }
|
||||||
|
|
||||||
|
bool operator!= (const iterator& other) const { return index != other.index; }
|
||||||
|
|
||||||
|
iterator operator++ () {
|
||||||
|
index++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
object operator* () const { return borrow(py_list_getitem(ptr, index)); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
py_Ref ptr;
|
||||||
|
int index;
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() const { return iterator(m_ptr, 0); }
|
||||||
|
|
||||||
|
iterator end() const { return iterator(m_ptr, size()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class dict : public object {
|
||||||
|
PKBIND_TYPE_IMPL(object, dict, tp_dict);
|
||||||
|
|
||||||
|
dict() : object(alloc_t{}) { py_newdict(m_ptr); }
|
||||||
|
|
||||||
|
dict(std::initializer_list<arg_with_default> args);
|
||||||
|
|
||||||
|
dict_accessor<int> operator[] (int key) const;
|
||||||
|
|
||||||
|
dict_accessor<name> operator[] (name key) const;
|
||||||
|
|
||||||
|
dict_accessor<handle> operator[] (handle key) const;
|
||||||
|
|
||||||
|
int size() const { return py_dict_len(m_ptr); }
|
||||||
|
|
||||||
|
bool empty() const { return size() == 0; }
|
||||||
|
|
||||||
|
template <typename Fn>
|
||||||
|
void apply(Fn&& fn) {
|
||||||
|
static_assert(std::is_invocable_v<Fn, py_Ref, py_Ref>);
|
||||||
|
using ret = std::invoke_result_t<Fn, py_Ref, py_Ref>;
|
||||||
|
auto callback = +[](py_Ref key, py_Ref value, void* data) -> bool {
|
||||||
|
auto fn = reinterpret_cast<Fn*>(data);
|
||||||
|
if constexpr(std::is_same_v<ret, void>) {
|
||||||
|
(*fn)(key, value);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return (*fn)(key, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
raise_call<py_dict_apply>(m_ptr, callback, &fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
class iterator {
|
||||||
|
friend dict;
|
||||||
|
|
||||||
|
iterator() : iter(pkbind::iterator::sentinel()) {}
|
||||||
|
|
||||||
|
iterator(handle ptr);
|
||||||
|
|
||||||
|
public:
|
||||||
|
iterator(const iterator&) = default;
|
||||||
|
|
||||||
|
bool operator== (const iterator& other) const { return iter == other.iter; }
|
||||||
|
|
||||||
|
bool operator!= (const iterator& other) const { return !(*this == other); }
|
||||||
|
|
||||||
|
iterator operator++ () {
|
||||||
|
++iter;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<object, object> operator* () const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
object items;
|
||||||
|
pkbind::iterator iter;
|
||||||
|
};
|
||||||
|
|
||||||
|
iterator begin() const { return iterator(m_ptr); }
|
||||||
|
|
||||||
|
iterator end() const { return iterator(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class slice : public object {
|
||||||
|
PKBIND_TYPE_IMPL(object, slice, tp_slice);
|
||||||
|
};
|
||||||
|
|
||||||
|
class set : public object {};
|
||||||
|
|
||||||
class args : public tuple {
|
class args : public tuple {
|
||||||
using tuple::tuple;
|
PKBIND_TYPE_IMPL(tuple, args, tp_tuple);
|
||||||
};
|
};
|
||||||
|
|
||||||
class kwargs : public dict {
|
class kwargs : public dict {
|
||||||
using dict::dict;
|
PKBIND_TYPE_IMPL(dict, kwargs, tp_dict);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
// TODO:
|
||||||
handle type::handle_of() {
|
class capsule : public object {
|
||||||
if constexpr(std::is_same_v<T, object>) { return vm->_t(vm->tp_object); }
|
struct capsule_impl {
|
||||||
#define PYBIND11_TYPE_MAPPER(type, tp) \
|
void* data;
|
||||||
else if constexpr(std::is_same_v<T, type>) { return vm->_t(vm->tp); }
|
void (*destructor)(void*);
|
||||||
PYBIND11_TYPE_MAPPER(type, tp_type)
|
};
|
||||||
PYBIND11_TYPE_MAPPER(str, tp_str)
|
|
||||||
PYBIND11_TYPE_MAPPER(int_, tp_int)
|
|
||||||
PYBIND11_TYPE_MAPPER(float_, tp_float)
|
|
||||||
PYBIND11_TYPE_MAPPER(bool_, tp_bool)
|
|
||||||
PYBIND11_TYPE_MAPPER(list, tp_list)
|
|
||||||
PYBIND11_TYPE_MAPPER(tuple, tp_tuple)
|
|
||||||
PYBIND11_TYPE_MAPPER(args, tp_tuple)
|
|
||||||
PYBIND11_TYPE_MAPPER(dict, tp_dict)
|
|
||||||
PYBIND11_TYPE_MAPPER(kwargs, tp_dict)
|
|
||||||
#undef PYBIND11_TYPE_MAPPER
|
|
||||||
else {
|
|
||||||
auto result = vm->_cxx_typeid_map.find(typeid(T));
|
|
||||||
if(result != vm->_cxx_typeid_map.end()) { return vm->_t(result->second); }
|
|
||||||
|
|
||||||
vm->TypeError("Type not registered");
|
inline static py_Type m_type = 0;
|
||||||
|
|
||||||
|
PKBIND_TYPE_IMPL(object, capsule, m_type);
|
||||||
|
|
||||||
|
static void register_() {
|
||||||
|
m_type = py_newtype("capsule", tp_object, nullptr, [](void* data) {
|
||||||
|
auto impl = static_cast<capsule_impl*>(data);
|
||||||
|
if(impl->data && impl->destructor) { impl->destructor(impl->data); }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pybind11
|
capsule(void* data, void (*destructor)(void*) = nullptr) : object(alloc_t{}) {
|
||||||
|
void* impl = py_newobject(m_ptr, m_type, 0, sizeof(capsule_impl));
|
||||||
|
new (impl) capsule_impl{data, destructor};
|
||||||
|
}
|
||||||
|
|
||||||
|
void* data() { return static_cast<capsule_impl*>(py_touserdata(m_ptr))->data; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& cast() {
|
||||||
|
return *static_cast<T*>(data());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pkbind
|
||||||
|
204
include/pybind11/operators.h
Normal file
204
include/pybind11/operators.h
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pybind11.h"
|
||||||
|
|
||||||
|
namespace pkbind::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 PKBIND_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 PKBIND_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 PKBIND_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>(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
PKBIND_BINARY_OPERATOR(sub, rsub, operator-, l - r)
|
||||||
|
PKBIND_BINARY_OPERATOR(add, radd, operator+, l + r)
|
||||||
|
PKBIND_BINARY_OPERATOR(mul, rmul, operator*, l* r)
|
||||||
|
PKBIND_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
|
||||||
|
PKBIND_BINARY_OPERATOR(mod, rmod, operator%, l % r)
|
||||||
|
PKBIND_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
|
||||||
|
PKBIND_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
|
||||||
|
PKBIND_BINARY_OPERATOR(and, rand, operator&, l& r)
|
||||||
|
PKBIND_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
|
||||||
|
PKBIND_BINARY_OPERATOR(eq, eq, operator==, l == r)
|
||||||
|
PKBIND_BINARY_OPERATOR(ne, ne, operator!=, l != r)
|
||||||
|
PKBIND_BINARY_OPERATOR(or, ror, operator|, l | r)
|
||||||
|
PKBIND_BINARY_OPERATOR(gt, lt, operator>, l > r)
|
||||||
|
PKBIND_BINARY_OPERATOR(ge, le, operator>=, l >= r)
|
||||||
|
PKBIND_BINARY_OPERATOR(lt, gt, operator<, l < r)
|
||||||
|
PKBIND_BINARY_OPERATOR(le, ge, operator<=, l <= r)
|
||||||
|
// PKBIND_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
|
||||||
|
PKBIND_INPLACE_OPERATOR(iadd, operator+=, l += r)
|
||||||
|
PKBIND_INPLACE_OPERATOR(isub, operator-=, l -= r)
|
||||||
|
PKBIND_INPLACE_OPERATOR(imul, operator*=, l *= r)
|
||||||
|
PKBIND_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
|
||||||
|
PKBIND_INPLACE_OPERATOR(imod, operator%=, l %= r)
|
||||||
|
PKBIND_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
|
||||||
|
PKBIND_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
|
||||||
|
PKBIND_INPLACE_OPERATOR(iand, operator&=, l &= r)
|
||||||
|
PKBIND_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
|
||||||
|
PKBIND_INPLACE_OPERATOR(ior, operator|=, l |= r)
|
||||||
|
|
||||||
|
PKBIND_UNARY_OPERATOR(neg, operator-, -l)
|
||||||
|
PKBIND_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
|
||||||
|
|
||||||
|
// PKBIND_UNARY_OPERATOR(abs, abs, std::abs(l))
|
||||||
|
// PKBIND_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
|
||||||
|
// PKBIND_UNARY_OPERATOR(invert, operator~, (~l))
|
||||||
|
// PKBIND_UNARY_OPERATOR(bool, operator!, !!l)
|
||||||
|
// PKBIND_UNARY_OPERATOR(int, int_, (int)l)
|
||||||
|
// PKBIND_UNARY_OPERATOR(float, float_, (double)l)
|
||||||
|
|
||||||
|
#undef PKBIND_BINARY_OPERATOR
|
||||||
|
#undef PKBIND_INPLACE_OPERATOR
|
||||||
|
#undef PKBIND_UNARY_OPERATOR
|
||||||
|
|
||||||
|
} // namespace pkbind::impl
|
||||||
|
|
||||||
|
namespace pkbind {
|
||||||
|
|
||||||
|
using impl::self;
|
||||||
|
|
||||||
|
} // namespace pkbind
|
3
include/pybind11/pkbind.h
Normal file
3
include/pybind11/pkbind.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "pybind11.h"
|
@ -1,3 +1,65 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "internal/class.h"
|
#include "internal/class.h"
|
||||||
|
|
||||||
|
namespace pkbind {
|
||||||
|
|
||||||
|
namespace literals {
|
||||||
|
inline arg operator""_a (const char* c, size_t) { return arg(c); }
|
||||||
|
} // namespace literals
|
||||||
|
|
||||||
|
inline bool initialized = false;
|
||||||
|
|
||||||
|
/// initialize the vm.
|
||||||
|
inline void initialize(int object_pool_size = 1024) {
|
||||||
|
if(!initialized) { py_initialize(); }
|
||||||
|
|
||||||
|
// initialize all registers.
|
||||||
|
reg<0>.value = py_getreg(0);
|
||||||
|
reg<1>.value = py_getreg(1);
|
||||||
|
reg<2>.value = py_getreg(2);
|
||||||
|
reg<3>.value = py_getreg(3);
|
||||||
|
reg<4>.value = py_getreg(4);
|
||||||
|
reg<5>.value = py_getreg(5);
|
||||||
|
reg<6>.value = py_getreg(6);
|
||||||
|
|
||||||
|
// initialize ret.
|
||||||
|
retv.value = py_retval();
|
||||||
|
|
||||||
|
// initialize object pool.
|
||||||
|
object_pool::initialize(object_pool_size);
|
||||||
|
|
||||||
|
m_type_map = new std::unordered_map<std::type_index, py_Type>();
|
||||||
|
|
||||||
|
// register types.
|
||||||
|
capsule::register_();
|
||||||
|
cpp_function::register_();
|
||||||
|
|
||||||
|
action::initialize();
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// finalize the vm.
|
||||||
|
inline void finalize(bool test = false) {
|
||||||
|
if(!initialized) { return; }
|
||||||
|
delete m_type_map;
|
||||||
|
m_type_map = nullptr;
|
||||||
|
object_pool::finalize();
|
||||||
|
if(test) {
|
||||||
|
py_resetvm();
|
||||||
|
} else {
|
||||||
|
py_finalize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// a RAII class to initialize and finalize python interpreter
|
||||||
|
class scoped_interpreter {
|
||||||
|
public:
|
||||||
|
scoped_interpreter(int object_pool_size = 1024) { initialize(object_pool_size); }
|
||||||
|
|
||||||
|
~scoped_interpreter() { finalize(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pkbind
|
||||||
|
|
||||||
|
namespace pybind11 = pkbind;
|
170
include/pybind11/stl.h
Normal file
170
include/pybind11/stl.h
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#include "pybind11.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <deque>
|
||||||
|
#include <forward_list>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace pkbind {
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
struct type_caster<std::array<T, N>> {
|
||||||
|
std::array<T, N> data;
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static handle cast(U&& src, return_value_policy policy, handle parent) {
|
||||||
|
auto list = pkbind::list();
|
||||||
|
for(auto&& item: src) {
|
||||||
|
list.append(pkbind::cast(std::move(item), policy, parent));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if(!isinstance<list>(src)) { return false; }
|
||||||
|
auto list = src.cast<pkbind::list>();
|
||||||
|
|
||||||
|
if(list.size() != N) { return false; }
|
||||||
|
|
||||||
|
for(int i = 0; i < N; ++i) {
|
||||||
|
type_caster<T> caster;
|
||||||
|
if(!caster.load(list[i], convert)) { return false; }
|
||||||
|
data[i] = std::move(caster.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<T, N>& value() { return data; }
|
||||||
|
|
||||||
|
constexpr inline static bool is_temporary_v = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 <>
|
||||||
|
struct type_caster<std::vector<bool>> {
|
||||||
|
std::vector<bool> data;
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static object cast(U&& src, return_value_policy policy, handle parent) {
|
||||||
|
auto list = pkbind::list();
|
||||||
|
for(auto&& item: src) {
|
||||||
|
list.append(pkbind::cast(bool(item), policy, parent));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if(!isinstance<list>(src)) { return false; }
|
||||||
|
|
||||||
|
auto list = src.cast<pkbind::list>();
|
||||||
|
data.clear();
|
||||||
|
data.reserve(list.size());
|
||||||
|
|
||||||
|
for(auto item: list) {
|
||||||
|
type_caster<bool> caster;
|
||||||
|
if(!caster.load(item, convert)) { return false; }
|
||||||
|
data.push_back(caster.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<bool>& value() { return data; }
|
||||||
|
|
||||||
|
constexpr inline static bool is_temporary_v = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct type_caster<T, std::enable_if_t<is_py_list_like_v<T>>> {
|
||||||
|
T data;
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static object cast(U&& src, return_value_policy policy, handle parent) {
|
||||||
|
auto list = pkbind::list();
|
||||||
|
for(auto&& item: src) {
|
||||||
|
list.append(pkbind::cast(std::move(item), policy, parent));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if(!isinstance<list>(src)) { return false; }
|
||||||
|
|
||||||
|
auto list = src.cast<pkbind::list>();
|
||||||
|
|
||||||
|
for(auto item: list) {
|
||||||
|
type_caster<typename T::value_type> caster;
|
||||||
|
if(!caster.load(item, convert)) { return false; }
|
||||||
|
data.push_back(std::move(caster.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& value() { return data; }
|
||||||
|
|
||||||
|
constexpr inline static bool is_temporary_v = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
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>>> {
|
||||||
|
T data;
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
static object cast(U&& src, return_value_policy policy, handle parent) {
|
||||||
|
auto dict = pkbind::dict();
|
||||||
|
for(auto&& [key, value]: src) {
|
||||||
|
dict[pkbind::cast(std::move(key), policy, parent)] =
|
||||||
|
pkbind::cast(std::move(value), policy, parent);
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(handle src, bool convert) {
|
||||||
|
if(!isinstance<dict>(src)) { return false; }
|
||||||
|
auto dict = src.cast<pkbind::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; }
|
||||||
|
|
||||||
|
data.try_emplace(std::move(key_caster.value()), std::move(value_caster.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& value() { return data; }
|
||||||
|
|
||||||
|
constexpr inline static bool is_temporary_v = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace pkbind
|
26
include/pybind11/tests/CMakeLists.txt
Normal file
26
include/pybind11/tests/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(PKBIND_TEST)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../.." "${CMAKE_CURRENT_BINARY_DIR}/pocketpy")
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
|
googletest
|
||||||
|
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
|
||||||
|
DOWNLOAD_EXTRACT_TIMESTAMP true
|
||||||
|
)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(googletest)
|
||||||
|
|
||||||
|
file(GLOB CPP_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
|
||||||
|
add_executable(PKBIND_TEST ${CPP_SOURCES})
|
||||||
|
|
||||||
|
target_include_directories(PKBIND_TEST PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..")
|
||||||
|
target_link_libraries(PKBIND_TEST PRIVATE pocketpy gtest_main)
|
121
include/pybind11/tests/builtins.cpp
Normal file
121
include/pybind11/tests/builtins.cpp
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
int copy_constructor_calls = 0;
|
||||||
|
int move_constructor_calls = 0;
|
||||||
|
int destructor_calls = 0;
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
Point(int x, int y) : x(x), y(y) {}
|
||||||
|
|
||||||
|
Point(const Point& other) : x(other.x), y(other.y) { ++copy_constructor_calls; }
|
||||||
|
|
||||||
|
Point(Point&& other) : x(other.x), y(other.y) { ++move_constructor_calls; }
|
||||||
|
|
||||||
|
~Point() { ++destructor_calls; }
|
||||||
|
|
||||||
|
bool operator== (const Point& p) const { return x == p.x && y == p.y; }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, exec_and_eval) {
|
||||||
|
auto m = py::module::__main__();
|
||||||
|
|
||||||
|
py::dict locals = {py::arg("x") = 1, py::arg("y") = 2};
|
||||||
|
py::object obj = py::eval("x + y", py::none{}, locals);
|
||||||
|
EXPECT_EQ(obj.cast<int>(), 3);
|
||||||
|
|
||||||
|
py::exec("x = 1 + 2");
|
||||||
|
EXPECT_EQ(py::eval("x").cast<int>(), 3);
|
||||||
|
|
||||||
|
py::exec("y = 1 + 2", m.attr("__dict__"));
|
||||||
|
EXPECT_EQ(py::eval("y", m.attr("__dict__")).cast<int>(), 3);
|
||||||
|
|
||||||
|
EXPECT_EQ(locals["x"].cast<int>(), 1);
|
||||||
|
EXPECT_EQ(locals["y"].cast<int>(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, locals_and_globals) {
|
||||||
|
py::exec("x = 1");
|
||||||
|
|
||||||
|
auto globals = py::globals();
|
||||||
|
EXPECT_EQ(globals["x"].cast<int>(), 1);
|
||||||
|
|
||||||
|
globals["y"] = 2;
|
||||||
|
EXPECT_EQ(py::eval("y").cast<int>(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, cast) {
|
||||||
|
auto m = py::module::__main__();
|
||||||
|
|
||||||
|
py::class_<Point>(m, "Point")
|
||||||
|
.def(py::init<int, int>())
|
||||||
|
.def_readwrite("x", &Point::x)
|
||||||
|
.def_readwrite("y", &Point::y)
|
||||||
|
.def("__eq__", &Point::operator==);
|
||||||
|
|
||||||
|
Point p(1, 2);
|
||||||
|
|
||||||
|
// for py::cast's default policy
|
||||||
|
|
||||||
|
// if argument is lvalue, policy is copy
|
||||||
|
py::object o = py::cast(p);
|
||||||
|
EXPECT_EQ(py::cast<Point&>(o), p);
|
||||||
|
EXPECT_EQ(copy_constructor_calls, 1);
|
||||||
|
EXPECT_EQ(move_constructor_calls, 0);
|
||||||
|
|
||||||
|
// if argument is rvalue, policy is move
|
||||||
|
py::object o2 = py::cast(std::move(p));
|
||||||
|
EXPECT_EQ(py::cast<Point&>(o2), p);
|
||||||
|
EXPECT_EQ(copy_constructor_calls, 1);
|
||||||
|
EXPECT_EQ(move_constructor_calls, 1);
|
||||||
|
|
||||||
|
// if argument is pointer, policy is reference(no taking ownership)
|
||||||
|
py::object o3 = py::cast(&p);
|
||||||
|
EXPECT_EQ(py::cast<Point&>(o3), p);
|
||||||
|
EXPECT_EQ(copy_constructor_calls, 1);
|
||||||
|
EXPECT_EQ(move_constructor_calls, 1);
|
||||||
|
|
||||||
|
py::finalize(true);
|
||||||
|
|
||||||
|
EXPECT_EQ(destructor_calls, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, cpp_call_py) {
|
||||||
|
auto m = py::module::__main__();
|
||||||
|
|
||||||
|
py::exec(R"(
|
||||||
|
class Test:
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
def sum1(self, z):
|
||||||
|
return self.x + self.y + z
|
||||||
|
|
||||||
|
def sum2(self, z, *args):
|
||||||
|
return self.x + self.y + z + sum(args)
|
||||||
|
|
||||||
|
def sum3(self, z, n=0):
|
||||||
|
return self.x + self.y + z + n
|
||||||
|
|
||||||
|
def sum4(self, z, *args, **kwargs):
|
||||||
|
return self.x + self.y + z + sum(args) + kwargs['a'] + kwargs['b']
|
||||||
|
)");
|
||||||
|
|
||||||
|
auto obj = py::eval("Test(1, 2)");
|
||||||
|
|
||||||
|
EXPECT_EQ(obj.attr("sum1")(3).cast<int>(), 6);
|
||||||
|
EXPECT_EQ(obj.attr("sum2")(3, 4, 5).cast<int>(), 15);
|
||||||
|
|
||||||
|
using namespace py::literals;
|
||||||
|
|
||||||
|
EXPECT_EQ(obj.attr("sum3")(3, "n"_a = 4).cast<int>(), 10);
|
||||||
|
EXPECT_EQ(obj.attr("sum4")(3, 4, 5, "a"_a = 6, "b"_a = 7).cast<int>(), 28);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
225
include/pybind11/tests/class.cpp
Normal file
225
include/pybind11/tests/class.cpp
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int z;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline static int constructor_calls = 0;
|
||||||
|
inline static int copy_constructor_calls = 0;
|
||||||
|
inline static int move_constructor_calls = 0;
|
||||||
|
inline static int destructor_calls = 0;
|
||||||
|
|
||||||
|
Point() : x(0), y(0), z(0) { constructor_calls++; }
|
||||||
|
|
||||||
|
Point(int x, int y, int z) : x(x), y(y), z(z) { constructor_calls++; }
|
||||||
|
|
||||||
|
Point(const Point& p) : x(p.x), y(p.y), z(p.z) {
|
||||||
|
copy_constructor_calls++;
|
||||||
|
constructor_calls++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point(Point&& p) noexcept : x(p.x), y(p.y), z(p.z) {
|
||||||
|
move_constructor_calls++;
|
||||||
|
constructor_calls++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point& operator= (const Point& p) {
|
||||||
|
x = p.x;
|
||||||
|
y = p.y;
|
||||||
|
z = p.z;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Point() { destructor_calls++; }
|
||||||
|
|
||||||
|
int& get_z() { return z; }
|
||||||
|
|
||||||
|
void set_z(int z) { this->z = z; }
|
||||||
|
|
||||||
|
std::string stringfy() const {
|
||||||
|
return "(" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z) + ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Line {
|
||||||
|
Point start;
|
||||||
|
Point end;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, class) {
|
||||||
|
py::module_ m = py::module_::import("__main__");
|
||||||
|
py::class_<Point>(m, "Point")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def(py::init<int, int, int>())
|
||||||
|
.def_readwrite("x", &Point::x)
|
||||||
|
.def_readwrite("y", &Point::y)
|
||||||
|
.def_property("z", &Point::get_z, &Point::set_z)
|
||||||
|
.def("stringfy", &Point::stringfy);
|
||||||
|
|
||||||
|
py::exec(R"(
|
||||||
|
p = Point()
|
||||||
|
assert p.stringfy() == '(0, 0, 0)'
|
||||||
|
p = Point(1, 2, 3)
|
||||||
|
assert p.x == 1
|
||||||
|
assert p.y == 2
|
||||||
|
assert p.z == 3
|
||||||
|
assert p.stringfy() == '(1, 2, 3)'
|
||||||
|
p.x = 10
|
||||||
|
p.y = 20
|
||||||
|
p.z = 30
|
||||||
|
assert p.stringfy() == '(10, 20, 30)'
|
||||||
|
)");
|
||||||
|
|
||||||
|
py::class_<Line> line(m, "Line");
|
||||||
|
|
||||||
|
line // not bind constructor
|
||||||
|
.def_readwrite("start", &Line::start)
|
||||||
|
.def_readwrite("end", &Line::end);
|
||||||
|
|
||||||
|
// bind constructor
|
||||||
|
line.def(py::init<>());
|
||||||
|
|
||||||
|
py::exec(R"(
|
||||||
|
l = Line()
|
||||||
|
l.start = Point(1, 2, 3)
|
||||||
|
l.end = Point(4, 5, 6)
|
||||||
|
p = l.start
|
||||||
|
assert l.start.stringfy() == '(1, 2, 3)'
|
||||||
|
assert l.end.stringfy() == '(4, 5, 6)'
|
||||||
|
)");
|
||||||
|
|
||||||
|
py::finalize(true);
|
||||||
|
|
||||||
|
EXPECT_EQ(Point::constructor_calls, Point::destructor_calls);
|
||||||
|
EXPECT_EQ(Point::copy_constructor_calls, 0);
|
||||||
|
EXPECT_EQ(Point::move_constructor_calls, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, inheritance) {
|
||||||
|
static int constructor_calls = 0;
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
Point() : x(0), y(0) { constructor_calls++; }
|
||||||
|
|
||||||
|
Point(int x, int y) : x(x), y(y) { constructor_calls++; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Point3D : Point {
|
||||||
|
int z;
|
||||||
|
|
||||||
|
Point3D() : Point(), z(0) { constructor_calls++; }
|
||||||
|
|
||||||
|
Point3D(int x, int y, int z) : Point(x, y), z(z) { constructor_calls++; }
|
||||||
|
};
|
||||||
|
|
||||||
|
py::module_ m = py::module_::import("__main__");
|
||||||
|
|
||||||
|
py::class_<Point>(m, "Point")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def(py::init<int, int>())
|
||||||
|
.def_readwrite("x", &Point::x)
|
||||||
|
.def_readwrite("y", &Point::y);
|
||||||
|
|
||||||
|
py::class_<Point3D, Point>(m, "Point3D")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def(py::init<int, int, int>())
|
||||||
|
.def_readwrite("z", &Point3D::z);
|
||||||
|
|
||||||
|
py::exec(R"(
|
||||||
|
p = Point3D()
|
||||||
|
assert type(p) == Point3D
|
||||||
|
assert p.x == 0
|
||||||
|
assert p.y == 0
|
||||||
|
assert p.z == 0
|
||||||
|
|
||||||
|
p = Point3D(1, 2, 3)
|
||||||
|
assert p.x == 1
|
||||||
|
assert p.y == 2
|
||||||
|
assert p.z == 3
|
||||||
|
|
||||||
|
p.x = 10
|
||||||
|
p.y = 20
|
||||||
|
p.z = 30
|
||||||
|
assert p.x == 10
|
||||||
|
assert p.y == 20
|
||||||
|
assert p.z == 30
|
||||||
|
)");
|
||||||
|
|
||||||
|
py::finalize(true);
|
||||||
|
|
||||||
|
EXPECT_EQ(constructor_calls, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, dynamic_attr) {
|
||||||
|
py::module_ m = py::module_::import("__main__");
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
Point(int x, int y) : x(x), y(y) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
py::class_<Point>(m, "Point", py::dynamic_attr())
|
||||||
|
.def(py::init<int, int>())
|
||||||
|
.def_readwrite("x", &Point::x)
|
||||||
|
.def_readwrite("y", &Point::y);
|
||||||
|
|
||||||
|
py::object p = py::eval("Point(1, 2)");
|
||||||
|
EXPECT_EQ(p.attr("x").cast<int>(), 1);
|
||||||
|
EXPECT_EQ(p.attr("y").cast<int>(), 2);
|
||||||
|
|
||||||
|
p.attr("z") = py::int_(3);
|
||||||
|
EXPECT_EQ(p.attr("z").cast<int>(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, enum) {
|
||||||
|
enum class Color { RED, Yellow, GREEN, BLUE };
|
||||||
|
|
||||||
|
py::module_ m = py::module_::import("__main__");
|
||||||
|
|
||||||
|
py::enum_<Color> color(m, "Color");
|
||||||
|
|
||||||
|
color.value("RED", Color::RED)
|
||||||
|
.value("Yellow", Color::Yellow)
|
||||||
|
.value("GREEN", Color::GREEN)
|
||||||
|
.value("BLUE", Color::BLUE);
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("Color.RED", Color::RED);
|
||||||
|
EXPECT_EVAL_EQ("Color.Yellow", Color::Yellow);
|
||||||
|
EXPECT_EVAL_EQ("Color.GREEN", Color::GREEN);
|
||||||
|
EXPECT_EVAL_EQ("Color.BLUE", Color::BLUE);
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("Color(0)", Color::RED);
|
||||||
|
EXPECT_EVAL_EQ("Color(1)", Color::Yellow);
|
||||||
|
EXPECT_EVAL_EQ("Color(2)", Color::GREEN);
|
||||||
|
EXPECT_EVAL_EQ("Color(3)", Color::BLUE);
|
||||||
|
|
||||||
|
EXPECT_EXEC_EQ("Color(0)", "Color.RED");
|
||||||
|
EXPECT_EXEC_EQ("Color(1)", "Color.Yellow");
|
||||||
|
EXPECT_EXEC_EQ("Color(2)", "Color.GREEN");
|
||||||
|
EXPECT_EXEC_EQ("Color(3)", "Color.BLUE");
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("Color.RED.value", static_cast<int>(Color::RED));
|
||||||
|
EXPECT_EVAL_EQ("Color.Yellow.value", static_cast<int>(Color::Yellow));
|
||||||
|
EXPECT_EVAL_EQ("Color.GREEN.value", static_cast<int>(Color::GREEN));
|
||||||
|
EXPECT_EVAL_EQ("Color.BLUE.value", static_cast<int>(Color::BLUE));
|
||||||
|
|
||||||
|
color.export_values();
|
||||||
|
EXPECT_EVAL_EQ("RED", Color::RED);
|
||||||
|
EXPECT_EVAL_EQ("Yellow", Color::Yellow);
|
||||||
|
EXPECT_EVAL_EQ("GREEN", Color::GREEN);
|
||||||
|
EXPECT_EVAL_EQ("BLUE", Color::BLUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
50
include/pybind11/tests/error.cpp
Normal file
50
include/pybind11/tests/error.cpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, exception_python_to_cpp) {
|
||||||
|
auto test = [](const char* code, py_Type type) {
|
||||||
|
try {
|
||||||
|
py::exec(code);
|
||||||
|
} catch(py::python_error& e) {
|
||||||
|
EXPECT_TRUE(e.match(type));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test("raise ValueError()", tp_ValueError);
|
||||||
|
test("raise KeyError()", tp_KeyError);
|
||||||
|
test("raise IndexError()", tp_IndexError);
|
||||||
|
test("raise StopIteration()", tp_StopIteration);
|
||||||
|
test("raise Exception()", tp_Exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, exception_cpp_to_python) {
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
#define TEST_EXCEPTION(cppe, pye) \
|
||||||
|
m.def("test_" #cppe, []() { \
|
||||||
|
throw cppe(#cppe); \
|
||||||
|
}); \
|
||||||
|
py::exec("try:\n test_" #cppe "()\nexcept Exception as e:\n assert isinstance(e, " #pye \
|
||||||
|
")\n assert str(e) == '" #cppe "'")
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
TEST_EXCEPTION(runtime_error, RuntimeError);
|
||||||
|
TEST_EXCEPTION(domain_error, ValueError);
|
||||||
|
TEST_EXCEPTION(invalid_argument, ValueError);
|
||||||
|
TEST_EXCEPTION(length_error, ValueError);
|
||||||
|
TEST_EXCEPTION(out_of_range, IndexError);
|
||||||
|
TEST_EXCEPTION(range_error, ValueError);
|
||||||
|
|
||||||
|
using namespace py;
|
||||||
|
|
||||||
|
m.def("test_stop_iteration", []() {
|
||||||
|
throw py::stop_iteration();
|
||||||
|
});
|
||||||
|
py::exec("try:\n test_stop_iteration()\nexcept StopIteration as e:\n pass");
|
||||||
|
TEST_EXCEPTION(index_error, IndexError);
|
||||||
|
// FIXME: TEST_EXCEPTION(key_error, KeyError);
|
||||||
|
TEST_EXCEPTION(value_error, ValueError);
|
||||||
|
TEST_EXCEPTION(type_error, TypeError);
|
||||||
|
TEST_EXCEPTION(import_error, ImportError);
|
||||||
|
TEST_EXCEPTION(attribute_error, AttributeError);
|
||||||
|
TEST_EXCEPTION(runtime_error, RuntimeError);
|
||||||
|
}
|
402
include/pybind11/tests/function.cpp
Normal file
402
include/pybind11/tests/function.cpp
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, vectorcall) {
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
py::exec(R"(
|
||||||
|
def add(a, b):
|
||||||
|
return a + b
|
||||||
|
)");
|
||||||
|
// position only
|
||||||
|
EXPECT_CAST_EQ(m.attr("add")(1, 2), 3);
|
||||||
|
// FIXME: pkpy does not support such calling.
|
||||||
|
// keyword only
|
||||||
|
// EXPECT_CAST_EQ(m.attr("add")(py::arg("a") = 1, py::arg("b") = 2), 3);
|
||||||
|
// mix
|
||||||
|
// EXPECT_CAST_EQ(m.attr("add")(1, py::arg("b") = 2), 3);
|
||||||
|
|
||||||
|
py::exec(R"(
|
||||||
|
def add2(a, *args):
|
||||||
|
return a + sum(args)
|
||||||
|
)");
|
||||||
|
EXPECT_CAST_EQ(m.attr("add2")(1, 2, 3, 4), 10);
|
||||||
|
|
||||||
|
EXPECT_EQ(py::type::of<py::tuple>()(py::eval("[1, 2, 3]")), py::eval("(1, 2, 3)"));
|
||||||
|
EXPECT_EQ(py::type::of<py::list>()(py::eval("(1, 2, 3)")), py::eval("[1, 2, 3]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, constructor) {
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
Point(int x, int y) : x(x), y(y) {}
|
||||||
|
|
||||||
|
bool operator== (const Point& p) const { return x == p.x && y == p.y; }
|
||||||
|
};
|
||||||
|
|
||||||
|
py::class_<Point>(m, "Point")
|
||||||
|
.def(py::init<int, int>(), py::arg("x") = 1, py::arg("y") = 2)
|
||||||
|
.def(py::init([](py::tuple tuple) {
|
||||||
|
return Point(tuple[0].cast<int>(), tuple[1].cast<int>());
|
||||||
|
}))
|
||||||
|
.def("__eq__", &Point::operator==);
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("Point()", Point(1, 2));
|
||||||
|
EXPECT_EVAL_EQ("Point(3)", Point(3, 2));
|
||||||
|
EXPECT_EVAL_EQ("Point(3, 4)", Point(3, 4));
|
||||||
|
EXPECT_EVAL_EQ("Point((3, 4))", Point(3, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, args) {
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
// test for binding function with args
|
||||||
|
m.def("sum", [](py::args args) {
|
||||||
|
int sum = 0;
|
||||||
|
for(auto arg: args) {
|
||||||
|
sum += arg.cast<int>();
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("sum(1, 2, 3)", 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, kwargs) {
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
// test for binding function with kwargs
|
||||||
|
m.def("cal", [](py::kwargs kwargs) {
|
||||||
|
int sum = kwargs["a"].cast<int>() + kwargs["b"].cast<int>() * kwargs["c"].cast<int>();
|
||||||
|
return sum;
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("cal(a=1, b=2, c=3)", 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, defaults) {
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
// test for binding function with defaults
|
||||||
|
m.def(
|
||||||
|
"cal",
|
||||||
|
[](int a, int b = 2, int c = 3) {
|
||||||
|
return a + b * c;
|
||||||
|
},
|
||||||
|
py::arg("a"),
|
||||||
|
py::arg("b") = 2,
|
||||||
|
py::arg("c") = 3);
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("cal(1)", 7); // a = 1, b = 2, c = 3
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4)", 13); // a = 1, b = 4, c = 3
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5)", 21); // a = 1, b = 4, c = 5
|
||||||
|
EXPECT_EVAL_EQ("cal(2, c=6)", 14); // a = 2, b = 2, c = 6
|
||||||
|
EXPECT_EVAL_EQ("cal(2, b=4, c=6)", 26); // a = 2, b = 4, c = 6
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, defaults_with_args) {
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
// test for binding function with defaults
|
||||||
|
m.def(
|
||||||
|
"cal",
|
||||||
|
[](int a, int b, int c, py::args args) {
|
||||||
|
int sum = a + b + c;
|
||||||
|
for(auto arg: args) {
|
||||||
|
sum += arg.cast<int>();
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
},
|
||||||
|
py::arg("a"),
|
||||||
|
py::arg("b") = 2,
|
||||||
|
py::arg("c") = 3);
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("cal(1)", 6); // a = 1, b = 2, c = 3
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4)", 8); // a = 1, b = 4, c = 3
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5)", 10); // a = 1, b = 4, c = 5
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5, 6)", 16); // a = 1, b = 4, c = 5, args = (6)
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5, 6, 7)", 23); // a = 1, b = 4, c = 5, args = (6, 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, default_with_args_and_kwargs) {
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
// test for binding function with defaults
|
||||||
|
m.def(
|
||||||
|
"cal",
|
||||||
|
[](int a, int b, int c, py::args args, py::kwargs kwargs) {
|
||||||
|
int sum = a + b + c;
|
||||||
|
for(auto arg: args) {
|
||||||
|
sum += arg.cast<int>();
|
||||||
|
}
|
||||||
|
for(auto item: kwargs) {
|
||||||
|
sum += item.second.cast<int>();
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
},
|
||||||
|
py::arg("a"),
|
||||||
|
py::arg("b") = 2,
|
||||||
|
py::arg("c") = 3);
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("cal(1)", 6); // a = 1, b = 2, c = 3
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4)", 8); // a = 1, b = 4, c = 3
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5)", 10); // a = 1, b = 4, c = 5
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5, 6)", 16); // a = 1, b = 4, c = 5, args = (6)
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5, 6, 7)", 23); // a = 1, b = 4, c = 5, args = (6, 7)
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5, d=6, e=7)", 23); // a = 1, b = 4, c = 5, kwargs = {d=6, e=7}
|
||||||
|
EXPECT_EVAL_EQ("cal(1, d=6, e=7)", 19); // a = 1, b = 2, c = 3, kwargs = {d=6, e=7}
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("cal(1)", 6); // a = 1, b = 2, c = 3
|
||||||
|
EXPECT_EVAL_EQ("cal(1, b=4)", 8); // a = 1, b = 4, c = 3
|
||||||
|
EXPECT_EVAL_EQ("cal(1, c=5)", 8); // a = 1, b = 2, c = 5
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, d=6)", 14); // a = 1, b = 4, c = 3, kwargs = {d=6}
|
||||||
|
EXPECT_EVAL_EQ("cal(1, c=5, d=6)", 14); // a = 1, b = 2, c = 5, kwargs = {d=6}
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5, d=6)", 16); // a = 1, b = 4, c = 5, kwargs = {d=6}
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5)", 10); // a = 1, b = 4, c = 5, args = (), kwargs = {}
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5, *[], **{})", 10); // a = 1, b = 4, c = 5, args = (), kwargs = {}
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 4, 5, 6, 7, d=8, e=9)", 40); // a = 1, b = 4, c = 5, args = (6, 7), kwargs = {d=8, e=9}
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
Point(int x, int y) : x(x), y(y) {}
|
||||||
|
|
||||||
|
int sum(int x = 1, int y = 2) { return this->x + this->y + x + y; }
|
||||||
|
};
|
||||||
|
|
||||||
|
py::class_<Point>(m, "Point")
|
||||||
|
.def(py::init<int, int>()) //
|
||||||
|
.def("sum", &Point::sum, py::arg("x") = 1, py::arg("y") = 2);
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("Point(1, 2).sum()", 6); // x = 1, y = 2
|
||||||
|
EXPECT_EVAL_EQ("Point(1, 2).sum(3)", 8); // x = 3, y = 2
|
||||||
|
EXPECT_EVAL_EQ("Point(1, 2).sum(3, 4)", 10); // x = 3, y = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, overload) {
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
// test for binding function with overloads
|
||||||
|
m.def("cal", [](int a, int b) {
|
||||||
|
return a + b;
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("cal", [](int a, int b, int c) {
|
||||||
|
return a + b + c;
|
||||||
|
});
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 2)", 3);
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 2, 3)", 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, return_value_policy) {
|
||||||
|
static int copy_constructor_calls = 0;
|
||||||
|
static int move_constructor_calls = 0;
|
||||||
|
static int destructor_calls = 0;
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
Point(int x, int y) : x(x), y(y) {}
|
||||||
|
|
||||||
|
Point(const Point& p) : x(p.x), y(p.y) { copy_constructor_calls++; }
|
||||||
|
|
||||||
|
Point(Point&& p) : x(p.x), y(p.y) { move_constructor_calls++; }
|
||||||
|
|
||||||
|
~Point() { destructor_calls++; }
|
||||||
|
|
||||||
|
static Point& make_point() {
|
||||||
|
static Point p(1, 2);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Point& new_point() { return *new Point(1, 2); }
|
||||||
|
|
||||||
|
bool operator== (const Point& p) const { return x == p.x && y == p.y; }
|
||||||
|
};
|
||||||
|
|
||||||
|
py::finalize(true);
|
||||||
|
|
||||||
|
auto test = [](py::return_value_policy policy, auto bound_fn, auto fn) {
|
||||||
|
py::initialize();
|
||||||
|
copy_constructor_calls = 0;
|
||||||
|
move_constructor_calls = 0;
|
||||||
|
destructor_calls = 0;
|
||||||
|
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
py::class_<Point>(m, "Point")
|
||||||
|
.def(py::init<int, int>())
|
||||||
|
.def_readwrite("x", &Point::x)
|
||||||
|
.def_readwrite("y", &Point::y)
|
||||||
|
.def("__eq__", &Point::operator==);
|
||||||
|
|
||||||
|
m.def("make_point", bound_fn, policy);
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("make_point()", Point::make_point());
|
||||||
|
|
||||||
|
py::finalize(true);
|
||||||
|
|
||||||
|
fn();
|
||||||
|
};
|
||||||
|
|
||||||
|
test(py::return_value_policy::reference, &Point::make_point, []() {
|
||||||
|
EXPECT_EQ(copy_constructor_calls, 0);
|
||||||
|
EXPECT_EQ(move_constructor_calls, 0);
|
||||||
|
EXPECT_EQ(destructor_calls, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(py::return_value_policy::copy, &Point::make_point, []() {
|
||||||
|
EXPECT_EQ(copy_constructor_calls, 1);
|
||||||
|
EXPECT_EQ(move_constructor_calls, 0);
|
||||||
|
EXPECT_EQ(destructor_calls, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(py::return_value_policy::move, &Point::make_point, []() {
|
||||||
|
EXPECT_EQ(copy_constructor_calls, 0);
|
||||||
|
EXPECT_EQ(move_constructor_calls, 1);
|
||||||
|
EXPECT_EQ(destructor_calls, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(py::return_value_policy::take_ownership, &Point::new_point, []() {
|
||||||
|
EXPECT_EQ(copy_constructor_calls, 0);
|
||||||
|
EXPECT_EQ(move_constructor_calls, 0);
|
||||||
|
EXPECT_EQ(destructor_calls, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, default_return_value_policy) {
|
||||||
|
static int copy_constructor_calls = 0;
|
||||||
|
static int move_constructor_calls = 0;
|
||||||
|
static int destructor_calls = 0;
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
Point(int x, int y) : x(x), y(y) {}
|
||||||
|
|
||||||
|
Point(const Point& p) : x(p.x), y(p.y) { copy_constructor_calls++; }
|
||||||
|
|
||||||
|
Point(Point&& p) : x(p.x), y(p.y) { move_constructor_calls++; }
|
||||||
|
|
||||||
|
~Point() { destructor_calls++; }
|
||||||
|
|
||||||
|
bool operator== (const Point& p) const { return x == p.x && y == p.y; }
|
||||||
|
};
|
||||||
|
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
py::class_<Point>(m, "Point")
|
||||||
|
.def(py::init<int, int>())
|
||||||
|
.def_readwrite("x", &Point::x)
|
||||||
|
.def_readwrite("y", &Point::y)
|
||||||
|
.def("__eq__", &Point::operator==);
|
||||||
|
|
||||||
|
// for function return value policy
|
||||||
|
|
||||||
|
// if return type is lvalue reference, policy is copy
|
||||||
|
m.def("make_point2", []() -> Point& {
|
||||||
|
static Point p(1, 2);
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
py::exec("p2 = make_point2()");
|
||||||
|
EXPECT_EQ(copy_constructor_calls, 1);
|
||||||
|
EXPECT_EQ(move_constructor_calls, 0);
|
||||||
|
|
||||||
|
// if return type is rvalue reference, policy is move
|
||||||
|
m.def("make_point3", []() -> Point&& {
|
||||||
|
static Point p(1, 2);
|
||||||
|
return std::move(p);
|
||||||
|
});
|
||||||
|
py::exec("p3 = make_point3()");
|
||||||
|
EXPECT_EQ(copy_constructor_calls, 1);
|
||||||
|
EXPECT_EQ(move_constructor_calls, 1);
|
||||||
|
|
||||||
|
// if return type is pointer, policy is take_ownership
|
||||||
|
m.def("make_point4", []() -> Point* {
|
||||||
|
return new Point(1, 2);
|
||||||
|
});
|
||||||
|
py::exec("p4 = make_point4()");
|
||||||
|
EXPECT_EQ(copy_constructor_calls, 1);
|
||||||
|
EXPECT_EQ(move_constructor_calls, 1);
|
||||||
|
|
||||||
|
py::finalize(true);
|
||||||
|
EXPECT_EQ(destructor_calls, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, lambda) {
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
static int destructor_calls = 0;
|
||||||
|
|
||||||
|
struct NotTrivial {
|
||||||
|
int data;
|
||||||
|
|
||||||
|
int operator() (int x, int y) { return x + y + data; }
|
||||||
|
|
||||||
|
~NotTrivial() { destructor_calls++; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NotSmall {
|
||||||
|
size_t a;
|
||||||
|
size_t b;
|
||||||
|
size_t c;
|
||||||
|
size_t d;
|
||||||
|
|
||||||
|
size_t operator() (size_t x, size_t y) { return x + y + a + b + c + d; }
|
||||||
|
|
||||||
|
~NotSmall() { destructor_calls++; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// test for binding lambda
|
||||||
|
m.def("cal", NotTrivial{3});
|
||||||
|
m.def("cal2", NotSmall{3, 4, 5, 6});
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("cal(1, 2)", 6);
|
||||||
|
EXPECT_EVAL_EQ("cal2(1, 2)", 21);
|
||||||
|
|
||||||
|
py::finalize(true);
|
||||||
|
|
||||||
|
EXPECT_EQ(destructor_calls, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
int add(int a, int b) { return a + b; }
|
||||||
|
|
||||||
|
int add(int a, int b, int c) { return a + b + c; }
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, overload_cast) {
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
m.def("add", py::overload_cast<int, int>(add));
|
||||||
|
m.def("add", py::overload_cast<int, int, int>(add));
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("add(1, 2)", 3);
|
||||||
|
EXPECT_EVAL_EQ("add(1, 2, 3)", 6);
|
||||||
|
|
||||||
|
struct X {
|
||||||
|
X() {}
|
||||||
|
|
||||||
|
int add(int a, int b) { return a + b; }
|
||||||
|
|
||||||
|
int add(int a, int b, int c) { return a + b + c; }
|
||||||
|
};
|
||||||
|
|
||||||
|
py::class_<X>(m, "X")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def("add", py::overload_cast<int, int>(&X::add))
|
||||||
|
.def("add", py::overload_cast<int, int, int>(&X::add));
|
||||||
|
|
||||||
|
EXPECT_EVAL_EQ("X().add(1, 2)", 3);
|
||||||
|
EXPECT_EVAL_EQ("X().add(1, 2, 3)", 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
31
include/pybind11/tests/module.cpp
Normal file
31
include/pybind11/tests/module.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
PYBIND11_EMBEDDED_MODULE(example, m) {
|
||||||
|
m.def("add", [](int a, int b) {
|
||||||
|
return a + b;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto math = m.def_submodule("math");
|
||||||
|
math.def("sub", [](int a, int b) {
|
||||||
|
return a - b;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, module) {
|
||||||
|
py::exec("import example");
|
||||||
|
EXPECT_EVAL_EQ("example.add(1, 2)", 3);
|
||||||
|
|
||||||
|
py::exec("from example import math");
|
||||||
|
EXPECT_EVAL_EQ("math.sub(1, 2)", -1);
|
||||||
|
|
||||||
|
py::exec("from example.math import sub");
|
||||||
|
EXPECT_EVAL_EQ("sub(1, 2)", -1);
|
||||||
|
|
||||||
|
auto math = py::module_::import("example.math");
|
||||||
|
EXPECT_EQ(math.attr("sub")(4, 3).cast<int>(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
98
include/pybind11/tests/object.cpp
Normal file
98
include/pybind11/tests/object.cpp
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const char* source = R"(
|
||||||
|
class Point:
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
return Point(self.x + other.x, self.y + other.y)
|
||||||
|
|
||||||
|
def __sub__(self, other):
|
||||||
|
return Point(self.x - other.x, self.y - other.y)
|
||||||
|
|
||||||
|
def __mul__(self, other):
|
||||||
|
return Point(self.x * other.x, self.y * other.y)
|
||||||
|
|
||||||
|
def __truediv__(self, other):
|
||||||
|
return Point(self.x / other.x, self.y / other.y)
|
||||||
|
|
||||||
|
def __floordiv__(self, other):
|
||||||
|
return Point(self.x // other.x, self.y // other.y)
|
||||||
|
|
||||||
|
def __mod__(self, other):
|
||||||
|
return Point(self.x % other.x, self.y % other.y)
|
||||||
|
|
||||||
|
def __pow__(self, other):
|
||||||
|
return Point(self.x ** other.x, self.y ** other.y)
|
||||||
|
|
||||||
|
def __lshift__(self, other):
|
||||||
|
return Point(self.x << other.x, self.y << other.y)
|
||||||
|
|
||||||
|
def __rshift__(self, other):
|
||||||
|
return Point(self.x >> other.x, self.y >> other.y)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.x == other.x and self.y == other.y
|
||||||
|
|
||||||
|
def __ne__(self, other) -> bool:
|
||||||
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
def __lt__(self, other) -> bool:
|
||||||
|
return self.x < other.x and self.y < other.y
|
||||||
|
|
||||||
|
def __le__(self, other) -> bool:
|
||||||
|
return self.x <= other.x and self.y <= other.y
|
||||||
|
|
||||||
|
def __gt__(self, other) -> bool:
|
||||||
|
return self.x > other.x and self.y > other.y
|
||||||
|
|
||||||
|
def __ge__(self, other) -> bool:
|
||||||
|
return self.x >= other.x and self.y >= other.y
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'Point({self.x}, {self.y})'
|
||||||
|
)";
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, object) {
|
||||||
|
py::module_ m = py::module_::import("__main__");
|
||||||
|
py::exec(source);
|
||||||
|
py::exec("p = Point(3, 4)");
|
||||||
|
py::object p = py::eval("p");
|
||||||
|
|
||||||
|
// is
|
||||||
|
EXPECT_FALSE(p.is_none());
|
||||||
|
EXPECT_TRUE(p.is(p));
|
||||||
|
|
||||||
|
// attrs
|
||||||
|
EXPECT_EQ(p.attr("x").cast<int>(), 3);
|
||||||
|
EXPECT_EQ(p.attr("y").cast<int>(), 4);
|
||||||
|
|
||||||
|
p.attr("x") = py::int_(5);
|
||||||
|
p.attr("y") = py::int_(6);
|
||||||
|
|
||||||
|
EXPECT_EQ(p.attr("x").cast<int>(), 5);
|
||||||
|
EXPECT_EQ(p.attr("y").cast<int>(), 6);
|
||||||
|
EXPECT_EXEC_EQ("p", "Point(5, 6)");
|
||||||
|
|
||||||
|
// operators
|
||||||
|
EXPECT_EVAL_EQ("Point(10, 12)", p + p);
|
||||||
|
EXPECT_EVAL_EQ("Point(0, 0)", p - p);
|
||||||
|
EXPECT_EVAL_EQ("Point(25, 36)", p * p);
|
||||||
|
EXPECT_EVAL_EQ("Point(1, 1)", p / p);
|
||||||
|
// EXPECT_EVAL_EQ("Point(0, 0)", p // p);
|
||||||
|
EXPECT_EVAL_EQ("Point(0, 0)", p % p);
|
||||||
|
|
||||||
|
// iterators
|
||||||
|
py::object l = py::eval("[1, 2, 3]");
|
||||||
|
int index = 0;
|
||||||
|
for(auto item: l) {
|
||||||
|
EXPECT_EQ(item.cast<int>(), index + 1);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
150
include/pybind11/tests/operators.cpp
Normal file
150
include/pybind11/tests/operators.cpp
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
#include "test.h"
|
||||||
|
#include <pybind11/operators.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct Int {
|
||||||
|
int x;
|
||||||
|
|
||||||
|
Int(int x) : x(x) {}
|
||||||
|
|
||||||
|
#define OPERATOR_IMPL(op) \
|
||||||
|
template <typename LHS, typename RHS> \
|
||||||
|
friend int operator op (const LHS& lhs, const RHS& rhs) { \
|
||||||
|
int l, r; \
|
||||||
|
if constexpr(std::is_same_v<LHS, Int>) \
|
||||||
|
l = lhs.x; \
|
||||||
|
else \
|
||||||
|
l = lhs; \
|
||||||
|
if constexpr(std::is_same_v<RHS, Int>) \
|
||||||
|
r = rhs.x; \
|
||||||
|
else \
|
||||||
|
r = rhs; \
|
||||||
|
return l op r; \
|
||||||
|
}
|
||||||
|
|
||||||
|
OPERATOR_IMPL(+)
|
||||||
|
OPERATOR_IMPL(-)
|
||||||
|
OPERATOR_IMPL(*)
|
||||||
|
OPERATOR_IMPL(/)
|
||||||
|
OPERATOR_IMPL(%)
|
||||||
|
OPERATOR_IMPL(&)
|
||||||
|
OPERATOR_IMPL(|)
|
||||||
|
OPERATOR_IMPL(^)
|
||||||
|
OPERATOR_IMPL(<<)
|
||||||
|
OPERATOR_IMPL(>>)
|
||||||
|
|
||||||
|
#undef OPERATOR_IMPL
|
||||||
|
|
||||||
|
bool operator== (const Int& other) const { return x == other.x; }
|
||||||
|
|
||||||
|
bool operator!= (const Int& other) const { return x != other.x; }
|
||||||
|
|
||||||
|
bool operator< (const Int& other) const { return x < other.x; }
|
||||||
|
|
||||||
|
bool operator<= (const Int& other) const { return x <= other.x; }
|
||||||
|
|
||||||
|
bool operator> (const Int& other) const { return x > other.x; }
|
||||||
|
|
||||||
|
bool operator>= (const Int& other) const { return x >= other.x; }
|
||||||
|
|
||||||
|
bool operator!() const { return !x; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, arithmetic_operators) {
|
||||||
|
py::module_ m = py::module_::import("__main__");
|
||||||
|
py::class_<Int>(m, "Int")
|
||||||
|
.def(py::init<int>())
|
||||||
|
.def(py::self + py::self)
|
||||||
|
.def(py::self + int())
|
||||||
|
.def(int() + py::self)
|
||||||
|
.def(py::self - py::self)
|
||||||
|
.def(py::self - int())
|
||||||
|
.def(int() - py::self)
|
||||||
|
.def(py::self * py::self)
|
||||||
|
.def(py::self * int())
|
||||||
|
.def(int() * py::self)
|
||||||
|
.def(py::self / py::self)
|
||||||
|
.def(py::self / int())
|
||||||
|
.def(int() / py::self)
|
||||||
|
.def(py::self % py::self)
|
||||||
|
.def(py::self % int())
|
||||||
|
.def(int() % py::self)
|
||||||
|
.def(py::self & py::self)
|
||||||
|
.def(py::self & int())
|
||||||
|
.def(int() & py::self)
|
||||||
|
.def(py::self | py::self)
|
||||||
|
.def(py::self | int())
|
||||||
|
.def(int() | py::self)
|
||||||
|
.def(py::self ^ py::self)
|
||||||
|
.def(py::self ^ int())
|
||||||
|
.def(int() ^ py::self)
|
||||||
|
.def(py::self << py::self)
|
||||||
|
.def(py::self << int())
|
||||||
|
.def(int() << py::self)
|
||||||
|
.def(py::self >> py::self)
|
||||||
|
.def(py::self >> int())
|
||||||
|
.def(int() >> py::self);
|
||||||
|
|
||||||
|
auto a = py::cast(Int(1));
|
||||||
|
auto ai = py::cast(1);
|
||||||
|
auto b = py::cast(Int(2));
|
||||||
|
auto bi = py::cast(2);
|
||||||
|
|
||||||
|
EXPECT_CAST_EQ(a + b, 3);
|
||||||
|
EXPECT_CAST_EQ(a + bi, 3);
|
||||||
|
EXPECT_CAST_EQ(ai + b, 3);
|
||||||
|
EXPECT_CAST_EQ(a - b, -1);
|
||||||
|
EXPECT_CAST_EQ(a - bi, -1);
|
||||||
|
EXPECT_CAST_EQ(ai - b, -1);
|
||||||
|
EXPECT_CAST_EQ(a * b, 2);
|
||||||
|
EXPECT_CAST_EQ(a * bi, 2);
|
||||||
|
EXPECT_CAST_EQ(ai * b, 2);
|
||||||
|
EXPECT_CAST_EQ(a / b, 0);
|
||||||
|
EXPECT_CAST_EQ(a / bi, 0);
|
||||||
|
// EXPECT_CAST_EQ(ai / b, 0);
|
||||||
|
EXPECT_CAST_EQ(a % b, 1);
|
||||||
|
EXPECT_CAST_EQ(a % bi, 1);
|
||||||
|
// EXPECT_CAST_EQ(ai % b, 1);
|
||||||
|
EXPECT_CAST_EQ(a & b, 0);
|
||||||
|
EXPECT_CAST_EQ(a & bi, 0);
|
||||||
|
// EXPECT_CAST_EQ(ai & b, 0);
|
||||||
|
EXPECT_CAST_EQ(a | b, 3);
|
||||||
|
EXPECT_CAST_EQ(a | bi, 3);
|
||||||
|
// EXPECT_CAST_EQ(ai | b, 3);
|
||||||
|
EXPECT_CAST_EQ(a ^ b, 3);
|
||||||
|
EXPECT_CAST_EQ(a ^ bi, 3);
|
||||||
|
// EXPECT_CAST_EQ(ai ^ b, 3);
|
||||||
|
EXPECT_CAST_EQ(a << b, 4);
|
||||||
|
EXPECT_CAST_EQ(a << bi, 4);
|
||||||
|
// EXPECT_CAST_EQ(ai << b, 4);
|
||||||
|
EXPECT_CAST_EQ(a >> b, 0);
|
||||||
|
EXPECT_CAST_EQ(a >> bi, 0);
|
||||||
|
// EXPECT_CAST_EQ(ai >> b, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, logic_operators) {
|
||||||
|
py::module_ m = py::module_::import("__main__");
|
||||||
|
py::class_<Int>(m, "Int")
|
||||||
|
.def(py::init<int>())
|
||||||
|
.def_readwrite("x", &Int::x)
|
||||||
|
.def(py::self == py::self)
|
||||||
|
.def(py::self != py::self)
|
||||||
|
.def(py::self < py::self)
|
||||||
|
.def(py::self <= py::self)
|
||||||
|
.def(py::self > py::self)
|
||||||
|
.def(py::self >= py::self);
|
||||||
|
|
||||||
|
auto a = py::cast(Int(1));
|
||||||
|
auto b = py::cast(Int(2));
|
||||||
|
|
||||||
|
EXPECT_FALSE(a == b);
|
||||||
|
EXPECT_TRUE(a != b);
|
||||||
|
EXPECT_TRUE(a < b);
|
||||||
|
EXPECT_TRUE(a <= b);
|
||||||
|
EXPECT_FALSE(a > b);
|
||||||
|
EXPECT_FALSE(a >= b);
|
||||||
|
}
|
||||||
|
|
125
include/pybind11/tests/stl.cpp
Normal file
125
include/pybind11/tests/stl.cpp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
#include "test.h"
|
||||||
|
#include <pybind11/stl.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
int constructor_calls = 0;
|
||||||
|
int destructor_calls = 0;
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
|
||||||
|
Point() : x(0), y(0) { constructor_calls++; }
|
||||||
|
|
||||||
|
Point(int x, int y) : x(x), y(y) { constructor_calls++; }
|
||||||
|
|
||||||
|
Point(const Point& p) : x(p.x), y(p.y) { constructor_calls++; }
|
||||||
|
|
||||||
|
Point(Point&& p) noexcept : x(p.x), y(p.y) { constructor_calls++; }
|
||||||
|
|
||||||
|
Point& operator= (const Point& p) {
|
||||||
|
x = p.x;
|
||||||
|
y = p.y;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Point() { destructor_calls++; }
|
||||||
|
|
||||||
|
bool operator== (const Point& p) const { return x == p.x && y == p.y; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, vector_bool) {
|
||||||
|
std::vector<bool> v = {true, false, true};
|
||||||
|
py::object obj = py::cast(v);
|
||||||
|
EXPECT_EVAL_EQ("[True, False, True]", obj);
|
||||||
|
|
||||||
|
std::vector<bool> v2 = obj.cast<std::vector<bool>>();
|
||||||
|
EXPECT_EQ(v, v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, list_like) {
|
||||||
|
py::class_<Point>(py::module_::__main__(), "Point")
|
||||||
|
.def(py::init<int, int>())
|
||||||
|
.def_readwrite("x", &Point::x)
|
||||||
|
.def_readwrite("y", &Point::y)
|
||||||
|
.def("__eq__", &Point::operator==);
|
||||||
|
|
||||||
|
// array
|
||||||
|
{
|
||||||
|
std::array<Point, 2> a = {Point(1, 2), Point(3, 4)};
|
||||||
|
py::object obj = py::eval("[Point(1, 2), Point(3, 4)]");
|
||||||
|
EXPECT_EVAL_EQ("[Point(1, 2), Point(3, 4)]", obj);
|
||||||
|
|
||||||
|
std::array<Point, 2> a2 = obj.cast<std::array<Point, 2>>();
|
||||||
|
EXPECT_EQ(a, a2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// vector
|
||||||
|
{
|
||||||
|
std::vector<Point> v = {Point(1, 2), Point(3, 4)};
|
||||||
|
py::object obj = py::cast(v);
|
||||||
|
EXPECT_EVAL_EQ("[Point(1, 2), Point(3, 4)]", obj);
|
||||||
|
|
||||||
|
std::vector<Point> v2 = obj.cast<std::vector<Point>>();
|
||||||
|
EXPECT_EQ(v, v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// list
|
||||||
|
{
|
||||||
|
std::list<Point> l = {Point(1, 2), Point(3, 4)};
|
||||||
|
py::object obj = py::cast(l);
|
||||||
|
EXPECT_EVAL_EQ("[Point(1, 2), Point(3, 4)]", obj);
|
||||||
|
|
||||||
|
std::list<Point> l2 = obj.cast<std::list<Point>>();
|
||||||
|
EXPECT_EQ(l, l2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// deque
|
||||||
|
{
|
||||||
|
std::deque<Point> d = {Point(1, 2), Point(3, 4)};
|
||||||
|
py::object obj = py::cast(d);
|
||||||
|
EXPECT_EVAL_EQ("[Point(1, 2), Point(3, 4)]", obj);
|
||||||
|
|
||||||
|
std::deque<Point> d2 = obj.cast<std::deque<Point>>();
|
||||||
|
EXPECT_EQ(d, d2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, dict_like) {
|
||||||
|
py::class_<Point>(py::module_::__main__(), "Point")
|
||||||
|
.def(py::init<int, int>())
|
||||||
|
.def_readwrite("x", &Point::x)
|
||||||
|
.def_readwrite("y", &Point::y)
|
||||||
|
.def("__eq__", &Point::operator==);
|
||||||
|
|
||||||
|
// map
|
||||||
|
{
|
||||||
|
std::map<std::string, Point> m = {
|
||||||
|
{"a", Point(1, 2)},
|
||||||
|
{"b", Point(3, 4)}
|
||||||
|
};
|
||||||
|
|
||||||
|
py::object obj = py::cast(m);
|
||||||
|
EXPECT_EVAL_EQ("{'a': Point(1, 2), 'b': Point(3, 4)}", obj);
|
||||||
|
|
||||||
|
std::map<std::string, Point> m2 = obj.cast<std::map<std::string, Point>>();
|
||||||
|
EXPECT_EQ(m, m2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unordered_map
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, Point> m = {
|
||||||
|
{"a", Point(1, 2)},
|
||||||
|
{"b", Point(3, 4)}
|
||||||
|
};
|
||||||
|
|
||||||
|
py::object obj = py::cast(m);
|
||||||
|
EXPECT_EVAL_EQ("{'a': Point(1, 2), 'b': Point(3, 4)}", obj);
|
||||||
|
|
||||||
|
std::unordered_map<std::string, Point> m2 = obj.cast<std::unordered_map<std::string, Point>>();
|
||||||
|
EXPECT_EQ(m, m2);
|
||||||
|
}
|
||||||
|
}
|
16
include/pybind11/tests/test.h
Normal file
16
include/pybind11/tests/test.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <pybind11/pkbind.h>
|
||||||
|
|
||||||
|
namespace py = pkbind;
|
||||||
|
|
||||||
|
class PYBIND11_TEST : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override { py::initialize(); }
|
||||||
|
|
||||||
|
void TearDown() override { py::finalize(true); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#define EXPECT_CAST_EQ(expr, expected) EXPECT_EQ(py::cast(expr), py::cast(expected))
|
||||||
|
#define EXPECT_EVAL_EQ(expr, expected) EXPECT_EQ(py::eval(expr).cast<decltype(expected)>(), expected)
|
||||||
|
#define EXPECT_EXEC_EQ(expr, expected) EXPECT_EQ(py::eval(expr), py::eval(expected))
|
||||||
|
|
169
include/pybind11/tests/types.cpp
Normal file
169
include/pybind11/tests/types.cpp
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, int) {
|
||||||
|
py::object obj = py::int_(123);
|
||||||
|
py::object obj2 = py::eval("123");
|
||||||
|
|
||||||
|
EXPECT_EQ(obj, obj2);
|
||||||
|
|
||||||
|
EXPECT_EQ(obj.cast<int>(), 123);
|
||||||
|
EXPECT_EQ(obj.cast<long>(), 123);
|
||||||
|
EXPECT_EQ(obj.cast<long long>(), 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, float) {
|
||||||
|
py::object obj = py::float_(123.0);
|
||||||
|
py::object obj2 = py::eval("123.0");
|
||||||
|
|
||||||
|
EXPECT_EQ(obj, obj2);
|
||||||
|
|
||||||
|
EXPECT_EQ(obj.cast<float>(), 123.0);
|
||||||
|
EXPECT_EQ(obj.cast<double>(), 123.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, str) {
|
||||||
|
py::object obj = py::str("123");
|
||||||
|
py::object obj2 = py::eval("'123'");
|
||||||
|
|
||||||
|
EXPECT_EQ(obj, obj2);
|
||||||
|
|
||||||
|
EXPECT_STREQ(obj.cast<const char*>(), "123");
|
||||||
|
EXPECT_EQ(obj.cast<std::string>(), "123");
|
||||||
|
EXPECT_EQ(obj.cast<std::string_view>(), "123");
|
||||||
|
|
||||||
|
auto s = py::str("Hello, {}");
|
||||||
|
EXPECT_EQ(s.format("world").cast<std::string>(), "Hello, world");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, tuple) {
|
||||||
|
py::tuple tuple = py::tuple{
|
||||||
|
py::int_(1),
|
||||||
|
py::str("123"),
|
||||||
|
py::int_(3),
|
||||||
|
};
|
||||||
|
EXPECT_EQ(tuple, py::eval("(1, '123', 3)"));
|
||||||
|
EXPECT_EQ(tuple.size(), 3);
|
||||||
|
EXPECT_FALSE(tuple.empty());
|
||||||
|
|
||||||
|
tuple[0] = py::int_(3);
|
||||||
|
tuple[2] = py::int_(1);
|
||||||
|
EXPECT_EQ(tuple, py::eval("(3, '123', 1)"));
|
||||||
|
|
||||||
|
// iterators.
|
||||||
|
int index = 0;
|
||||||
|
for(auto item: tuple) {
|
||||||
|
if(index == 0) {
|
||||||
|
EXPECT_EQ(item, py::int_(3));
|
||||||
|
} else if(index == 1) {
|
||||||
|
EXPECT_EQ(item, py::str("123"));
|
||||||
|
} else if(index == 2) {
|
||||||
|
EXPECT_EQ(item, py::int_(1));
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, list) {
|
||||||
|
// constructors
|
||||||
|
py::list list = py::list();
|
||||||
|
EXPECT_EQ(list, py::eval("[]"));
|
||||||
|
EXPECT_EQ(list.size(), 0);
|
||||||
|
EXPECT_TRUE(list.empty());
|
||||||
|
|
||||||
|
list = py::list{
|
||||||
|
py::int_(1),
|
||||||
|
py::int_(2),
|
||||||
|
py::int_(3),
|
||||||
|
};
|
||||||
|
EXPECT_EQ(list, py::eval("[1, 2, 3]"));
|
||||||
|
EXPECT_EQ(list.size(), 3);
|
||||||
|
EXPECT_FALSE(list.empty());
|
||||||
|
|
||||||
|
// accessor
|
||||||
|
list[0] = py::int_(3);
|
||||||
|
list[2] = py::int_(1);
|
||||||
|
EXPECT_EQ(list, py::eval("[3, 2, 1]"));
|
||||||
|
|
||||||
|
// iterators
|
||||||
|
int index = 0;
|
||||||
|
for(auto item: list) {
|
||||||
|
if(index == 0) {
|
||||||
|
EXPECT_EQ(item, py::int_(3));
|
||||||
|
} else if(index == 1) {
|
||||||
|
EXPECT_EQ(item, py::int_(2));
|
||||||
|
} else if(index == 2) {
|
||||||
|
EXPECT_EQ(item, py::int_(1));
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// others
|
||||||
|
list.append(py::int_(4));
|
||||||
|
EXPECT_EQ(list, py::eval("[3, 2, 1, 4]"));
|
||||||
|
|
||||||
|
list.insert(0, py::int_(7));
|
||||||
|
EXPECT_EQ(list, py::eval("[7, 3, 2, 1, 4]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, dict) {
|
||||||
|
// constructors
|
||||||
|
py::dict dict = py::dict();
|
||||||
|
EXPECT_EQ(dict, py::eval("{}"));
|
||||||
|
EXPECT_EQ(dict.size(), 0);
|
||||||
|
EXPECT_TRUE(dict.empty());
|
||||||
|
|
||||||
|
// accessor
|
||||||
|
dict["a"] = py::int_(1);
|
||||||
|
dict["b"] = py::int_(2);
|
||||||
|
dict["c"] = py::int_(3);
|
||||||
|
EXPECT_EQ(dict, py::eval("{'a': 1, 'b': 2, 'c': 3}"));
|
||||||
|
EXPECT_EQ(dict,
|
||||||
|
py::dict({
|
||||||
|
{"a", py::int_(1)},
|
||||||
|
{"b", py::int_(2)},
|
||||||
|
{"c", py::int_(3)},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// iterators
|
||||||
|
int index = 0;
|
||||||
|
for(auto item: dict) {
|
||||||
|
if(index == 0) {
|
||||||
|
EXPECT_EQ(item.first.cast<std::string>(), "a");
|
||||||
|
EXPECT_EQ(item.second, py::int_(1));
|
||||||
|
} else if(index == 1) {
|
||||||
|
EXPECT_EQ(item.first.cast<std::string>(), "b");
|
||||||
|
EXPECT_EQ(item.second, py::int_(2));
|
||||||
|
} else if(index == 2) {
|
||||||
|
EXPECT_EQ(item.first.cast<std::string>(), "c");
|
||||||
|
EXPECT_EQ(item.second, py::int_(3));
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(PYBIND11_TEST, capsule) {
|
||||||
|
static int times = 0;
|
||||||
|
|
||||||
|
struct NotTrivial {
|
||||||
|
~NotTrivial() { times++; }
|
||||||
|
};
|
||||||
|
|
||||||
|
py::handle x = py::capsule(new NotTrivial(), [](void* ptr) {
|
||||||
|
delete static_cast<NotTrivial*>(ptr);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto m = py::module_::__main__();
|
||||||
|
|
||||||
|
m.def("foo", [](int x) {
|
||||||
|
return py::capsule(new int(x));
|
||||||
|
});
|
||||||
|
|
||||||
|
m.def("bar", [](py::capsule x, int y) {
|
||||||
|
EXPECT_EQ(x.cast<int>(), y);
|
||||||
|
delete (int*)x.data();
|
||||||
|
});
|
||||||
|
|
||||||
|
py::exec("bar(foo(123), 123)");
|
||||||
|
py::finalize(true);
|
||||||
|
EXPECT_EQ(times, 1);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user