mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
add pybind11. (#254)
This commit is contained in:
parent
f9ed436d03
commit
23bacf8b4d
117
include/pybind11/internal/builtins.h
Normal file
117
include/pybind11/internal/builtins.h
Normal file
@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
namespace pybind11 {
|
||||
inline void exec(const char* code, handle global = {}, handle local = {}) {
|
||||
vm->py_exec(code, global.ptr(), local.ptr());
|
||||
}
|
||||
|
||||
// wrapper for builtin functions in Python
|
||||
inline bool hasattr(const handle& obj, const handle& name) {
|
||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
||||
return vm->getattr(obj.ptr(), key, false) != nullptr;
|
||||
}
|
||||
|
||||
inline bool hasattr(const handle& obj, const char* name) {
|
||||
return vm->getattr(obj.ptr(), name, false) != nullptr;
|
||||
}
|
||||
|
||||
inline void delattr(const handle& obj, const handle& name) {
|
||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
||||
vm->delattr(obj.ptr(), key);
|
||||
}
|
||||
|
||||
inline void delattr(const handle& obj, const char* name) { vm->delattr(obj.ptr(), name); }
|
||||
|
||||
inline object getattr(const handle& obj, const handle& name) {
|
||||
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) {
|
||||
return reinterpret_borrow<object>(vm->getattr(obj.ptr(), name));
|
||||
}
|
||||
|
||||
inline object getattr(const handle& obj, const handle& name, const handle& default_) {
|
||||
if(!hasattr(obj, name)) {
|
||||
return reinterpret_borrow<object>(default_);
|
||||
}
|
||||
return getattr(obj, name);
|
||||
}
|
||||
|
||||
inline object getattr(const handle& obj, const char* name, const handle& default_) {
|
||||
if(!hasattr(obj, name)) {
|
||||
return reinterpret_borrow<object>(default_);
|
||||
}
|
||||
return getattr(obj, name);
|
||||
}
|
||||
|
||||
inline void setattr(const handle& obj, const handle& name, const handle& value) {
|
||||
auto& key = _builtin_cast<pkpy::Str>(name);
|
||||
vm->setattr(obj.ptr(), key, value.ptr());
|
||||
}
|
||||
|
||||
inline void setattr(const handle& obj, const char* name, const handle& value) {
|
||||
vm->setattr(obj.ptr(), name, value.ptr());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool isinstance(const handle& obj) {
|
||||
pkpy::Type cls = _builtin_cast<pkpy::Type>(type::handle_of<T>().ptr());
|
||||
return vm->isinstance(obj.ptr(), cls);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool isinstance<handle>(const 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>
|
||||
struct type_caster;
|
||||
|
||||
template <typename T>
|
||||
handle _cast(T&& value,
|
||||
return_value_policy policy = return_value_policy::automatic_reference,
|
||||
handle parent = handle()) {
|
||||
using U = std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>;
|
||||
return type_caster<U>::cast(std::forward<T>(value), policy, parent);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
object cast(T&& value,
|
||||
return_value_policy policy = return_value_policy::automatic_reference,
|
||||
handle parent = handle()) {
|
||||
return reinterpret_borrow<object>(_cast(std::forward<T>(value), policy, parent));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T cast(handle obj, bool convert = false) {
|
||||
using Caster =
|
||||
type_caster<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>;
|
||||
Caster caster;
|
||||
|
||||
if(caster.load(obj, convert)) {
|
||||
if constexpr(std::is_rvalue_reference_v<T>) {
|
||||
return std::move(caster.value);
|
||||
} else {
|
||||
return caster.value;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Unable to cast Python instance to C++ type");
|
||||
}
|
||||
} // namespace pybind11
|
173
include/pybind11/internal/cast.h
Normal file
173
include/pybind11/internal/cast.h
Normal file
@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
|
||||
#include "instance.h"
|
||||
#include "builtins.h"
|
||||
#include "type_traits.h"
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
using pkpy::is_floating_point_v;
|
||||
using pkpy::is_integral_v;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_string_v =
|
||||
std::is_same_v<T, char*> || std::is_same_v<T, const char*> ||
|
||||
std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_pyobject_v = std::is_base_of_v<handle, T>;
|
||||
|
||||
template <typename T, typename>
|
||||
struct type_caster;
|
||||
|
||||
template <>
|
||||
struct type_caster<bool> {
|
||||
bool value;
|
||||
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<pybind11::bool_>(src)) {
|
||||
value = pkpy::_py_cast<bool>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(bool src, return_value_policy, handle) {
|
||||
return src ? vm->True : vm->False;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_integral_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool convert) {
|
||||
if(isinstance<pybind11::int_>(src)) {
|
||||
value = pkpy::_py_cast<T>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_floating_point_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool convert) {
|
||||
if(isinstance<pybind11::float_>(src)) {
|
||||
value = pkpy::_py_cast<T>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
if(convert && isinstance<pybind11::int_>(src)) {
|
||||
value = pkpy::_py_cast<int64_t>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_string_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<pybind11::str>(src)) {
|
||||
// FIXME: support other kinds of string
|
||||
value = pkpy::_py_cast<std::string>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(const std::string& src, return_value_policy, handle) {
|
||||
return pkpy::py_var(vm, src);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_pyobject_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<T>(src)) {
|
||||
value = reinterpret_borrow<T>(src);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& src, return_value_policy, handle) {
|
||||
return std::forward<U>(src);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename>
|
||||
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>
|
||||
struct type_caster<T, std::enable_if_t<std::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>>;
|
||||
|
||||
struct wrapper {
|
||||
type_caster<underlying> caster;
|
||||
|
||||
operator T () {
|
||||
if constexpr(std::is_pointer_v<T>) {
|
||||
return caster.value.pointer;
|
||||
} else {
|
||||
return caster.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
wrapper value;
|
||||
|
||||
bool load(const handle& src, bool convert) { return value.caster.load(src, convert); }
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& value, return_value_policy policy, const handle& parent) {
|
||||
return type_caster<underlying>::cast(std::forward<U>(value), policy, parent);
|
||||
}
|
||||
};
|
||||
} // namespace pybind11
|
||||
|
184
include/pybind11/internal/class.h
Normal file
184
include/pybind11/internal/class.h
Normal file
@ -0,0 +1,184 @@
|
||||
#pragma once
|
||||
|
||||
#include "cpp_function.h"
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
class module : public object {
|
||||
|
||||
public:
|
||||
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 {
|
||||
public:
|
||||
using type::type;
|
||||
|
||||
template <typename... Args>
|
||||
class_(const handle& scope, const char* name, Args&&... args) :
|
||||
type(vm->new_type_object(scope.ptr(),
|
||||
name,
|
||||
vm->tp_object,
|
||||
false,
|
||||
pkpy::PyTypeInfo::Vt::get<instance>()),
|
||||
true) {
|
||||
pkpy::PyVar mod = scope.ptr();
|
||||
mod->attr().set(name, m_ptr);
|
||||
vm->_cxx_typeid_map[typeid(T)] = _builtin_cast<pkpy::Type>(m_ptr);
|
||||
vm->bind_func(m_ptr, "__new__", -1, [](pkpy::VM* vm, pkpy::ArgsView args) {
|
||||
auto cls = _builtin_cast<pkpy::Type>(args[0]);
|
||||
return instance::create<T>(cls);
|
||||
});
|
||||
}
|
||||
|
||||
/// bind constructor
|
||||
template <typename... Args, typename... Extra>
|
||||
class_& def(init<Args...>, const Extra&... extra) {
|
||||
if constexpr(!std::is_constructible_v<T, Args...>) {
|
||||
static_assert(std::is_constructible_v<T, Args...>, "Invalid constructor arguments");
|
||||
} else {
|
||||
bind_function(
|
||||
*this,
|
||||
"__init__",
|
||||
[](T* self, Args... args) { new (self) T(args...); },
|
||||
pkpy::BindType::DEFAULT,
|
||||
extra...);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
/// bind member function
|
||||
template <typename Fn, typename... Extra>
|
||||
class_& def(const char* name, Fn&& f, const Extra&... extra) {
|
||||
using first = 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>>;
|
||||
|
||||
if constexpr(!is_first_base_of_v) {
|
||||
static_assert(
|
||||
is_first_base_of_v,
|
||||
"If you want to bind member function, the first argument must be the base class");
|
||||
} else {
|
||||
bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::DEFAULT, extra...);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// bind operators
|
||||
template <typename Operator, typename... Extras>
|
||||
class_& def(Operator op, const Extras&... extras) {
|
||||
op.execute(*this, extras...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TODO: factory function
|
||||
|
||||
/// bind static function
|
||||
template <typename Fn, typename... Extra>
|
||||
class_& def_static(const char* name, Fn&& f, const Extra&... extra) {
|
||||
bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::STATICMETHOD, extra...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename MP, typename... Extras>
|
||||
class_& def_readwrite(const char* name, MP mp, const Extras&... extras) {
|
||||
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
||||
static_assert(std::is_member_object_pointer_v<MP>,
|
||||
"def_readwrite only supports pointer to data member");
|
||||
} else {
|
||||
bind_property(*this, name, mp, mp, extras...);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename MP, typename... Extras>
|
||||
class_& def_readonly(const char* name, MP mp, const Extras&... extras) {
|
||||
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
||||
static_assert(std::is_member_object_pointer_v<MP>,
|
||||
"def_readonly only supports pointer to data member");
|
||||
} else {
|
||||
bind_property(*this, name, mp, nullptr, extras...);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Getter, typename Setter, typename... Extras>
|
||||
class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
||||
bind_property(*this, name, std::forward<Getter>(g), std::forward<Setter>(s), extras...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Getter, typename... Extras>
|
||||
class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) {
|
||||
bind_property(*this, name, std::forward<Getter>(mp), nullptr, extras...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Var, typename... Extras>
|
||||
class_& def_readwrite_static(const char* name, Var& mp, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Var>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Var, typename... Extras>
|
||||
class_& def_readonly_static(const char* name, Var& mp, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Var>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Getter, typename Setter, typename... Extras>
|
||||
class_&
|
||||
def_property_static(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Getter>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename... Others>
|
||||
class enum_ : public class_<T, Others...> {
|
||||
std::map<const char*, pkpy::PyVar> m_values;
|
||||
|
||||
public:
|
||||
using class_<T, Others...>::class_;
|
||||
|
||||
template <typename... Args>
|
||||
enum_(const handle& scope, const char* name, Args&&... args) :
|
||||
class_<T, Others...>(scope, name, std::forward<Args>(args)...) {}
|
||||
|
||||
enum_& value(const char* name, T value) {
|
||||
handle var = type_caster<T>::cast(value, return_value_policy::copy);
|
||||
this->m_ptr->attr().set(name, var.ptr());
|
||||
m_values[name] = var.ptr();
|
||||
return *this;
|
||||
}
|
||||
|
||||
enum_& export_values() {
|
||||
pkpy::PyVar mod = this->m_ptr->attr("__module__");
|
||||
for(auto& [name, value]: m_values) {
|
||||
mod->attr().set(name, value);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
} // namespace pybind11
|
383
include/pybind11/internal/cpp_function.h
Normal file
383
include/pybind11/internal/cpp_function.h
Normal file
@ -0,0 +1,383 @@
|
||||
#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
|
147
include/pybind11/internal/instance.h
Normal file
147
include/pybind11/internal/instance.h
Normal file
@ -0,0 +1,147 @@
|
||||
#pragma once
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
namespace pybind11 {
|
||||
struct type_info {
|
||||
const char* name;
|
||||
std::size_t size;
|
||||
std::size_t alignment;
|
||||
void (*destructor)(void*);
|
||||
void (*copy)(void*, const void*);
|
||||
void (*move)(void*, void*);
|
||||
const std::type_info* type;
|
||||
|
||||
template <typename T>
|
||||
static type_info& of() {
|
||||
static_assert(!std::is_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
|
||||
"T must not be a reference type or const type.");
|
||||
static type_info info = {
|
||||
typeid(T).name(),
|
||||
sizeof(T),
|
||||
alignof(T),
|
||||
[](void* ptr) {
|
||||
((T*)ptr)->~T();
|
||||
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),
|
||||
};
|
||||
return info;
|
||||
}
|
||||
};
|
||||
|
||||
// all registered C++ class will be ensured as instance type.
|
||||
class instance {
|
||||
public:
|
||||
// use to record the type information of C++ class.
|
||||
|
||||
private:
|
||||
enum Flag {
|
||||
None = 0,
|
||||
Own = 1 << 0, // if the instance is owned by C++ side.
|
||||
Ref = 1 << 1, // need to mark the parent object.
|
||||
};
|
||||
|
||||
Flag flag;
|
||||
void* data;
|
||||
const type_info* type;
|
||||
pkpy::PyVar parent;
|
||||
// pkpy::PyVar
|
||||
|
||||
public:
|
||||
instance() noexcept : flag(Flag::None), data(nullptr), type(nullptr), parent(nullptr) {}
|
||||
|
||||
instance(const instance&) = delete;
|
||||
|
||||
instance(instance&& other) noexcept :
|
||||
flag(other.flag), data(other.data), type(other.type), parent(other.parent) {
|
||||
other.flag = Flag::None;
|
||||
other.data = nullptr;
|
||||
other.type = nullptr;
|
||||
other.parent = nullptr;
|
||||
}
|
||||
|
||||
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>) {
|
||||
return *reinterpret_cast<underlying_type*>(value);
|
||||
} else {
|
||||
return const_cast<underlying_type&>(value);
|
||||
}
|
||||
}();
|
||||
|
||||
instance instance;
|
||||
instance.type = &type_info::of<underlying_type>();
|
||||
|
||||
if(policy == return_value_policy::take_ownership) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::Own;
|
||||
} else if(policy == return_value_policy::copy) {
|
||||
instance.data = ::new auto(_value);
|
||||
instance.flag = Flag::Own;
|
||||
} else if(policy == return_value_policy::move) {
|
||||
instance.data = ::new auto(std::move(_value));
|
||||
instance.flag = Flag::Own;
|
||||
} else if(policy == return_value_policy::reference) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::None;
|
||||
} else if(policy == return_value_policy::reference_internal) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::Ref;
|
||||
instance.parent = parent;
|
||||
}
|
||||
|
||||
return vm->new_object<pybind11::instance>(type, std::move(instance));
|
||||
}
|
||||
|
||||
~instance() {
|
||||
if(flag & Flag::Own) {
|
||||
type->destructor(data);
|
||||
}
|
||||
}
|
||||
|
||||
void _gc_mark(pkpy::VM* vm) const noexcept {
|
||||
if(parent && (flag & Flag::Ref)) {
|
||||
PK_OBJ_MARK(parent);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& cast() noexcept {
|
||||
return *static_cast<T*>(data);
|
||||
}
|
||||
};
|
||||
} // namespace pybind11
|
108
include/pybind11/internal/kernel.h
Normal file
108
include/pybind11/internal/kernel.h
Normal file
@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include <pocketpy.h>
|
||||
|
||||
namespace pybind11
|
||||
{
|
||||
inline pkpy::VM* vm = nullptr;
|
||||
inline std::map<pkpy::PyVar, int*>* _ref_counts_map = nullptr;
|
||||
|
||||
inline void initialize(bool enable_os = true)
|
||||
{
|
||||
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.
|
||||
vm->heap._gc_marker_ex = [](pkpy::VM* vm)
|
||||
{
|
||||
for(auto iter = _ref_counts_map->begin(); iter != _ref_counts_map->end();)
|
||||
{
|
||||
auto ref_count = iter->second;
|
||||
if(*ref_count != 0)
|
||||
{
|
||||
// if ref count is not zero, then mark it.
|
||||
PK_OBJ_MARK(iter->first);
|
||||
++iter;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if ref count is zero, then delete it.
|
||||
iter = _ref_counts_map->erase(iter);
|
||||
delete ref_count;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
inline void finalize()
|
||||
{
|
||||
delete _ref_counts_map;
|
||||
delete vm;
|
||||
}
|
||||
|
||||
enum class return_value_policy : uint8_t
|
||||
{
|
||||
/**
|
||||
* This is the default return value policy, which falls back to the policy
|
||||
* return_value_policy::take_ownership when the return value is a pointer.
|
||||
* Otherwise, it uses return_value::move or return_value::copy for rvalue
|
||||
* and lvalue references, respectively. See below for a description of what
|
||||
* all of these different policies do.
|
||||
*/
|
||||
automatic = 0,
|
||||
|
||||
/**
|
||||
* As above, but use policy return_value_policy::reference when the return
|
||||
* value is a pointer. This is the default conversion policy for function
|
||||
* arguments when calling Python functions manually from C++ code (i.e. via
|
||||
* handle::operator()). You probably won't need to use this.
|
||||
*/
|
||||
automatic_reference,
|
||||
|
||||
/**
|
||||
* Reference an existing object (i.e. do not create a new copy) and take
|
||||
* ownership. Python will call the destructor and delete operator when the
|
||||
* object's reference count reaches zero. Undefined behavior ensues when
|
||||
* the C++ side does the same..
|
||||
*/
|
||||
take_ownership,
|
||||
|
||||
/**
|
||||
* Create a new copy of the returned object, which will be owned by
|
||||
* Python. This policy is comparably safe because the lifetimes of the two
|
||||
* instances are decoupled.
|
||||
*/
|
||||
copy,
|
||||
|
||||
/**
|
||||
* Use std::move to move the return value contents into a new instance
|
||||
* that will be owned by Python. This policy is comparably safe because the
|
||||
* lifetimes of the two instances (move source and destination) are
|
||||
* decoupled.
|
||||
*/
|
||||
move,
|
||||
|
||||
/**
|
||||
* Reference an existing object, but do not take ownership. The C++ side
|
||||
* is responsible for managing the object's lifetime and deallocating it
|
||||
* when it is no longer used. Warning: undefined behavior will ensue when
|
||||
* the C++ side deletes an object that is still referenced and used by
|
||||
* Python.
|
||||
*/
|
||||
reference,
|
||||
|
||||
/**
|
||||
* This policy only applies to methods and properties. It references the
|
||||
* object without taking ownership similar to the above
|
||||
* return_value_policy::reference policy. In contrast to that policy, the
|
||||
* function or property's implicit this argument (called the parent) is
|
||||
* considered to be the the owner of the return value (the child).
|
||||
* pybind11 then couples the lifetime of the parent to the child via a
|
||||
* reference relationship that ensures that the parent cannot be garbage
|
||||
* collected while Python is still using the child. More advanced
|
||||
* variations of this scheme are also possible using combinations of
|
||||
* return_value_policy::reference and the keep_alive call policy
|
||||
*/
|
||||
reference_internal
|
||||
};
|
||||
} // namespace pybind11
|
256
include/pybind11/internal/object.h
Normal file
256
include/pybind11/internal/object.h
Normal file
@ -0,0 +1,256 @@
|
||||
#pragma once
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
namespace pybind11 {
|
||||
class handle;
|
||||
class object;
|
||||
class attr_accessor;
|
||||
class item_accessor;
|
||||
class iterator;
|
||||
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>
|
||||
T& _builtin_cast(const handle& obj);
|
||||
|
||||
template <typename T>
|
||||
T reinterpret_borrow(const handle& h);
|
||||
|
||||
template <typename T>
|
||||
T reinterpret_steal(const handle& h);
|
||||
|
||||
class handle {
|
||||
protected:
|
||||
pkpy::PyVar m_ptr = nullptr;
|
||||
mutable int* ref_count = nullptr;
|
||||
|
||||
public:
|
||||
handle() = default;
|
||||
handle(const handle& h) = default;
|
||||
handle& operator= (const handle& other) = default;
|
||||
|
||||
handle(pkpy::PyVar ptr) : m_ptr(ptr) {}
|
||||
|
||||
pkpy::PyVar ptr() const { return m_ptr; }
|
||||
|
||||
int reference_count() const { return ref_count == nullptr ? 0 : *ref_count; }
|
||||
|
||||
const handle& inc_ref() const {
|
||||
PK_DEBUG_ASSERT(m_ptr != nullptr);
|
||||
if(ref_count == nullptr) {
|
||||
auto iter = _ref_counts_map->find(m_ptr);
|
||||
if(iter == _ref_counts_map->end()) {
|
||||
ref_count = ::new int(1);
|
||||
_ref_counts_map->insert({m_ptr, ref_count});
|
||||
} else {
|
||||
ref_count = iter->second;
|
||||
*ref_count += 1;
|
||||
}
|
||||
} else {
|
||||
*ref_count += 1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const handle& dec_ref() const {
|
||||
PK_DEBUG_ASSERT(m_ptr != nullptr);
|
||||
PK_DEBUG_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;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
T cast() const;
|
||||
|
||||
explicit operator bool () const { return m_ptr.operator bool (); }
|
||||
|
||||
bool is(const handle& other) const { return m_ptr == other.m_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 end() const;
|
||||
|
||||
str doc() 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;
|
||||
|
||||
template <return_value_policy policy = return_value_policy::automatic, typename... Args>
|
||||
object operator() (Args&&... args) const;
|
||||
|
||||
private:
|
||||
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>
|
||||
friend T& _builtin_cast(const handle& obj) {
|
||||
// FIXME: 2.0 does not use Py_<T> anymore
|
||||
static_assert(!std::is_reference_v<T>, "T must not be a reference type.");
|
||||
return obj.ptr().obj_get<T>();
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<handle>);
|
||||
|
||||
class object : public handle {
|
||||
public:
|
||||
object(const object& other) : handle(other) { inc_ref(); }
|
||||
|
||||
object(object&& other) noexcept : handle(other) {
|
||||
other.m_ptr = nullptr;
|
||||
other.ref_count = nullptr;
|
||||
}
|
||||
|
||||
object& operator= (const object& other) {
|
||||
if(this != &other) {
|
||||
dec_ref();
|
||||
m_ptr = other.m_ptr;
|
||||
ref_count = other.ref_count;
|
||||
inc_ref();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
object& operator= (object&& other) noexcept {
|
||||
if(this != &other) {
|
||||
dec_ref();
|
||||
m_ptr = other.m_ptr;
|
||||
ref_count = other.ref_count;
|
||||
other.m_ptr = nullptr;
|
||||
other.ref_count = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~object() {
|
||||
if(m_ptr != nullptr) {
|
||||
dec_ref();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
object(const handle& h, bool borrow) : handle(h) {
|
||||
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);
|
||||
inline void setitem(const handle& obj, const handle& key, const handle& value);
|
||||
|
||||
#define PYBIND11_BINARY_OPERATOR(OP, NAME) \
|
||||
inline object operator OP (const handle& lhs, const handle& rhs) { \
|
||||
return reinterpret_borrow<object>(vm->call(vm->py_op(NAME), lhs.m_ptr, rhs.m_ptr)); \
|
||||
}
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(+, "add");
|
||||
PYBIND11_BINARY_OPERATOR(-, "sub");
|
||||
PYBIND11_BINARY_OPERATOR(*, "mul");
|
||||
PYBIND11_BINARY_OPERATOR(/, "truediv");
|
||||
PYBIND11_BINARY_OPERATOR(%, "mod");
|
||||
PYBIND11_BINARY_OPERATOR(|, "or_");
|
||||
PYBIND11_BINARY_OPERATOR(&, "and_");
|
||||
PYBIND11_BINARY_OPERATOR(^, "xor");
|
||||
PYBIND11_BINARY_OPERATOR(<<, "lshift");
|
||||
PYBIND11_BINARY_OPERATOR(>>, "rshift");
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(+=, "iadd");
|
||||
PYBIND11_BINARY_OPERATOR(-=, "isub");
|
||||
PYBIND11_BINARY_OPERATOR(*=, "imul");
|
||||
PYBIND11_BINARY_OPERATOR(/=, "itruediv");
|
||||
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");
|
||||
PYBIND11_BINARY_OPERATOR(!=, "ne");
|
||||
PYBIND11_BINARY_OPERATOR(<, "lt");
|
||||
PYBIND11_BINARY_OPERATOR(>, "gt");
|
||||
PYBIND11_BINARY_OPERATOR(<=, "le");
|
||||
PYBIND11_BINARY_OPERATOR(>=, "ge");
|
||||
|
||||
#undef PYBIND11_BINARY_OPERATOR
|
||||
|
||||
} // namespace pybind11
|
157
include/pybind11/internal/type_traits.h
Normal file
157
include/pybind11/internal/type_traits.h
Normal file
@ -0,0 +1,157 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace pybind11 {
|
||||
template <typename T>
|
||||
constexpr bool dependent_false = false;
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
struct tuple_push_front;
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
struct tuple_push_front<T, std::tuple<Ts...>> {
|
||||
using type = std::tuple<T, Ts...>;
|
||||
};
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
using tuple_push_front_t = typename tuple_push_front<T, Tuple>::type;
|
||||
|
||||
// traits for function types
|
||||
template <typename Fn>
|
||||
struct function_traits {
|
||||
static_assert(dependent_false<Fn>, "unsupported function type");
|
||||
};
|
||||
|
||||
#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(qualifiers) \
|
||||
template <typename R, typename... Args> \
|
||||
struct function_traits<R(Args...) qualifiers> { \
|
||||
using return_type = R; \
|
||||
using args_type = std::tuple<Args...>; \
|
||||
constexpr static std::size_t args_count = sizeof...(Args); \
|
||||
};
|
||||
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE()
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(&)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const&)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(noexcept)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(& noexcept)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const noexcept)
|
||||
PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const& noexcept)
|
||||
|
||||
#undef PYBIND11_FUNCTION_TRAITS_SPECIALIZE
|
||||
|
||||
template <typename T>
|
||||
using function_return_t = typename function_traits<T>::return_type;
|
||||
|
||||
template <typename T>
|
||||
using function_args_t = typename function_traits<T>::args_type;
|
||||
|
||||
template <typename T>
|
||||
constexpr std::size_t function_args_count = function_traits<T>::args_count;
|
||||
|
||||
// traits for member pointers
|
||||
template <typename T>
|
||||
struct member_traits;
|
||||
|
||||
template <typename M, typename C>
|
||||
struct member_traits<M C::*> {
|
||||
using member_type = M;
|
||||
using class_type = C;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using member_type_t = typename member_traits<T>::member_type;
|
||||
|
||||
template <typename T>
|
||||
using class_type_t = typename member_traits<T>::class_type;
|
||||
|
||||
// some traits for distinguishing between function pointers, member function pointers and
|
||||
// functors
|
||||
using std::is_member_function_pointer_v;
|
||||
using std::is_member_object_pointer_v;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_function_pointer_v = std::is_function_v<std::remove_pointer_t<T>>;
|
||||
|
||||
template <typename T, typename U = void>
|
||||
constexpr bool is_functor_v = false;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_functor_v<T, std::void_t<decltype(&T::operator())>> = true;
|
||||
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct callable_traits;
|
||||
|
||||
template <typename T>
|
||||
struct callable_traits<T, std::enable_if_t<is_member_function_pointer_v<T>>> {
|
||||
using args_type = tuple_push_front_t<class_type_t<T>&, function_args_t<member_type_t<T>>>;
|
||||
using return_type = function_return_t<member_type_t<T>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct callable_traits<T, std::enable_if_t<is_function_pointer_v<T>>> {
|
||||
using args_type = function_args_t<std::remove_pointer<T>>;
|
||||
using return_type = function_return_t<std::remove_pointer<T>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct callable_traits<T, std::enable_if_t<is_functor_v<T>>> {
|
||||
using args_type = function_args_t<member_type_t<decltype(&T::operator())>>;
|
||||
using return_type = function_return_t<member_type_t<decltype(&T::operator())>>;
|
||||
};
|
||||
|
||||
template <typename Callable>
|
||||
using callable_args_t = typename callable_traits<Callable>::args_type;
|
||||
|
||||
template <typename Callable>
|
||||
using callable_return_t = typename callable_traits<Callable>::return_type;
|
||||
|
||||
template <typename Callable>
|
||||
constexpr std::size_t callable_args_count_v = std::tuple_size_v<callable_args_t<Callable>>;
|
||||
|
||||
template <typename T>
|
||||
struct type_identity {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
constexpr inline std::size_t types_count_v = (std::is_same_v<T, Ts> + ...);
|
||||
|
||||
template <typename T>
|
||||
constexpr inline std::size_t types_count_v<T> = 0;
|
||||
|
||||
template <typename T>
|
||||
struct value_wrapper {
|
||||
T* pointer;
|
||||
|
||||
operator T& () { return *pointer; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct value_wrapper<T*> {
|
||||
T* pointer;
|
||||
|
||||
operator T* () { return pointer; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
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
|
218
include/pybind11/internal/types.h
Normal file
218
include/pybind11/internal/types.h
Normal file
@ -0,0 +1,218 @@
|
||||
#pragma once
|
||||
|
||||
#include "object.h"
|
||||
|
||||
namespace pybind11 {
|
||||
class type : public object {
|
||||
public:
|
||||
using object::object;
|
||||
template <typename T>
|
||||
static handle handle_of();
|
||||
};
|
||||
|
||||
class iterable : public object {
|
||||
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:
|
||||
using object::object;
|
||||
str(const char* c, int len) :
|
||||
object(vm->new_object<pkpy::Str>(pkpy::VM::tp_str, c, len), true) {
|
||||
|
||||
};
|
||||
|
||||
str(const char* c = "") : str(c, strlen(c)) {}
|
||||
|
||||
str(const std::string& s) : str(s.data(), s.size()) {}
|
||||
|
||||
str(std::string_view sv) : str(sv.data(), sv.size()) {}
|
||||
|
||||
explicit str(const bytes& b);
|
||||
explicit str(handle h);
|
||||
operator std::string () const;
|
||||
|
||||
template <typename... Args>
|
||||
str format(Args&&... args) const;
|
||||
};
|
||||
|
||||
class int_ : public object {
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
int_(int64_t value) : object(pkpy::py_var(vm, value), true) {}
|
||||
};
|
||||
|
||||
class float_ : public object {
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
float_(double value) : object(pkpy::py_var(vm, value), true) {}
|
||||
};
|
||||
|
||||
class bool_ : public object {
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
bool_(bool value) : object(pkpy::py_var(vm, value), true) {}
|
||||
};
|
||||
|
||||
class function : public object {
|
||||
public:
|
||||
using object::object;
|
||||
};
|
||||
|
||||
class attr_accessor : public object {
|
||||
private:
|
||||
object key;
|
||||
|
||||
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 {
|
||||
return attr_accessor(reinterpret_borrow<object>(*this), str(name));
|
||||
}
|
||||
|
||||
inline attr_accessor handle::attr(const handle& name) const {
|
||||
return attr_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(name));
|
||||
}
|
||||
|
||||
inline attr_accessor handle::attr(object&& name) const {
|
||||
return attr_accessor(reinterpret_borrow<object>(*this), std::move(name));
|
||||
}
|
||||
|
||||
class item_accessor : public object {
|
||||
public:
|
||||
object key;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
item_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
|
||||
|
||||
template <typename T>
|
||||
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>
|
||||
item_accessor& operator= (object&& value) && {
|
||||
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 {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), int_(key));
|
||||
}
|
||||
|
||||
inline item_accessor handle::operator[] (const char* key) const {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), str(key));
|
||||
}
|
||||
|
||||
inline item_accessor handle::operator[] (const handle& key) const {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(key));
|
||||
}
|
||||
|
||||
inline item_accessor handle::operator[] (object&& key) const {
|
||||
return item_accessor(reinterpret_borrow<object>(*this), std::move(key));
|
||||
}
|
||||
|
||||
class args : public tuple {
|
||||
using tuple::tuple;
|
||||
};
|
||||
|
||||
class kwargs : public dict {
|
||||
using dict::dict;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
handle type::handle_of() {
|
||||
if constexpr(std::is_same_v<T, object>) {
|
||||
return vm->_t(vm->tp_object);
|
||||
}
|
||||
#define PYBIND11_TYPE_MAPPER(type, tp) \
|
||||
else if constexpr(std::is_same_v<T, type>) { \
|
||||
return vm->_t(vm->tp); \
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pybind11
|
3
include/pybind11/pybind11.h
Normal file
3
include/pybind11/pybind11.h
Normal file
@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
#include "internal/class.h"
|
Loading…
x
Reference in New Issue
Block a user