mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-19 19:10:17 +00:00
Compare commits
8 Commits
6736fa7f50
...
fece79b169
Author | SHA1 | Date | |
---|---|---|---|
|
fece79b169 | ||
|
95553438e1 | ||
|
a9d296dedc | ||
|
21d886f1a4 | ||
|
435ded95b5 | ||
|
f937fc1da8 | ||
|
71432a4054 | ||
|
cc4dd34c86 |
2
include/pybind11/embed.h
Normal file
2
include/pybind11/embed.h
Normal file
@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
#include "pybind11.h"
|
2
include/pybind11/functional.h
Normal file
2
include/pybind11/functional.h
Normal file
@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
#include "pybind11.h"
|
173
include/pybind11/internal/accessor.h
Normal file
173
include/pybind11/internal/accessor.h
Normal file
@ -0,0 +1,173 @@
|
||||
#pragma once
|
||||
#include "builtins.h"
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
// implement iterator methods for interface
|
||||
template <typename Derived>
|
||||
inline iterator interface<Derived>::begin() const {
|
||||
return handle(vm->py_iter(this->ptr()));
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
inline iterator interface<Derived>::end() const {
|
||||
return iterator::sentinel();
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
inline str interface<Derived>::package() const {
|
||||
return handle(this->attr(pkpy::__package__));
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
inline str interface<Derived>::name() const {
|
||||
return handle(this->attr(pkpy::__name__));
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
inline str interface<Derived>::repr() const {
|
||||
return handle(str(vm->py_repr(this->ptr())));
|
||||
}
|
||||
|
||||
template <typename policy>
|
||||
class accessor : public interface<accessor<policy>> {
|
||||
|
||||
using key_type = typename policy::key_type;
|
||||
|
||||
handle m_obj;
|
||||
mutable handle m_value;
|
||||
key_type m_key;
|
||||
|
||||
friend interface<handle>;
|
||||
friend interface<accessor<policy>>;
|
||||
friend tuple;
|
||||
friend list;
|
||||
friend dict;
|
||||
|
||||
accessor(const handle& obj, key_type key) : m_obj(obj), m_value(), m_key(key) {}
|
||||
|
||||
public:
|
||||
pkpy::PyVar ptr() const {
|
||||
if(!m_value) { m_value = policy::get(m_obj, m_key); }
|
||||
return m_value.ptr();
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
accessor& operator= (Value&& value) && {
|
||||
policy::set(m_obj, m_key, std::forward<Value>(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
accessor& operator= (Value&& value) & {
|
||||
m_value = std::forward<Value>(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T cast() const {
|
||||
return operator handle ().template cast<T>();
|
||||
}
|
||||
|
||||
operator handle () const { return ptr(); }
|
||||
};
|
||||
|
||||
namespace policy {
|
||||
struct attr {
|
||||
using key_type = pkpy::StrName;
|
||||
|
||||
static handle get(const handle& obj, pkpy::StrName key) { return vm->getattr(obj.ptr(), key); }
|
||||
|
||||
static void set(const handle& obj, pkpy::StrName key, const handle& value) {
|
||||
vm->setattr(obj.ptr(), key, value.ptr());
|
||||
}
|
||||
};
|
||||
|
||||
struct item {
|
||||
using key_type = handle;
|
||||
|
||||
static handle get(const handle& obj, const handle& key) {
|
||||
return vm->call(vm->py_op("getitem"), obj.ptr(), key.ptr());
|
||||
}
|
||||
|
||||
static void set(const handle& obj, const handle& key, const handle& value) {
|
||||
vm->call(vm->py_op("setitem"), obj.ptr(), key.ptr(), value.ptr());
|
||||
}
|
||||
};
|
||||
|
||||
struct tuple {
|
||||
using key_type = int;
|
||||
|
||||
static handle get(const handle& obj, int key) { return obj._as<pkpy::Tuple>()[key]; }
|
||||
|
||||
static void set(const handle& obj, size_t key, const handle& value) { obj._as<pkpy::Tuple>()[key] = value.ptr(); }
|
||||
};
|
||||
|
||||
struct list {
|
||||
using key_type = int;
|
||||
|
||||
static handle get(const handle& obj, size_t key) { return obj._as<pkpy::List>()[key]; }
|
||||
|
||||
static void set(const handle& obj, size_t key, const handle& value) { obj._as<pkpy::List>()[key] = value.ptr(); }
|
||||
};
|
||||
|
||||
struct dict {
|
||||
using key_type = handle;
|
||||
|
||||
static handle get(const handle& obj, const handle& key) { return obj.cast<pybind11::dict>().getitem(key); }
|
||||
|
||||
static void set(const handle& obj, const handle& key, const handle& value) {
|
||||
obj.cast<pybind11::dict>().setitem(key, value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace policy
|
||||
|
||||
// implement other methods of interface
|
||||
|
||||
template <typename Derived>
|
||||
inline attr_accessor interface<Derived>::attr(pkpy::StrName key) const {
|
||||
return attr_accessor(this->ptr(), key);
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
inline attr_accessor interface<Derived>::attr(const char* key) const {
|
||||
return attr_accessor(this->ptr(), pkpy::StrName(key));
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
inline attr_accessor interface<Derived>::attr(const handle& key) const {
|
||||
return attr_accessor(this->ptr(), pkpy::StrName(key._as<pkpy::Str>()));
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
inline attr_accessor interface<Derived>::doc() const {
|
||||
return attr_accessor(this->ptr(), pkpy::StrName("__doc__"));
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
inline item_accessor interface<Derived>::operator[] (int index) const {
|
||||
return item_accessor(this->ptr(), int_(index));
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
inline item_accessor interface<Derived>::operator[] (const char* key) const {
|
||||
return item_accessor(this->ptr(), str(key));
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
inline item_accessor interface<Derived>::operator[] (const handle& key) const {
|
||||
return item_accessor(this->ptr(), key);
|
||||
}
|
||||
|
||||
inline tuple_accessor tuple::operator[] (int i) const { return tuple_accessor(this->ptr(), i); }
|
||||
|
||||
inline list_accessor list::operator[] (int i) const { return list_accessor(this->ptr(), i); }
|
||||
|
||||
inline dict_accessor dict::operator[] (int index) const { return dict_accessor(this->ptr(), int_(index)); }
|
||||
|
||||
inline dict_accessor dict::operator[] (std::string_view key) const { return dict_accessor(this->ptr(), str(key)); }
|
||||
|
||||
inline dict_accessor dict::operator[] (const handle& key) const { return dict_accessor(this->ptr(), key); }
|
||||
|
||||
} // namespace pybind11
|
222
include/pybind11/internal/builtins.h
Normal file
222
include/pybind11/internal/builtins.h
Normal file
@ -0,0 +1,222 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include "type_traits.h"
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
inline object eval(std::string_view code, const handle& global = none{}, handle local = none{}) {
|
||||
return vm->py_eval(code, global.ptr(), local.ptr());
|
||||
}
|
||||
|
||||
inline void exec(std::string_view code, const handle& global = none{}, handle local = none{}) {
|
||||
vm->py_exec(code, global.ptr(), local.ptr());
|
||||
}
|
||||
|
||||
/// globas() in pkpy is immutable, your changes will not be reflected in the Python interpreter
|
||||
inline dict globals() {
|
||||
auto& proxy = eval("globals()")._as<pkpy::MappingProxy>().attr();
|
||||
dict result;
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
proxy.apply(
|
||||
[](pkpy::StrName key, pkpy::PyVar value, void* data) {
|
||||
auto& dict = static_cast<pybind11::dict*>(data)->_as<pkpy::Dict>();
|
||||
auto key_ = pybind11::str(key.sv()).ptr();
|
||||
dict.set(vm, key_, value);
|
||||
},
|
||||
&result);
|
||||
#else
|
||||
proxy.apply([&](pkpy::StrName key, pkpy::PyVar value) {
|
||||
result.setitem(str(key.sv()), value);
|
||||
});
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
// wrapper for builtin functions in Python
|
||||
inline bool hasattr(const handle& obj, const handle& name) {
|
||||
return vm->getattr(obj.ptr(), name._as<pkpy::Str>(), false) != nullptr;
|
||||
}
|
||||
|
||||
inline bool hasattr(const handle& obj, const char* name) { return vm->getattr(obj.ptr(), name, false) != nullptr; }
|
||||
|
||||
inline void delattr(const handle& obj, const handle& name) { vm->delattr(obj.ptr(), name._as<pkpy::Str>()); }
|
||||
|
||||
inline void delattr(const handle& obj, const char* name) { vm->delattr(obj.ptr(), name); }
|
||||
|
||||
inline object getattr(const handle& obj, const handle& name) { return vm->getattr(obj.ptr(), name._as<pkpy::Str>()); }
|
||||
|
||||
inline object getattr(const handle& obj, const char* name) { return vm->getattr(obj.ptr(), name); }
|
||||
|
||||
inline object getattr(const handle& obj, const handle& name, const handle& default_) {
|
||||
auto attr = vm->getattr(obj.ptr(), name._as<pkpy::Str>(), false);
|
||||
if(attr) { return attr; }
|
||||
return default_;
|
||||
}
|
||||
|
||||
inline object getattr(const handle& obj, const char* name, const handle& default_) {
|
||||
auto attr = vm->getattr(obj.ptr(), name, false);
|
||||
if(attr) { return attr; }
|
||||
return default_;
|
||||
}
|
||||
|
||||
inline void setattr(const handle& obj, const handle& name, const handle& value) {
|
||||
vm->setattr(obj.ptr(), name._as<pkpy::Str>(), value.ptr());
|
||||
}
|
||||
|
||||
inline void setattr(const handle& obj, const char* name, const handle& value) {
|
||||
vm->setattr(obj.ptr(), name, value.ptr());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool isinstance(const handle& obj) {
|
||||
return type_visitor::check<T>(obj);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool isinstance<handle>(const handle&) = delete;
|
||||
|
||||
inline bool isinstance(const handle& obj, const handle& type) {
|
||||
return vm->isinstance(obj.ptr(), type._as<pkpy::Type>());
|
||||
}
|
||||
|
||||
inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); }
|
||||
|
||||
template <typename T, typename SFINAE = void>
|
||||
struct type_caster;
|
||||
|
||||
template <typename T>
|
||||
handle cast(T&& value, return_value_policy policy, handle parent) {
|
||||
// decay_t can resolve c-array type, but remove_cv_ref_t can't.
|
||||
using underlying_type = std::decay_t<T>;
|
||||
|
||||
if constexpr(std::is_convertible_v<underlying_type, handle>) {
|
||||
return std::forward<T>(value);
|
||||
} else {
|
||||
static_assert(!is_multiple_pointer_v<underlying_type>, "multiple pointer is not supported.");
|
||||
static_assert(!std::is_void_v<std::remove_pointer_t<underlying_type>>,
|
||||
"void* is not supported, consider using py::capsule.");
|
||||
|
||||
// resolve for automatic policy.
|
||||
if(policy == return_value_policy::automatic) {
|
||||
policy = std::is_pointer_v<underlying_type> ? return_value_policy::take_ownership
|
||||
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
|
||||
: return_value_policy::move;
|
||||
} else if(policy == return_value_policy::automatic_reference) {
|
||||
policy = std::is_pointer_v<underlying_type> ? return_value_policy::reference
|
||||
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
|
||||
: return_value_policy::move;
|
||||
}
|
||||
|
||||
return type_caster<underlying_type>::cast(std::forward<T>(value), policy, parent);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T cast(const handle& obj, bool convert) {
|
||||
assert(obj.ptr() != nullptr);
|
||||
|
||||
type_caster<T> caster = {};
|
||||
|
||||
if(caster.load(obj, convert)) {
|
||||
return caster.value;
|
||||
} else {
|
||||
std::string msg = "cast python instance to c++ failed, ";
|
||||
msg += "obj type is: {";
|
||||
msg += type::of(obj).name();
|
||||
msg += "}, target type is: {";
|
||||
msg += type_name<T>();
|
||||
msg += "}.";
|
||||
vm->TypeError(msg);
|
||||
PK_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
struct kwargs_proxy {
|
||||
handle value;
|
||||
};
|
||||
|
||||
struct args_proxy {
|
||||
handle value;
|
||||
|
||||
kwargs_proxy operator* () { return kwargs_proxy{value}; }
|
||||
};
|
||||
|
||||
template <typename Derived>
|
||||
args_proxy interface<Derived>::operator* () const {
|
||||
return args_proxy{ptr()};
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
handle interpreter::vectorcall(const handle& callable, const handle& self, const Args&... args) {
|
||||
vm->s_data.push(callable.ptr());
|
||||
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
vm->s_data.push(self ? self.ptr() : PY_NULL);
|
||||
#else
|
||||
vm->s_data.push(self ? self.ptr() : pkpy::PY_NULL);
|
||||
#endif
|
||||
|
||||
int argc = 0;
|
||||
int kwargsc = 0;
|
||||
|
||||
auto push_arg = [&](const handle& value) {
|
||||
assert(value);
|
||||
vm->s_data.push(value.ptr());
|
||||
argc++;
|
||||
};
|
||||
|
||||
auto push_named_arg = [&](std::string_view name, const handle& value) {
|
||||
assert(value);
|
||||
vm->s_data.push(int_(pkpy::StrName(name).index).ptr());
|
||||
vm->s_data.push(value.ptr());
|
||||
kwargsc++;
|
||||
};
|
||||
|
||||
auto foreach_ = [&](const auto& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr(std::is_convertible_v<T, handle>) {
|
||||
push_arg(arg);
|
||||
} else if constexpr(std::is_same_v<T, args_proxy>) {
|
||||
pybind11::tuple args = arg.value.template cast<pybind11::tuple>();
|
||||
for(auto item: args) {
|
||||
push_arg(item);
|
||||
}
|
||||
} else if constexpr(std::is_same_v<T, pybind11::arg>) {
|
||||
push_named_arg(arg.name, arg.default_);
|
||||
} else if constexpr(std::is_same_v<T, kwargs_proxy>) {
|
||||
pybind11::dict kwargs = arg.value.template cast<pybind11::dict>();
|
||||
for(auto item: kwargs) {
|
||||
str name = item.first.template cast<str>();
|
||||
push_named_arg(name, item.second);
|
||||
}
|
||||
} else {
|
||||
static_assert(dependent_false<T>, "unsupported type");
|
||||
}
|
||||
};
|
||||
(foreach_(args), ...);
|
||||
|
||||
return vm->vectorcall(argc, kwargsc);
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
template <return_value_policy policy, typename... Args>
|
||||
inline object interface<Derived>::operator() (Args&&... args) const {
|
||||
auto _cast = [&](auto&& arg) {
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr(std::is_same_v<T, pybind11::arg> || std::is_same_v<T, kwargs_proxy> ||
|
||||
std::is_same_v<T, args_proxy> || std::is_convertible_v<T, handle>) {
|
||||
return arg;
|
||||
} else {
|
||||
return pybind11::cast(std::forward<decltype(arg)>(arg), policy);
|
||||
}
|
||||
};
|
||||
return interpreter::vectorcall(ptr(), handle(), _cast(std::forward<Args>(args))...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void print(Args&&... args) {
|
||||
handle print = getattr(vm->builtins, "print");
|
||||
print(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // namespace pybind11
|
174
include/pybind11/internal/cast.h
Normal file
174
include/pybind11/internal/cast.h
Normal file
@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
#include "instance.h"
|
||||
#include "accessor.h"
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
using pkpy::is_floating_point_v;
|
||||
using pkpy::is_integral_v;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_string_v =
|
||||
std::is_same_v<T, const char*> || std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_pointer_v = std::is_pointer_v<T> && !std::is_same_v<T, char*> && !std::is_same_v<T, const char*>;
|
||||
|
||||
template <typename T, typename>
|
||||
struct type_caster;
|
||||
|
||||
template <>
|
||||
struct type_caster<bool> {
|
||||
bool value;
|
||||
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<pybind11::bool_>(src)) {
|
||||
value = pkpy::_py_cast<bool>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(bool src, return_value_policy, handle) { return src ? vm->True : vm->False; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_integral_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool convert) {
|
||||
if(isinstance<pybind11::int_>(src)) {
|
||||
value = pkpy::_py_cast<T>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_floating_point_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool convert) {
|
||||
if(isinstance<pybind11::float_>(src)) {
|
||||
value = pkpy::_py_cast<T>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
if(convert && isinstance<pybind11::int_>(src)) {
|
||||
value = pkpy::_py_cast<int64_t>(vm, src.ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_string_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<pybind11::str>(src)) {
|
||||
auto& str = src._as<pkpy::Str>();
|
||||
if constexpr(std::is_same_v<T, std::string>) {
|
||||
value = str;
|
||||
} else if constexpr(std::is_same_v<T, std::string_view>) {
|
||||
value = str;
|
||||
} else if constexpr(std::is_same_v<T, const char*>) {
|
||||
value = str.c_str();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& src, return_value_policy, handle) {
|
||||
return str(std::forward<U>(src));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_pyobject_v<T>>> {
|
||||
T value;
|
||||
|
||||
bool load(const handle& src, bool) {
|
||||
if(isinstance<T>(src)) {
|
||||
value = src;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static handle cast(const handle& src, return_value_policy, handle) { return src; }
|
||||
};
|
||||
|
||||
template <typename T, typename>
|
||||
struct type_caster {
|
||||
struct value_wrapper {
|
||||
T* pointer;
|
||||
|
||||
operator T& () { return *pointer; }
|
||||
};
|
||||
|
||||
value_wrapper value;
|
||||
|
||||
using underlying_type = std::remove_pointer_t<decltype(value.pointer)>;
|
||||
|
||||
bool load(handle src, bool convert) {
|
||||
if(isinstance<underlying_type>(src)) {
|
||||
auto& i = src._as<instance>();
|
||||
value.pointer = &i._as<underlying_type>();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& value, return_value_policy policy, const handle& parent = handle()) {
|
||||
const auto& info = typeid(underlying_type);
|
||||
auto type = type_visitor::type<underlying_type>();
|
||||
return instance::create(std::forward<U>(value), type, policy, parent.ptr());
|
||||
// TODO: support implicit cast
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_pointer_v<T> || std::is_reference_v<T>>> {
|
||||
using underlying =
|
||||
std::remove_cv_t<std::conditional_t<is_pointer_v<T>, std::remove_pointer_t<T>, std::remove_reference_t<T>>>;
|
||||
|
||||
struct wrapper {
|
||||
type_caster<underlying> caster;
|
||||
|
||||
operator T () {
|
||||
if constexpr(std::is_pointer_v<T>) {
|
||||
return caster.value.pointer;
|
||||
} else {
|
||||
return caster.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
wrapper value;
|
||||
|
||||
bool load(const handle& src, bool convert) { return value.caster.load(src, convert); }
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& value, return_value_policy policy, const handle& parent) {
|
||||
return type_caster<underlying>::cast(std::forward<U>(value), policy, parent);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pybind11
|
||||
|
186
include/pybind11/internal/class.h
Normal file
186
include/pybind11/internal/class.h
Normal file
@ -0,0 +1,186 @@
|
||||
#pragma once
|
||||
|
||||
#include "module.h"
|
||||
#include <vector>
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
struct dynamic_attr {};
|
||||
|
||||
template <typename T, typename Base = void>
|
||||
class class_ : public type {
|
||||
protected:
|
||||
handle m_scope;
|
||||
|
||||
public:
|
||||
using type::type;
|
||||
using underlying_type = T;
|
||||
|
||||
template <typename... Args>
|
||||
class_(const handle& scope, const char* name, const Args&... args) :
|
||||
m_scope(scope), type(type_visitor::create<T, Base>(scope, name)) {
|
||||
auto& info = type_info::of<T>();
|
||||
info.name = name;
|
||||
|
||||
// bind __new__
|
||||
interpreter::bind_func(m_ptr, pkpy::__new__, -1, [](pkpy::VM* vm, pkpy::ArgsView args) {
|
||||
auto cls = handle(args[0])._as<pkpy::Type>();
|
||||
|
||||
// check if the class has constructor, if not, raise error
|
||||
if(vm->find_name_in_mro(cls, pkpy::__init__) == nullptr) {
|
||||
vm->RuntimeError("if you want to create instance of bound class, you must bind constructor for it");
|
||||
}
|
||||
|
||||
auto var = instance::create(cls, &type_info::of<T>());
|
||||
|
||||
if constexpr(types_count_v<dynamic_attr, Args...> != 0) {
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
var.get()->_attr = new pkpy::NameDict();
|
||||
#else
|
||||
var->_enable_instance_dict();
|
||||
#endif
|
||||
}
|
||||
|
||||
return var;
|
||||
});
|
||||
}
|
||||
|
||||
/// bind constructor
|
||||
template <typename... Args, typename... Extra>
|
||||
class_& def(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 {
|
||||
impl::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_base_of_v<remove_cvref_t<first>, T>;
|
||||
|
||||
if constexpr(!is_first_base_of_v) {
|
||||
static_assert(is_first_base_of_v,
|
||||
"If you want to bind member function, the first argument must be the base class");
|
||||
} else {
|
||||
impl::bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::DEFAULT, extra...);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// bind operators
|
||||
template <typename Operator, typename... Extras>
|
||||
class_& def(Operator op, const Extras&... extras) {
|
||||
op.execute(*this, extras...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TODO: factory function
|
||||
|
||||
/// bind static function
|
||||
template <typename Fn, typename... Extra>
|
||||
class_& def_static(const char* name, Fn&& f, const Extra&... extra) {
|
||||
impl::bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::STATICMETHOD, extra...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename MP, typename... Extras>
|
||||
class_& def_readwrite(const char* name, MP mp, const Extras&... extras) {
|
||||
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
||||
static_assert(std::is_member_object_pointer_v<MP>, "def_readwrite only supports pointer to data member");
|
||||
} else {
|
||||
impl::bind_property(*this, name, mp, mp, extras...);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename MP, typename... Extras>
|
||||
class_& def_readonly(const char* name, MP mp, const Extras&... extras) {
|
||||
if constexpr(!std::is_member_object_pointer_v<MP>) {
|
||||
static_assert(std::is_member_object_pointer_v<MP>, "def_readonly only supports pointer to data member");
|
||||
} else {
|
||||
impl::bind_property(*this, name, mp, nullptr, extras...);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Getter, typename Setter, typename... Extras>
|
||||
class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
||||
impl::bind_property(*this, name, std::forward<Getter>(g), std::forward<Setter>(s), extras...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Getter, typename... Extras>
|
||||
class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) {
|
||||
impl::bind_property(*this, name, std::forward<Getter>(mp), nullptr, extras...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Var, typename... Extras>
|
||||
class_& def_readwrite_static(const char* name, Var& mp, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Var>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Var, typename... Extras>
|
||||
class_& def_readonly_static(const char* name, Var& mp, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Var>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Getter, typename Setter, typename... Extras>
|
||||
class_& def_property_static(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
|
||||
static_assert(
|
||||
dependent_false<Getter>,
|
||||
"define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented.");
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename... Others>
|
||||
class enum_ : public class_<T, Others...> {
|
||||
std::vector<std::pair<const char*, handle>> m_values;
|
||||
|
||||
public:
|
||||
using Base = class_<T, Others...>;
|
||||
using class_<T, Others...>::class_;
|
||||
|
||||
template <typename... Args>
|
||||
enum_(const handle& scope, const char* name, Args&&... args) :
|
||||
class_<T, Others...>(scope, name, std::forward<Args>(args)...) {
|
||||
Base::def_property_readonly("value", [](T& self) {
|
||||
return int_(static_cast<std::underlying_type_t<T>>(self));
|
||||
});
|
||||
}
|
||||
|
||||
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.emplace_back(name, var);
|
||||
return *this;
|
||||
}
|
||||
|
||||
enum_& export_values() {
|
||||
for(auto& [name, value]: m_values) {
|
||||
Base::m_scope.ptr()->attr().set(name, value.ptr());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
} // namespace pybind11
|
||||
|
551
include/pybind11/internal/cpp_function.h
Normal file
551
include/pybind11/internal/cpp_function.h
Normal file
@ -0,0 +1,551 @@
|
||||
#pragma once
|
||||
#include "cast.h"
|
||||
#include <map>
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
// append the overload to the beginning of the overload list
|
||||
struct prepend {};
|
||||
|
||||
template <typename... Args>
|
||||
struct init {};
|
||||
|
||||
// TODO: support more customized tags
|
||||
//
|
||||
// template <std::size_t Nurse, std::size_t... Patients>
|
||||
// struct keep_alive {};
|
||||
//
|
||||
// template <typename T>
|
||||
// struct call_guard {
|
||||
// static_assert(std::is_default_constructible_v<T>, "call_guard must be default constructible");
|
||||
// };
|
||||
//
|
||||
// struct kw_only {};
|
||||
//
|
||||
// struct pos_only {};
|
||||
|
||||
class cpp_function : public function {
|
||||
PYBIND11_TYPE_IMPLEMENT(function, pkpy::NativeFunc, vm->tp_native_func);
|
||||
|
||||
public:
|
||||
template <typename Fn, typename... Extras>
|
||||
cpp_function(Fn&& f, const Extras&... extras) {}
|
||||
|
||||
template <typename T>
|
||||
decltype(auto) get_userdata_as() {
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
return self()._userdata.as<T>();
|
||||
#else
|
||||
return self()._userdata._cast<T>();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void set_userdata(T&& value) {
|
||||
self()._userdata = std::forward<T>(value);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pybind11
|
||||
|
||||
namespace pybind11::impl {
|
||||
|
||||
template <typename Callable,
|
||||
typename Extra,
|
||||
typename Args = callable_args_t<Callable>,
|
||||
typename IndexSequence = std::make_index_sequence<std::tuple_size_v<Args>>>
|
||||
struct template_parser;
|
||||
|
||||
class function_record {
|
||||
private:
|
||||
template <typename C, typename E, typename A, typename I>
|
||||
friend struct template_parser;
|
||||
|
||||
struct arguments_t {
|
||||
std::vector<pkpy::StrName> names;
|
||||
std::vector<handle> defaults;
|
||||
};
|
||||
|
||||
using destructor_t = void (*)(function_record*);
|
||||
using wrapper_t = handle (*)(function_record&, pkpy::ArgsView, bool convert, handle parent);
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<pkpy::StrName>);
|
||||
|
||||
private:
|
||||
union {
|
||||
void* data;
|
||||
char buffer[16];
|
||||
};
|
||||
|
||||
wrapper_t wrapper = nullptr;
|
||||
function_record* next = nullptr;
|
||||
arguments_t* arguments = nullptr;
|
||||
destructor_t destructor = nullptr;
|
||||
const char* signature = nullptr;
|
||||
return_value_policy policy = return_value_policy::automatic;
|
||||
|
||||
public:
|
||||
template <typename Fn, typename... Extras>
|
||||
function_record(Fn&& f, const Extras&... extras) {
|
||||
using Callable = std::decay_t<Fn>;
|
||||
|
||||
if constexpr(std::is_trivially_copyable_v<Callable> && sizeof(Callable) <= sizeof(buffer)) {
|
||||
// if the callable object is trivially copyable and the size is less than 16 bytes, store it in the
|
||||
// buffer
|
||||
new (buffer) auto(std::forward<Fn>(f));
|
||||
destructor = [](function_record* self) {
|
||||
reinterpret_cast<Callable*>(self->buffer)->~Callable();
|
||||
};
|
||||
} else {
|
||||
// otherwise, store it in the heap
|
||||
data = new auto(std::forward<Fn>(f));
|
||||
destructor = [](function_record* self) {
|
||||
delete static_cast<Callable*>(self->data);
|
||||
};
|
||||
}
|
||||
|
||||
using Parser = template_parser<Callable, std::tuple<Extras...>>;
|
||||
Parser::initialize(*this, extras...);
|
||||
wrapper = Parser::wrapper;
|
||||
}
|
||||
|
||||
function_record(const function_record&) = delete;
|
||||
function_record& operator= (const function_record&) = delete;
|
||||
function_record& operator= (function_record&&) = delete;
|
||||
|
||||
function_record(function_record&& other) noexcept {
|
||||
std::memcpy(this, &other, sizeof(function_record));
|
||||
std::memset(&other, 0, sizeof(function_record));
|
||||
}
|
||||
|
||||
~function_record() {
|
||||
if(destructor) { destructor(this); }
|
||||
if(arguments) { delete arguments; }
|
||||
if(next) { delete next; }
|
||||
if(signature) { delete[] signature; }
|
||||
}
|
||||
|
||||
void append(function_record* record) {
|
||||
function_record* p = this;
|
||||
while(p->next) {
|
||||
p = p->next;
|
||||
}
|
||||
p->next = record;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& _as() {
|
||||
if constexpr(std::is_trivially_copyable_v<T> && sizeof(T) <= sizeof(buffer)) {
|
||||
return *reinterpret_cast<T*>(buffer);
|
||||
} else {
|
||||
return *static_cast<T*>(data);
|
||||
}
|
||||
}
|
||||
|
||||
handle operator() (pkpy::ArgsView view) {
|
||||
function_record* p = this;
|
||||
// foreach function record and call the function with not convert
|
||||
while(p != nullptr) {
|
||||
handle result = p->wrapper(*p, view, false, {});
|
||||
if(result) { return result; }
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
p = this;
|
||||
// foreach function record and call the function with convert
|
||||
while(p != nullptr) {
|
||||
handle result = p->wrapper(*p, view, true, {});
|
||||
if(result) { return result; }
|
||||
p = p->next;
|
||||
}
|
||||
|
||||
std::string msg = "no matching function found, function signature:\n";
|
||||
std::size_t index = 0;
|
||||
p = this;
|
||||
while(p != nullptr) {
|
||||
msg += " ";
|
||||
msg += p->signature;
|
||||
msg += "\n";
|
||||
p = p->next;
|
||||
}
|
||||
vm->TypeError(msg);
|
||||
PK_UNREACHABLE();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Fn, std::size_t... Is, typename... Args>
|
||||
handle invoke(Fn&& fn,
|
||||
std::index_sequence<Is...>,
|
||||
std::tuple<type_caster<Args>...>& casters,
|
||||
return_value_policy policy,
|
||||
handle parent) {
|
||||
using underlying_type = std::decay_t<Fn>;
|
||||
using return_type = callable_return_t<underlying_type>;
|
||||
|
||||
constexpr bool is_void = std::is_void_v<return_type>;
|
||||
constexpr bool is_member_function_pointer = std::is_member_function_pointer_v<underlying_type>;
|
||||
|
||||
if constexpr(is_member_function_pointer) {
|
||||
// helper function to unpack the arguments to call the member pointer
|
||||
auto unpack = [&](class_type_t<underlying_type>& self, auto&... args) {
|
||||
return (self.*fn)(args...);
|
||||
};
|
||||
|
||||
if constexpr(!is_void) {
|
||||
return pybind11::cast(unpack(std::get<Is>(casters).value...), policy, parent);
|
||||
} else {
|
||||
unpack(std::get<Is>(casters).value...);
|
||||
return vm->None;
|
||||
}
|
||||
} else {
|
||||
if constexpr(!is_void) {
|
||||
return pybind11::cast(fn(std::get<Is>(casters).value...), policy, parent);
|
||||
} else {
|
||||
fn(std::get<Is>(casters).value...);
|
||||
return vm->None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct arguments_info_t {
|
||||
int argc = 0;
|
||||
int args_pos = -1;
|
||||
int kwargs_pos = -1;
|
||||
};
|
||||
|
||||
struct extras_info_t {
|
||||
int doc_pos = -1;
|
||||
int named_argc = 0;
|
||||
int policy_pos = -1;
|
||||
};
|
||||
|
||||
template <typename Callable, typename... Extras, typename... Args, std::size_t... Is>
|
||||
struct template_parser<Callable, std::tuple<Extras...>, std::tuple<Args...>, std::index_sequence<Is...>> {
|
||||
constexpr static arguments_info_t parse_arguments() {
|
||||
constexpr auto args_count = types_count_v<args, Args...>;
|
||||
constexpr auto kwargs_count = types_count_v<kwargs, Args...>;
|
||||
|
||||
static_assert(args_count <= 1, "py::args can occur at most once");
|
||||
static_assert(kwargs_count <= 1, "py::kwargs can occur at most once");
|
||||
|
||||
constexpr auto args_pos = type_index_v<args, Args...>;
|
||||
constexpr auto kwargs_pos = type_index_v<kwargs, Args...>;
|
||||
|
||||
if constexpr(kwargs_count == 1) {
|
||||
static_assert(kwargs_pos == sizeof...(Args) - 1, "py::kwargs must be the last argument");
|
||||
|
||||
// FIXME: temporarily, args and kwargs must be at the end of the arguments list
|
||||
if constexpr(args_count == 1) {
|
||||
static_assert(args_pos == kwargs_pos - 1, "py::args must be before py::kwargs");
|
||||
}
|
||||
}
|
||||
|
||||
return {sizeof...(Args), args_pos, kwargs_pos};
|
||||
}
|
||||
|
||||
constexpr static extras_info_t parse_extras() {
|
||||
constexpr auto doc_count = types_count_v<const char*, Extras...>;
|
||||
constexpr auto policy_count = types_count_v<return_value_policy, Extras...>;
|
||||
|
||||
static_assert(doc_count <= 1, "doc can occur at most once");
|
||||
static_assert(policy_count <= 1, "return_value_policy can occur at most once");
|
||||
|
||||
constexpr auto doc_pos = type_index_v<const char*, Extras...>;
|
||||
constexpr auto policy_pos = type_index_v<return_value_policy, Extras...>;
|
||||
|
||||
constexpr auto named_argc = types_count_v<arg, Extras...>;
|
||||
constexpr auto normal_argc =
|
||||
sizeof...(Args) - (arguments_info.args_pos != -1) - (arguments_info.kwargs_pos != -1);
|
||||
static_assert(named_argc == 0 || named_argc == normal_argc,
|
||||
"named arguments must be the same as the number of function arguments");
|
||||
|
||||
return {doc_pos, named_argc, policy_pos};
|
||||
}
|
||||
|
||||
constexpr inline static auto arguments_info = parse_arguments();
|
||||
constexpr inline static auto extras_info = parse_extras();
|
||||
|
||||
static void initialize(function_record& record, const Extras&... extras) {
|
||||
auto extras_tuple = std::make_tuple(extras...);
|
||||
constexpr static bool has_named_args = (extras_info.named_argc > 0);
|
||||
// set return value policy
|
||||
if constexpr(extras_info.policy_pos != -1) { record.policy = std::get<extras_info.policy_pos>(extras_tuple); }
|
||||
|
||||
// TODO: set others
|
||||
|
||||
// set default arguments
|
||||
if constexpr(has_named_args) {
|
||||
record.arguments = new function_record::arguments_t();
|
||||
|
||||
auto add_arguments = [&](const auto& arg) {
|
||||
if constexpr(std::is_same_v<pybind11::arg, remove_cvref_t<decltype(arg)>>) {
|
||||
auto& arguments = *record.arguments;
|
||||
arguments.names.emplace_back(arg.name);
|
||||
arguments.defaults.emplace_back(arg.default_);
|
||||
}
|
||||
};
|
||||
|
||||
(add_arguments(extras), ...);
|
||||
}
|
||||
|
||||
// set signature
|
||||
{
|
||||
std::string sig = "(";
|
||||
std::size_t index = 0;
|
||||
auto append = [&](auto _t) {
|
||||
using T = pybind11_decay_t<typename decltype(_t)::type>;
|
||||
if constexpr(std::is_same_v<T, args>) {
|
||||
sig += "*args";
|
||||
} else if constexpr(std::is_same_v<T, kwargs>) {
|
||||
sig += "**kwargs";
|
||||
} else if constexpr(has_named_args) {
|
||||
sig += record.arguments->names[index].c_str();
|
||||
sig += ": ";
|
||||
sig += type_info::of<T>().name;
|
||||
if(record.arguments->defaults[index]) {
|
||||
sig += " = ";
|
||||
sig += record.arguments->defaults[index].repr();
|
||||
}
|
||||
} else {
|
||||
sig += "_: ";
|
||||
sig += type_info::of<T>().name;
|
||||
}
|
||||
|
||||
if(index + 1 < arguments_info.argc) { sig += ", "; }
|
||||
index++;
|
||||
};
|
||||
(append(type_identity<Args>{}), ...);
|
||||
sig += ")";
|
||||
char* buffer = new char[sig.size() + 1];
|
||||
std::memcpy(buffer, sig.data(), sig.size());
|
||||
buffer[sig.size()] = '\0';
|
||||
record.signature = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
static handle wrapper(function_record& record, pkpy::ArgsView view, bool convert, handle parent) {
|
||||
constexpr auto argc = arguments_info.argc;
|
||||
constexpr auto named_argc = extras_info.named_argc;
|
||||
constexpr auto args_pos = arguments_info.args_pos;
|
||||
constexpr auto kwargs_pos = arguments_info.kwargs_pos;
|
||||
constexpr auto normal_argc = argc - (args_pos != -1) - (kwargs_pos != -1);
|
||||
|
||||
// avoid gc call in bound function
|
||||
vm->heap.gc_scope_lock();
|
||||
|
||||
// add 1 to avoid zero-size array when argc is 0
|
||||
handle stack[argc + 1] = {};
|
||||
|
||||
// ensure the number of passed arguments is no greater than the number of parameters
|
||||
if(args_pos == -1 && view.size() > normal_argc) { return handle(); }
|
||||
|
||||
// if have default arguments, load them
|
||||
if constexpr(named_argc > 0) {
|
||||
auto& defaults = record.arguments->defaults;
|
||||
std::memcpy(stack, defaults.data(), defaults.size() * sizeof(handle));
|
||||
}
|
||||
|
||||
// load arguments from call arguments
|
||||
const auto size = std::min(view.size(), normal_argc);
|
||||
std::memcpy(stack, view.begin(), size * sizeof(handle));
|
||||
|
||||
// pack the args
|
||||
if constexpr(args_pos != -1) {
|
||||
const auto n = std::max(view.size() - normal_argc, 0);
|
||||
tuple args = tuple(n);
|
||||
for(std::size_t i = 0; i < n; ++i) {
|
||||
args[i] = view[normal_argc + i];
|
||||
}
|
||||
stack[args_pos] = args;
|
||||
}
|
||||
|
||||
// resolve keyword arguments
|
||||
const auto n = vm->s_data._sp - view.end();
|
||||
std::size_t index = 0;
|
||||
|
||||
if constexpr(named_argc > 0) {
|
||||
std::size_t arg_index = 0;
|
||||
auto& arguments = *record.arguments;
|
||||
|
||||
while(arg_index < named_argc && index < n) {
|
||||
const auto key = pkpy::_py_cast<pkpy::i64>(vm, view.end()[index]);
|
||||
const auto value = view.end()[index + 1];
|
||||
const auto name = pkpy::StrName(key);
|
||||
auto& arg_name = record.arguments->names[arg_index];
|
||||
|
||||
if(name == arg_name) {
|
||||
stack[arg_index] = value;
|
||||
index += 2;
|
||||
}
|
||||
|
||||
arg_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// pack the kwargs
|
||||
if constexpr(kwargs_pos != -1) {
|
||||
dict kwargs;
|
||||
while(index < n) {
|
||||
const auto key = pkpy::_py_cast<pkpy::i64>(vm, view.end()[index]);
|
||||
const str name = str(pkpy::StrName(key).sv());
|
||||
kwargs[name] = view.end()[index + 1];
|
||||
index += 2;
|
||||
}
|
||||
stack[kwargs_pos] = kwargs;
|
||||
}
|
||||
|
||||
// if have rest keyword arguments, call fails
|
||||
if(index != n) { return handle(); }
|
||||
|
||||
// check if all the arguments are valid
|
||||
for(std::size_t i = 0; i < argc; ++i) {
|
||||
if(!stack[i]) { return handle(); }
|
||||
}
|
||||
|
||||
// ok, all the arguments are valid, call the function
|
||||
std::tuple<type_caster<Args>...> casters;
|
||||
|
||||
// check type compatibility
|
||||
if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
|
||||
return invoke(record._as<Callable>(), std::index_sequence<Is...>{}, casters, record.policy, parent);
|
||||
}
|
||||
|
||||
return handle();
|
||||
}
|
||||
};
|
||||
|
||||
inline auto _wrapper(pkpy::VM* vm, pkpy::ArgsView view) {
|
||||
auto&& record = unpack<function_record>(view);
|
||||
return record(view).ptr();
|
||||
}
|
||||
|
||||
template <typename Fn, typename... Extras>
|
||||
handle bind_function(const handle& obj, const char* name, Fn&& fn, pkpy::BindType type, const Extras&... extras) {
|
||||
// do not use cpp_function directly to avoid unnecessary reference count change
|
||||
pkpy::PyVar var = obj.ptr();
|
||||
cpp_function callable = var->attr().try_get(name);
|
||||
|
||||
// if the function is not bound yet, bind it
|
||||
if(!callable) {
|
||||
auto record = function_record(std::forward<Fn>(fn), extras...);
|
||||
void* data = interpreter::take_ownership(std::move(record));
|
||||
callable = interpreter::bind_func(var, name, -1, _wrapper, data);
|
||||
} else {
|
||||
function_record* record = new function_record(std::forward<Fn>(fn), extras...);
|
||||
function_record* last = callable.get_userdata_as<function_record*>();
|
||||
|
||||
if constexpr((types_count_v<prepend, Extras...> != 0)) {
|
||||
// if prepend is specified, append the new record to the beginning of the list
|
||||
fn.set_userdata(record);
|
||||
record->append(last);
|
||||
} else {
|
||||
// otherwise, append the new record to the end of the list
|
||||
last->append(record);
|
||||
}
|
||||
}
|
||||
|
||||
return callable;
|
||||
}
|
||||
|
||||
} // namespace pybind11::impl
|
||||
|
||||
namespace pybind11::impl {
|
||||
|
||||
template <typename Getter>
|
||||
pkpy::PyVar getter_wrapper(pkpy::VM* vm, pkpy::ArgsView view) {
|
||||
handle result = vm->None;
|
||||
auto&& getter = unpack<Getter>(view);
|
||||
constexpr auto policy = return_value_policy::reference_internal;
|
||||
|
||||
if constexpr(std::is_member_pointer_v<Getter>) {
|
||||
using Self = class_type_t<Getter>;
|
||||
auto& self = handle(view[0])._as<instance>()._as<Self>();
|
||||
|
||||
if constexpr(std::is_member_object_pointer_v<Getter>) {
|
||||
// specialize for pointer to data member
|
||||
result = cast(self.*getter, policy, view[0]);
|
||||
} else {
|
||||
// specialize for pointer to member function
|
||||
result = cast((self.*getter)(), policy, view[0]);
|
||||
}
|
||||
} else {
|
||||
// specialize for function pointer and lambda
|
||||
using Self = remove_cvref_t<std::tuple_element_t<0, callable_args_t<Getter>>>;
|
||||
auto& self = handle(view[0])._as<instance>()._as<Self>();
|
||||
|
||||
result = cast(getter(self), policy, view[0]);
|
||||
}
|
||||
|
||||
return result.ptr();
|
||||
}
|
||||
|
||||
template <typename Setter>
|
||||
pkpy::PyVar setter_wrapper(pkpy::VM* vm, pkpy::ArgsView view) {
|
||||
auto&& setter = unpack<Setter>(view);
|
||||
|
||||
if constexpr(std::is_member_pointer_v<Setter>) {
|
||||
using Self = class_type_t<Setter>;
|
||||
auto& self = handle(view[0])._as<instance>()._as<Self>();
|
||||
|
||||
if constexpr(std::is_member_object_pointer_v<Setter>) {
|
||||
// specialize for pointer to data member
|
||||
type_caster<member_type_t<Setter>> caster;
|
||||
if(caster.load(view[1], true)) {
|
||||
self.*setter = caster.value;
|
||||
return vm->None;
|
||||
}
|
||||
} else {
|
||||
// specialize for pointer to member function
|
||||
type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
|
||||
if(caster.load(view[1], true)) {
|
||||
(self.*setter)(caster.value);
|
||||
return vm->None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// specialize for function pointer and lambda
|
||||
using Self = remove_cvref_t<std::tuple_element_t<0, callable_args_t<Setter>>>;
|
||||
auto& self = handle(view[0])._as<instance>()._as<Self>();
|
||||
|
||||
type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
|
||||
if(caster.load(view[1], true)) {
|
||||
setter(self, caster.value);
|
||||
return vm->None;
|
||||
}
|
||||
}
|
||||
|
||||
vm->TypeError("Unexpected argument type");
|
||||
PK_UNREACHABLE();
|
||||
}
|
||||
|
||||
template <typename Getter, typename Setter, typename... Extras>
|
||||
handle bind_property(const handle& obj, const char* name, Getter&& getter_, Setter&& setter_, const Extras&... extras) {
|
||||
handle getter = none();
|
||||
handle setter = none();
|
||||
using Wrapper = pkpy::PyVar (*)(pkpy::VM*, pkpy::ArgsView);
|
||||
|
||||
constexpr auto create = [](Wrapper wrapper, int argc, auto&& f) {
|
||||
if constexpr(need_host<remove_cvref_t<decltype(f)>>) {
|
||||
// otherwise, store it in the type_info
|
||||
void* data = interpreter::take_ownership(std::forward<decltype(f)>(f));
|
||||
// store the index in the object
|
||||
return vm->heap.gcnew<pkpy::NativeFunc>(vm->tp_native_func, wrapper, argc, data);
|
||||
} else {
|
||||
// if the function is trivially copyable and the size is less than 16 bytes, store it in the object
|
||||
// directly
|
||||
return vm->heap.gcnew<pkpy::NativeFunc>(vm->tp_native_func, wrapper, argc, f);
|
||||
}
|
||||
};
|
||||
|
||||
getter = create(impl::getter_wrapper<std::decay_t<Getter>>, 1, std::forward<Getter>(getter_));
|
||||
|
||||
if constexpr(!std::is_same_v<Setter, std::nullptr_t>) {
|
||||
setter = create(impl::setter_wrapper<std::decay_t<Setter>>, 2, std::forward<Setter>(setter_));
|
||||
}
|
||||
|
||||
handle property = pybind11::property(getter, setter);
|
||||
setattr(obj, name, property);
|
||||
return property;
|
||||
}
|
||||
|
||||
} // namespace pybind11::impl
|
142
include/pybind11/internal/instance.h
Normal file
142
include/pybind11/internal/instance.h
Normal file
@ -0,0 +1,142 @@
|
||||
#pragma once
|
||||
#include "kernel.h"
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
struct type_info {
|
||||
std::string_view name;
|
||||
std::size_t size;
|
||||
std::size_t alignment;
|
||||
void (*destructor)(void*);
|
||||
const std::type_info* type;
|
||||
|
||||
template <typename T>
|
||||
static type_info& of() {
|
||||
static_assert(!std::is_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
|
||||
"T must not be a reference type or const type.");
|
||||
static type_info info = {
|
||||
type_name<T>(),
|
||||
sizeof(T),
|
||||
alignof(T),
|
||||
[](void* ptr) {
|
||||
((T*)ptr)->~T();
|
||||
operator delete (ptr);
|
||||
},
|
||||
&typeid(T),
|
||||
};
|
||||
return info;
|
||||
}
|
||||
};
|
||||
|
||||
// all registered C++ class will be ensured as instance type.
|
||||
class instance {
|
||||
public:
|
||||
// use to record the type information of C++ class.
|
||||
|
||||
private:
|
||||
enum Flag {
|
||||
None = 0,
|
||||
Own = 1 << 0, // if the instance is owned by C++ side.
|
||||
Ref = 1 << 1, // need to mark the parent object.
|
||||
};
|
||||
|
||||
Flag flag;
|
||||
void* data;
|
||||
const type_info* type;
|
||||
pkpy::PyVar parent;
|
||||
|
||||
public:
|
||||
instance() noexcept : flag(Flag::None), data(nullptr), type(nullptr), parent(nullptr) {}
|
||||
|
||||
instance(const instance&) = delete;
|
||||
|
||||
instance(instance&& other) noexcept : flag(other.flag), data(other.data), type(other.type), parent(other.parent) {
|
||||
other.flag = Flag::None;
|
||||
other.data = nullptr;
|
||||
other.type = nullptr;
|
||||
other.parent = nullptr;
|
||||
}
|
||||
|
||||
static pkpy::PyVar create(pkpy::Type type, const type_info* info) noexcept {
|
||||
instance instance;
|
||||
instance.type = info;
|
||||
instance.data = operator new (info->size);
|
||||
instance.flag = Flag::Own;
|
||||
return vm->heap.gcnew<pybind11::instance>(type, std::move(instance));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static pkpy::PyVar create(T&& value,
|
||||
pkpy::Type type,
|
||||
return_value_policy policy = return_value_policy::automatic_reference,
|
||||
pkpy::PyVar parent = nullptr) noexcept {
|
||||
using underlying_type = remove_cvref_t<T>;
|
||||
|
||||
auto& _value = [&]() -> auto& {
|
||||
// note that, pybind11 will ignore the const qualifier.
|
||||
// in fact, try to modify a const value will result in undefined behavior.
|
||||
if constexpr(std::is_pointer_v<underlying_type>) {
|
||||
return *reinterpret_cast<underlying_type*>(value);
|
||||
} else {
|
||||
return const_cast<underlying_type&>(value);
|
||||
}
|
||||
}();
|
||||
|
||||
using primary = std::remove_pointer_t<underlying_type>;
|
||||
instance instance;
|
||||
instance.type = &type_info::of<primary>();
|
||||
|
||||
if(policy == return_value_policy::take_ownership) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::Own;
|
||||
} else if(policy == return_value_policy::copy) {
|
||||
if constexpr(std::is_copy_constructible_v<primary>) {
|
||||
instance.data = ::new auto(_value);
|
||||
instance.flag = Flag::Own;
|
||||
} else {
|
||||
std::string msg = "cannot use copy policy on non-copyable type: ";
|
||||
msg += type_name<primary>();
|
||||
vm->RuntimeError(msg);
|
||||
}
|
||||
} else if(policy == return_value_policy::move) {
|
||||
if constexpr(std::is_move_constructible_v<primary>) {
|
||||
instance.data = ::new auto(std::move(_value));
|
||||
instance.flag = Flag::Own;
|
||||
} else {
|
||||
std::string msg = "cannot use move policy on non-moveable type: ";
|
||||
msg += type_name<primary>();
|
||||
vm->RuntimeError(msg);
|
||||
}
|
||||
} else if(policy == return_value_policy::reference) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::None;
|
||||
} else if(policy == return_value_policy::reference_internal) {
|
||||
instance.data = &_value;
|
||||
instance.flag = Flag::Ref;
|
||||
instance.parent = parent;
|
||||
}
|
||||
|
||||
return vm->heap.gcnew<pybind11::instance>(type, std::move(instance));
|
||||
}
|
||||
|
||||
~instance() {
|
||||
if(flag & Flag::Own) { type->destructor(data); }
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& _as() noexcept {
|
||||
return *static_cast<T*>(data);
|
||||
}
|
||||
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
void _gc_mark(pkpy::VM* vm) const noexcept {
|
||||
if(parent && (flag & Flag::Ref)) { PK_OBJ_MARK(parent); }
|
||||
}
|
||||
#else
|
||||
void _gc_mark() const noexcept {
|
||||
if(parent && (flag & Flag::Ref)) { PK_OBJ_MARK(parent); }
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace pybind11
|
224
include/pybind11/internal/kernel.h
Normal file
224
include/pybind11/internal/kernel.h
Normal file
@ -0,0 +1,224 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <pocketpy.h>
|
||||
|
||||
#include "type_traits.h"
|
||||
|
||||
namespace pybind11::impl {
|
||||
struct capsule {
|
||||
void* ptr;
|
||||
void (*destructor)(void*);
|
||||
|
||||
template <typename T>
|
||||
capsule(T&& value) :
|
||||
ptr(new auto(std::forward<T>(value))), destructor([](void* ptr) {
|
||||
delete static_cast<std::decay_t<T>*>(ptr);
|
||||
}) {}
|
||||
|
||||
capsule(void* ptr, void (*destructor)(void*)) : ptr(ptr), destructor(destructor) {}
|
||||
|
||||
capsule(const capsule&) = delete;
|
||||
|
||||
capsule(capsule&& other) noexcept : ptr(other.ptr), destructor(other.destructor) {
|
||||
other.ptr = nullptr;
|
||||
other.destructor = nullptr;
|
||||
}
|
||||
|
||||
~capsule() {
|
||||
if(ptr != nullptr && destructor != nullptr) destructor(ptr);
|
||||
}
|
||||
};
|
||||
} // namespace pybind11::impl
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
class handle;
|
||||
class object;
|
||||
class iterator;
|
||||
class str;
|
||||
class arg;
|
||||
struct args_proxy;
|
||||
struct kwargs_proxy;
|
||||
|
||||
inline pkpy::VM* vm = nullptr;
|
||||
|
||||
class interpreter {
|
||||
inline static std::vector<impl::capsule>* _capsules = nullptr;
|
||||
inline static std::vector<void (*)()>* _init = nullptr;
|
||||
|
||||
public:
|
||||
inline static void initialize(bool enable_os = true) {
|
||||
if(vm == nullptr) {
|
||||
vm = new pkpy::VM();
|
||||
if(_init != nullptr) {
|
||||
for(auto& fn: *_init)
|
||||
fn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline static void finalize() {
|
||||
if(_capsules != nullptr) {
|
||||
delete _capsules;
|
||||
_capsules = nullptr;
|
||||
}
|
||||
|
||||
if(vm != nullptr) {
|
||||
delete vm;
|
||||
vm = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static void* take_ownership(T&& value) {
|
||||
if(_capsules == nullptr) _capsules = new std::vector<impl::capsule>();
|
||||
_capsules->emplace_back(std::forward<T>(value));
|
||||
return _capsules->back().ptr;
|
||||
}
|
||||
|
||||
inline static void register_init(void (*init)()) {
|
||||
if(_init == nullptr) _init = new std::vector<void (*)()>();
|
||||
_init->push_back(init);
|
||||
}
|
||||
|
||||
inline static pkpy::PyVar bind_func(pkpy::PyVar scope,
|
||||
pkpy::StrName name,
|
||||
int argc,
|
||||
pkpy::NativeFuncC fn,
|
||||
pkpy::any any = {},
|
||||
pkpy::BindType type = pkpy::BindType::DEFAULT) {
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
return vm->bind_func(scope.get(), name, argc, fn, any, type);
|
||||
#else
|
||||
return vm->bind_func(scope, name, argc, fn, std::move(any), type);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline static handle vectorcall(const handle& self, const handle& func, const Args&... args);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool need_host = !(std::is_trivially_copyable_v<T> && (sizeof(T) <= 8));
|
||||
|
||||
template <typename T>
|
||||
decltype(auto) unpack(pkpy::ArgsView view) {
|
||||
if constexpr(need_host<T>) {
|
||||
void* data = pkpy::lambda_get_userdata<void*>(view.begin());
|
||||
return *static_cast<T*>(data);
|
||||
} else {
|
||||
return pkpy::lambda_get_userdata<T>(view.begin());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename policy>
|
||||
class accessor;
|
||||
|
||||
namespace policy {
|
||||
struct attr;
|
||||
struct item;
|
||||
struct tuple;
|
||||
struct list;
|
||||
struct dict;
|
||||
} // namespace policy
|
||||
|
||||
using attr_accessor = accessor<policy::attr>;
|
||||
using item_accessor = accessor<policy::item>;
|
||||
using tuple_accessor = accessor<policy::tuple>;
|
||||
using list_accessor = accessor<policy::list>;
|
||||
using dict_accessor = accessor<policy::dict>;
|
||||
|
||||
template <typename T>
|
||||
T cast(const handle& obj, bool convert = false);
|
||||
|
||||
enum class return_value_policy : uint8_t {
|
||||
/**
|
||||
* This is the default return value policy, which falls back to the policy
|
||||
* return_value_policy::take_ownership when the return value is a pointer.
|
||||
* Otherwise, it uses return_value::move or return_value::copy for rvalue
|
||||
* and lvalue references, respectively. See below for a description of what
|
||||
* all of these different policies do.
|
||||
*/
|
||||
automatic = 0,
|
||||
|
||||
/**
|
||||
* As above, but use policy return_value_policy::reference when the return
|
||||
* value is a pointer. This is the default conversion policy for function
|
||||
* arguments when calling Python functions manually from C++ code (i.e. via
|
||||
* handle::operator()). You probably won't need to use this.
|
||||
*/
|
||||
automatic_reference,
|
||||
|
||||
/**
|
||||
* Reference an existing object (i.e. do not create a new copy) and take
|
||||
* ownership. Python will call the destructor and delete operator when the
|
||||
* object's reference count reaches zero. Undefined behavior ensues when
|
||||
* the C++ side does the same..
|
||||
*/
|
||||
take_ownership,
|
||||
|
||||
/**
|
||||
* Create a new copy of the returned object, which will be owned by
|
||||
* Python. This policy is comparably safe because the lifetimes of the two
|
||||
* instances are decoupled.
|
||||
*/
|
||||
copy,
|
||||
|
||||
/**
|
||||
* Use std::move to move the return value contents into a new instance
|
||||
* that will be owned by Python. This policy is comparably safe because the
|
||||
* lifetimes of the two instances (move source and destination) are
|
||||
* decoupled.
|
||||
*/
|
||||
move,
|
||||
|
||||
/**
|
||||
* Reference an existing object, but do not take ownership. The C++ side
|
||||
* is responsible for managing the object's lifetime and deallocating it
|
||||
* when it is no longer used. Warning: undefined behavior will ensue when
|
||||
* the C++ side deletes an object that is still referenced and used by
|
||||
* Python.
|
||||
*/
|
||||
reference,
|
||||
|
||||
/**
|
||||
* This policy only applies to methods and properties. It references the
|
||||
* object without taking ownership similar to the above
|
||||
* return_value_policy::reference policy. In contrast to that policy, the
|
||||
* function or property's implicit this argument (called the parent) is
|
||||
* considered to be the the owner of the return value (the child).
|
||||
* pybind11 then couples the lifetime of the parent to the child via a
|
||||
* reference relationship that ensures that the parent cannot be garbage
|
||||
* collected while Python is still using the child. More advanced
|
||||
* variations of this scheme are also possible using combinations of
|
||||
* return_value_policy::reference and the keep_alive call policy
|
||||
*/
|
||||
reference_internal
|
||||
};
|
||||
|
||||
struct empty {};
|
||||
|
||||
template <typename... Args>
|
||||
void print(Args&&... args);
|
||||
|
||||
class object;
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_pyobject_v = std::is_base_of_v<object, T>;
|
||||
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
using error_already_set = pkpy::TopLevelException;
|
||||
#else
|
||||
class error_already_set : std::exception {
|
||||
public:
|
||||
error_already_set() = default;
|
||||
|
||||
const char* what() const noexcept override { return "An error occurred while calling a Python function."; }
|
||||
};
|
||||
#endif
|
||||
|
||||
inline void setattr(const handle& obj, const handle& name, const handle& value);
|
||||
inline void setattr(const handle& obj, const char* name, const handle& value);
|
||||
|
||||
} // namespace pybind11
|
48
include/pybind11/internal/module.h
Normal file
48
include/pybind11/internal/module.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include "cpp_function.h"
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
class module_ : public object {
|
||||
|
||||
public:
|
||||
using object::object;
|
||||
|
||||
static module_ __main__() { return vm->_main; }
|
||||
|
||||
static module_ import(const char* name) {
|
||||
if(name == std::string_view{"__main__"}) {
|
||||
return vm->_main;
|
||||
} else {
|
||||
return vm->py_import(name, false);
|
||||
}
|
||||
}
|
||||
|
||||
module_ def_submodule(const char* name, const char* doc = nullptr) {
|
||||
auto package = this->package()._as<pkpy::Str>() + "." + this->name()._as<pkpy::Str>();
|
||||
auto m = vm->new_module(name, package);
|
||||
setattr(*this, name, m);
|
||||
return m;
|
||||
}
|
||||
|
||||
template <typename Fn, typename... Extras>
|
||||
module_& def(const char* name, Fn&& fn, const Extras... extras) {
|
||||
impl::bind_function(*this, name, std::forward<Fn>(fn), pkpy::BindType::DEFAULT, extras...);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
#define PYBIND11_EMBEDDED_MODULE(name, variable) \
|
||||
static void _pybind11_register_##name(pybind11::module_& variable); \
|
||||
namespace pybind11::impl { \
|
||||
auto _module_##name = [] { \
|
||||
interpreter::register_init([] { \
|
||||
pybind11::module_ m = vm->new_module(#name, ""); \
|
||||
_pybind11_register_##name(m); \
|
||||
}); \
|
||||
return 1; \
|
||||
}(); \
|
||||
} \
|
||||
static void _pybind11_register_##name(pybind11::module_& variable)
|
||||
|
||||
} // namespace pybind11
|
268
include/pybind11/internal/object.h
Normal file
268
include/pybind11/internal/object.h
Normal file
@ -0,0 +1,268 @@
|
||||
#pragma once
|
||||
#include "instance.h"
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
template <typename Derived>
|
||||
class interface {
|
||||
private:
|
||||
pkpy::PyVar ptr() const { return static_cast<const Derived*>(this)->ptr(); }
|
||||
|
||||
public:
|
||||
bool is_none() const {
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
return ptr().operator== (vm->None.get());
|
||||
#else
|
||||
return ptr() == vm->None;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is(const interface& other) const {
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
return ptr().operator== (other.ptr().get());
|
||||
#else
|
||||
return ptr() == other.ptr();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool in(const interface& other) const {
|
||||
return pybind11::cast<bool>(vm->call(vm->py_op("contains"), other.ptr(), ptr()));
|
||||
}
|
||||
|
||||
bool contains(const interface& other) const {
|
||||
return pybind11::cast<bool>(vm->call(vm->py_op("contains"), ptr(), other.ptr()));
|
||||
}
|
||||
|
||||
protected:
|
||||
attr_accessor attr(pkpy::StrName key) const;
|
||||
|
||||
public:
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
attr_accessor attr(const char* key) const;
|
||||
attr_accessor attr(const handle& key) const;
|
||||
attr_accessor doc() const;
|
||||
|
||||
item_accessor operator[] (int index) const;
|
||||
item_accessor operator[] (const char* key) const;
|
||||
item_accessor operator[] (const handle& key) const;
|
||||
|
||||
args_proxy operator* () const;
|
||||
object operator- () const;
|
||||
object operator~() const;
|
||||
|
||||
template <return_value_policy policy = return_value_policy::automatic, typename... Args>
|
||||
object operator() (Args&&... args) const;
|
||||
|
||||
str package() const;
|
||||
str name() const;
|
||||
str repr() const;
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
T cast() const {
|
||||
return pybind11::cast<T>(ptr());
|
||||
}
|
||||
|
||||
// this is a internal function, use to interact with pocketpy python
|
||||
template <typename T>
|
||||
decltype(auto) _as() const {
|
||||
static_assert(!std::is_reference_v<T>, "T must not be a reference type.");
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
if constexpr(pkpy::is_sso_v<T>) {
|
||||
return pkpy::_py_cast<T>(vm, ptr());
|
||||
} else {
|
||||
return ptr().template obj_get<T>();
|
||||
}
|
||||
#else
|
||||
return (((pkpy::Py_<T>*)ptr())->_value);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/// a lightweight wrapper for python objects
|
||||
class handle : public interface<handle> {
|
||||
protected:
|
||||
mutable pkpy::PyVar m_ptr = nullptr;
|
||||
|
||||
public:
|
||||
handle() = default;
|
||||
handle(const handle& h) = default;
|
||||
handle& operator= (const handle& other) = default;
|
||||
|
||||
handle(std::nullptr_t) = delete;
|
||||
|
||||
handle(pkpy::PyVar ptr) : m_ptr(ptr) {}
|
||||
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
handle(pkpy::PyObject* ptr) : m_ptr(ptr) {}
|
||||
#endif
|
||||
|
||||
pkpy::PyVar ptr() const { return m_ptr; }
|
||||
|
||||
explicit operator bool () const { return m_ptr != nullptr; }
|
||||
};
|
||||
|
||||
// a helper class to visit type
|
||||
struct type_visitor {
|
||||
|
||||
template <typename T>
|
||||
constexpr static bool is_type = std::is_same_v<pkpy::Type, std::decay_t<decltype(T::type_or_check())>>;
|
||||
|
||||
template <typename T>
|
||||
static pkpy::Type type() {
|
||||
if constexpr(is_pyobject_v<T>) {
|
||||
if constexpr(is_type<T>) {
|
||||
// for some type, they have according type in python, e.g. bool, int, float
|
||||
// so just return the according type
|
||||
return T::type_or_check();
|
||||
} else {
|
||||
// for other type, they don't have according type in python, like iterable, iterator
|
||||
static_assert(dependent_false<T>, "type_or_check not defined");
|
||||
}
|
||||
} else {
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
// for C++ type, lookup the type in the type map
|
||||
auto type = vm->_cxx_typeid_map.try_get(typeid(T));
|
||||
// if found, return the type
|
||||
if(type) return *type;
|
||||
#else
|
||||
auto result = vm->_cxx_typeid_map.find(typeid(T));
|
||||
if(result != vm->_cxx_typeid_map.end()) { return result->second; }
|
||||
#endif
|
||||
// if not found, raise error
|
||||
std::string msg = "can not c++ instance cast to object, type: {";
|
||||
msg += type_name<T>();
|
||||
msg += "} is not registered.";
|
||||
vm->TypeError(msg);
|
||||
PK_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Base = void>
|
||||
static handle create(const handle& scope, const char* name, bool is_builtin = false) {
|
||||
pkpy::Type type = vm->tp_object;
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
pkpy::PyTypeInfo::Vt vt = pkpy::PyTypeInfo::Vt::get<instance>();
|
||||
|
||||
if(is_builtin) { vt = pkpy::PyTypeInfo::Vt::get<T>(); }
|
||||
|
||||
if constexpr(!std::is_same_v<Base, void>) {
|
||||
type = type_visitor::type<Base>();
|
||||
vt = {};
|
||||
}
|
||||
|
||||
handle result = vm->new_type_object(scope.ptr().get(), name, type, true, vt);
|
||||
if(!is_builtin) { vm->_cxx_typeid_map.insert(typeid(T), result._as<pkpy::Type>()); }
|
||||
#else
|
||||
if constexpr(!std::is_same_v<Base, void>) { type = type_visitor::type<Base>(); }
|
||||
handle result = vm->new_type_object(scope.ptr(), name, type, true);
|
||||
if(!is_builtin) (vm->_cxx_typeid_map.try_emplace(typeid(T), result._as<pkpy::Type>()));
|
||||
#endif
|
||||
// set __module__
|
||||
setattr(scope, name, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool check(const handle& obj) {
|
||||
if constexpr(is_pyobject_v<T>) {
|
||||
if constexpr(is_type<T>) {
|
||||
return vm->isinstance(obj.ptr(), T::type_or_check());
|
||||
} else {
|
||||
// some type, like iterable, iterator, they don't have according type in python
|
||||
// but they have a function to check the type, then just call the function
|
||||
return T::type_or_check(obj);
|
||||
}
|
||||
} else {
|
||||
return vm->isinstance(obj.ptr(), type<T>());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// undef in pybind11.h
|
||||
#define PYBIND11_TYPE_IMPLEMENT(parent, name, tp) \
|
||||
\
|
||||
private: \
|
||||
using underlying_type = name; \
|
||||
\
|
||||
inline static auto type_or_check = [] { \
|
||||
return tp; \
|
||||
}; \
|
||||
\
|
||||
decltype(auto) self() const { return _as<underlying_type>(); } \
|
||||
\
|
||||
template <typename... Args> \
|
||||
static handle create(Args&&... args) { \
|
||||
if constexpr(pkpy::is_sso_v<underlying_type>) { \
|
||||
return pkpy::py_var(vm, std::forward<Args>(args)...); \
|
||||
} else { \
|
||||
return vm->heap.gcnew<underlying_type>(type_or_check(), std::forward<Args>(args)...); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
friend type_visitor; \
|
||||
using parent::parent;
|
||||
|
||||
/*=============================================================================//
|
||||
// pkpy does not use reference counts, so object is just fot API compatibility //
|
||||
//=============================================================================*/
|
||||
class object : public handle {
|
||||
PYBIND11_TYPE_IMPLEMENT(handle, empty, vm->tp_object);
|
||||
|
||||
public:
|
||||
object(const handle& h) : handle(h) {
|
||||
// object is must not null ptr
|
||||
assert(h.ptr() != nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
// undef after usage
|
||||
#define PYBIND11_BINARY_OPERATOR(OP, NAME) \
|
||||
inline object operator OP (const handle& lhs, const handle& rhs) { return handle(vm->py_op(NAME))(lhs, rhs); }
|
||||
|
||||
#define PYBIND11_INPLACE_OPERATOR(OP, NAME) \
|
||||
inline object operator OP (handle& lhs, const handle& rhs) { \
|
||||
handle result = handle(vm->py_op(NAME))(lhs, rhs); \
|
||||
return lhs = result; \
|
||||
}
|
||||
|
||||
#define PYBIND11_BINARY_LOGIC_OPERATOR(OP, NAME) \
|
||||
inline bool operator OP (const handle& lhs, const handle& rhs) { \
|
||||
return pybind11::cast<bool>(handle(vm->py_op(NAME))(lhs, rhs)); \
|
||||
}
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(+, "add");
|
||||
PYBIND11_BINARY_OPERATOR(-, "sub");
|
||||
PYBIND11_BINARY_OPERATOR(*, "mul");
|
||||
PYBIND11_BINARY_OPERATOR(/, "truediv");
|
||||
PYBIND11_BINARY_OPERATOR(%, "mod");
|
||||
PYBIND11_BINARY_OPERATOR(|, "or_");
|
||||
PYBIND11_BINARY_OPERATOR(&, "and_");
|
||||
PYBIND11_BINARY_OPERATOR(^, "xor");
|
||||
PYBIND11_BINARY_OPERATOR(<<, "lshift");
|
||||
PYBIND11_BINARY_OPERATOR(>>, "rshift");
|
||||
|
||||
PYBIND11_INPLACE_OPERATOR(+=, "iadd");
|
||||
PYBIND11_INPLACE_OPERATOR(-=, "isub");
|
||||
PYBIND11_INPLACE_OPERATOR(*=, "imul");
|
||||
PYBIND11_INPLACE_OPERATOR(/=, "itruediv");
|
||||
PYBIND11_INPLACE_OPERATOR(%=, "imod");
|
||||
PYBIND11_INPLACE_OPERATOR(|=, "ior");
|
||||
PYBIND11_INPLACE_OPERATOR(&=, "iand");
|
||||
PYBIND11_INPLACE_OPERATOR(^=, "ixor");
|
||||
PYBIND11_INPLACE_OPERATOR(<<=, "ilshift");
|
||||
PYBIND11_INPLACE_OPERATOR(>>=, "irshift");
|
||||
|
||||
PYBIND11_BINARY_LOGIC_OPERATOR(==, "eq");
|
||||
PYBIND11_BINARY_LOGIC_OPERATOR(!=, "ne");
|
||||
PYBIND11_BINARY_LOGIC_OPERATOR(<, "lt");
|
||||
PYBIND11_BINARY_LOGIC_OPERATOR(>, "gt");
|
||||
PYBIND11_BINARY_LOGIC_OPERATOR(<=, "le");
|
||||
PYBIND11_BINARY_LOGIC_OPERATOR(>=, "ge");
|
||||
|
||||
#undef PYBIND11_BINARY_OPERATOR
|
||||
#undef PYBIND11_INPLACE_OPERATOR
|
||||
#undef PYBIND11_BINARY_LOGIC_OPERATOR
|
||||
}; // namespace pybind11
|
170
include/pybind11/internal/type_traits.h
Normal file
170
include/pybind11/internal/type_traits.h
Normal file
@ -0,0 +1,170 @@
|
||||
#pragma once
|
||||
#include <tuple>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
template <typename T>
|
||||
constexpr bool dependent_false = false;
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
struct tuple_push_front;
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
struct tuple_push_front<T, std::tuple<Ts...>> {
|
||||
using type = std::tuple<T, Ts...>;
|
||||
};
|
||||
|
||||
template <typename T, typename Tuple>
|
||||
using tuple_push_front_t = typename tuple_push_front<T, Tuple>::type;
|
||||
|
||||
// traits for function types
|
||||
template <typename Fn>
|
||||
struct function_traits {
|
||||
static_assert(dependent_false<Fn>, "unsupported function type");
|
||||
};
|
||||
|
||||
#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(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<T>>;
|
||||
using return_type = function_return_t<std::remove_pointer_t<T>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct callable_traits<T, std::enable_if_t<is_functor_v<T>>> {
|
||||
using args_type = function_args_t<member_type_t<decltype(&T::operator())>>;
|
||||
using return_type = function_return_t<member_type_t<decltype(&T::operator())>>;
|
||||
};
|
||||
|
||||
template <typename Callable>
|
||||
using callable_args_t = typename callable_traits<Callable>::args_type;
|
||||
|
||||
template <typename Callable>
|
||||
using callable_return_t = typename callable_traits<Callable>::return_type;
|
||||
|
||||
template <typename Callable>
|
||||
constexpr std::size_t callable_args_count_v = std::tuple_size_v<callable_args_t<Callable>>;
|
||||
|
||||
template <typename T>
|
||||
struct type_identity {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
constexpr inline std::size_t types_count_v = (std::is_same_v<T, Ts> + ...);
|
||||
|
||||
template <typename T>
|
||||
constexpr inline std::size_t types_count_v<T> = 0;
|
||||
|
||||
template <typename T, typename... Ts>
|
||||
constexpr inline int type_index_v = [] {
|
||||
bool arr[sizeof...(Ts) + 1] = {std::is_same_v<T, Ts>...};
|
||||
for(int i = 0; i < sizeof...(Ts); ++i) {
|
||||
if(arr[i]) return i;
|
||||
}
|
||||
return -1;
|
||||
}();
|
||||
|
||||
static_assert(types_count_v<int, int, float, int> == 2);
|
||||
static_assert(types_count_v<int, float, double> == 0);
|
||||
static_assert(type_index_v<int, int, float, int> == 0);
|
||||
static_assert(type_index_v<float, int, float, int> == 1);
|
||||
static_assert(type_index_v<int, float, double> == -1);
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool is_multiple_pointer_v = std::is_pointer_v<T> && is_multiple_pointer_v<std::remove_pointer_t<T>>;
|
||||
|
||||
template <typename T>
|
||||
using pybind11_decay_t = std::decay_t<std::remove_pointer_t<std::decay_t<T>>>;
|
||||
|
||||
template <typename T>
|
||||
constexpr auto type_name() {
|
||||
#if __GNUC__ || __clang__
|
||||
std::string_view name = __PRETTY_FUNCTION__;
|
||||
std::size_t start = name.find('=') + 2;
|
||||
std::size_t end = name.size() - 1;
|
||||
return std::string_view{name.data() + start, end - start};
|
||||
#elif _MSC_VER
|
||||
std::string_view name = __FUNCSIG__;
|
||||
std::size_t start = name.find('<') + 1;
|
||||
std::size_t end = name.rfind(">(");
|
||||
name = std::string_view{name.data() + start, end - start};
|
||||
start = name.find(' ');
|
||||
return start == std::string_view::npos ? name : std::string_view{name.data() + start + 1, name.size() - start - 1};
|
||||
#else
|
||||
static_assert(false, "Unsupported compiler");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace pybind11
|
395
include/pybind11/internal/types.h
Normal file
395
include/pybind11/internal/types.h
Normal file
@ -0,0 +1,395 @@
|
||||
#pragma once
|
||||
#include "object.h"
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
template <typename T>
|
||||
handle cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = {});
|
||||
|
||||
struct arg {
|
||||
const char* name;
|
||||
handle default_;
|
||||
|
||||
arg(const char* name) : name(name), default_() {}
|
||||
|
||||
template <typename T>
|
||||
arg& operator= (T&& value) {
|
||||
default_ = cast(std::forward<T>(value));
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// undef in pybind11.h
|
||||
#define PYBIND11_REGISTER_INIT(func) \
|
||||
static inline int _register = [] { \
|
||||
interpreter::register_init(func); \
|
||||
return 0; \
|
||||
}();
|
||||
|
||||
class none : public object {
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
PYBIND11_TYPE_IMPLEMENT(object, empty, vm->tp_none_type);
|
||||
#else
|
||||
PYBIND11_TYPE_IMPLEMENT(object, empty, [](const handle& obj) {
|
||||
return obj.is_none();
|
||||
});
|
||||
#endif
|
||||
|
||||
public:
|
||||
none() : object(vm->None) {}
|
||||
};
|
||||
|
||||
/// corresponding to type in Python
|
||||
class type : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Type, vm->tp_type);
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
static handle handle_of() {
|
||||
return type_visitor::type<T>();
|
||||
}
|
||||
|
||||
static type of(const handle& obj) { return type(vm->_t(obj.ptr())); }
|
||||
};
|
||||
|
||||
/// corresponding to bool in Python
|
||||
class bool_ : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, bool, vm->tp_bool);
|
||||
|
||||
public:
|
||||
bool_(bool value) : object(create(value)) {}
|
||||
|
||||
operator bool () const { return self(); }
|
||||
};
|
||||
|
||||
/// corresponding to int in Python
|
||||
class int_ : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, pkpy::i64, vm->tp_int);
|
||||
|
||||
public:
|
||||
int_(int64_t value) : object(create(value)) {}
|
||||
|
||||
operator int64_t () const { return self(); }
|
||||
};
|
||||
|
||||
/// corresponding to float in Python
|
||||
class float_ : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, pkpy::f64, vm->tp_float);
|
||||
|
||||
public:
|
||||
float_(double value) : object(create(value)) {}
|
||||
|
||||
operator double () const { return self(); }
|
||||
};
|
||||
|
||||
class iterable : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, empty, [](const handle& obj) {
|
||||
return vm->getattr(obj.ptr(), pkpy::__iter__, false) != nullptr;
|
||||
});
|
||||
};
|
||||
|
||||
class iterator : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, empty, [](const handle& obj) {
|
||||
return vm->getattr(obj.ptr(), pkpy::__next__, false) != nullptr &&
|
||||
vm->getattr(obj.ptr(), pkpy::__iter__, false) != nullptr;
|
||||
});
|
||||
|
||||
handle m_value;
|
||||
|
||||
iterator(pkpy::PyVar n, pkpy::PyVar s) : object(n), m_value(s) {}
|
||||
|
||||
public:
|
||||
iterator(const handle& obj) : object(obj) { m_value = vm->py_next(obj.ptr()); }
|
||||
|
||||
iterator operator++ () {
|
||||
m_value = vm->py_next(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++ (int) {
|
||||
m_value = vm->py_next(m_ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const handle& operator* () const { return m_value; }
|
||||
|
||||
friend bool operator== (const iterator& lhs, const iterator& rhs) { return lhs.m_value.is(rhs.m_value); }
|
||||
|
||||
friend bool operator!= (const iterator& lhs, const iterator& rhs) { return !(lhs == rhs); }
|
||||
|
||||
static iterator sentinel() { return iterator(vm->None, vm->StopIteration); }
|
||||
};
|
||||
|
||||
class str : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Str, vm->tp_str);
|
||||
|
||||
public:
|
||||
str(const char* c, int len) : object(create(c, len)) {};
|
||||
|
||||
str(const char* c = "") : str(c, strlen(c)) {}
|
||||
|
||||
str(const std::string& s) : str(s.data(), s.size()) {}
|
||||
|
||||
str(std::string_view sv) : str(sv.data(), sv.size()) {}
|
||||
|
||||
// explicit str(const bytes& b);
|
||||
explicit str(handle h);
|
||||
|
||||
operator std::string_view () const { return self().sv(); }
|
||||
|
||||
template <typename... Args>
|
||||
str format(Args&&... args) const;
|
||||
};
|
||||
|
||||
// class bytes : public object {
|
||||
// public:
|
||||
// using object::object;
|
||||
// };
|
||||
|
||||
// class bytearray : public object {
|
||||
// public:
|
||||
// using object::object;
|
||||
// };
|
||||
|
||||
class tuple : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Tuple, vm->tp_tuple);
|
||||
|
||||
public:
|
||||
tuple(int n) : object(create(n)) {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<(sizeof...(Args) > 1)>* = nullptr>
|
||||
tuple(Args&&... args) : object(create(sizeof...(Args))) {
|
||||
int index = 0;
|
||||
((self()[index++] = pybind11::cast(std::forward<Args>(args)).ptr()), ...);
|
||||
}
|
||||
|
||||
int size() const { return self().size(); }
|
||||
|
||||
bool empty() const { return size() == 0; }
|
||||
|
||||
tuple_accessor operator[] (int i) const;
|
||||
};
|
||||
|
||||
class list : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, pkpy::List, vm->tp_list)
|
||||
|
||||
public:
|
||||
list() : object(create(0)) {}
|
||||
|
||||
list(int n) : object(create(n)) {}
|
||||
|
||||
template <typename... Args, std::enable_if_t<(sizeof...(Args) > 1)>* = nullptr>
|
||||
list(Args&&... args) : object(create(sizeof...(Args))) {
|
||||
int index = 0;
|
||||
((self()[index++] = pybind11::cast(std::forward<Args>(args)).ptr()), ...);
|
||||
}
|
||||
|
||||
int size() const { return self().size(); }
|
||||
|
||||
bool empty() const { return size() == 0; }
|
||||
|
||||
void clear() { self().clear(); }
|
||||
|
||||
list_accessor operator[] (int i) const;
|
||||
|
||||
void append(const handle& obj) { self().push_back(obj.ptr()); }
|
||||
|
||||
void extend(const handle& iterable) {
|
||||
for(auto& item: iterable) {
|
||||
append(item);
|
||||
}
|
||||
}
|
||||
|
||||
void insert(int index, const handle& obj) {
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
const auto pos = self().begin() + index;
|
||||
self().insert(pos, obj.ptr());
|
||||
#else
|
||||
self().insert(index, obj.ptr());
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
class slice : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Slice, vm->tp_slice);
|
||||
|
||||
public:
|
||||
};
|
||||
|
||||
// class set : public object {
|
||||
// public:
|
||||
// using object::object;
|
||||
// // set() : object(vm->new_object<pkpy::Se>(pkpy::VM::tp_set), true) {}
|
||||
// };
|
||||
//
|
||||
|
||||
class dict : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Dict, vm->tp_dict);
|
||||
|
||||
public:
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
dict() : object(create()) {}
|
||||
|
||||
template <typename... Args, typename = std::enable_if_t<(std::is_same_v<remove_cvref_t<Args>, arg> && ...)>>
|
||||
dict(Args&&... args) : object(create()) {
|
||||
auto foreach_ = [&](pybind11::arg& arg) {
|
||||
setitem(str(arg.name), arg.default_);
|
||||
};
|
||||
(foreach_(args), ...);
|
||||
}
|
||||
|
||||
void setitem(const handle& key, const handle& value) { self().set(vm, key.ptr(), value.ptr()); }
|
||||
|
||||
handle getitem(const handle& key) const { return self().try_get(vm, key.ptr()); }
|
||||
|
||||
struct iterator {
|
||||
pkpy_DictIter iter;
|
||||
std::pair<handle, handle> value;
|
||||
|
||||
iterator operator++ () {
|
||||
bool is_ended = pkpy_DictIter__next(&iter, (PyVar*)&value.first, (PyVar*)&value.second);
|
||||
if(!is_ended) {
|
||||
iter._dict = nullptr;
|
||||
iter._index = -1;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::pair<handle, handle> operator* () const { return value; }
|
||||
|
||||
bool operator== (const iterator& other) const {
|
||||
return iter._dict == other.iter._dict && iter._index == other.iter._index;
|
||||
}
|
||||
|
||||
bool operator!= (const iterator& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
iterator begin() const {
|
||||
iterator iter{self().iter(), {}};
|
||||
++iter;
|
||||
return iter;
|
||||
}
|
||||
|
||||
iterator end() const { return {nullptr, -1}; }
|
||||
#else
|
||||
dict() : object(create(vm)) {}
|
||||
|
||||
template <typename... Args, typename = std::enable_if_t<(std::is_same_v<remove_cvref_t<Args>, arg> && ...)>>
|
||||
dict(Args&&... args) : object(create(vm)) {
|
||||
auto foreach_ = [&](pybind11::arg& arg) {
|
||||
setitem(str(arg.name), arg.default_);
|
||||
};
|
||||
(foreach_(args), ...);
|
||||
}
|
||||
|
||||
void setitem(const handle& key, const handle& value) { self().set(key.ptr(), value.ptr()); }
|
||||
|
||||
handle getitem(const handle& key) const { return self().try_get(key.ptr()); }
|
||||
|
||||
struct iterator {
|
||||
pkpy::Dict::Item* items;
|
||||
pkpy::Dict::ItemNode* nodes;
|
||||
int index;
|
||||
|
||||
iterator operator++ () {
|
||||
index = nodes[index].next;
|
||||
if(index == -1) {
|
||||
items = nullptr;
|
||||
nodes = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::pair<handle, handle> operator* () const { return {items[index].first, items[index].second}; }
|
||||
|
||||
bool operator== (const iterator& other) const {
|
||||
return items == other.items && nodes == other.nodes && index == other.index;
|
||||
}
|
||||
|
||||
bool operator!= (const iterator& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
iterator begin() const {
|
||||
auto index = self()._head_idx;
|
||||
if(index == -1) {
|
||||
return end();
|
||||
} else {
|
||||
return {self()._items, self()._nodes, index};
|
||||
}
|
||||
}
|
||||
|
||||
iterator end() const { return {nullptr, nullptr, -1}; }
|
||||
|
||||
template <typename Key>
|
||||
bool contains(Key&& key) const {
|
||||
return self().contains(vm, pybind11::cast(std::forward<Key>(key)).ptr());
|
||||
}
|
||||
#endif
|
||||
|
||||
int size() const { return self().size(); }
|
||||
|
||||
bool empty() const { return size() == 0; }
|
||||
|
||||
void clear() { self().clear(); }
|
||||
|
||||
dict_accessor operator[] (int index) const;
|
||||
dict_accessor operator[] (std::string_view) const;
|
||||
dict_accessor operator[] (const handle& key) const;
|
||||
};
|
||||
|
||||
class function : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Function, vm->tp_function);
|
||||
};
|
||||
|
||||
//
|
||||
// class buffer : public object {
|
||||
// public:
|
||||
// using object::object;
|
||||
//};
|
||||
//
|
||||
// class memory_view : public object {
|
||||
// public:
|
||||
// using object::object;
|
||||
//};
|
||||
//
|
||||
class capsule : public object {
|
||||
PYBIND11_REGISTER_INIT([] {
|
||||
type_visitor::create<impl::capsule>(vm->builtins, "capsule", true);
|
||||
});
|
||||
|
||||
PYBIND11_TYPE_IMPLEMENT(object, impl::capsule, handle(vm->builtins->attr("capsule"))._as<pkpy::Type>());
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
capsule(T&& value) : object(create(std::forward<T>(value))) {}
|
||||
|
||||
template <typename T>
|
||||
T& cast() const {
|
||||
return *static_cast<T*>(self().ptr);
|
||||
}
|
||||
};
|
||||
|
||||
class property : public object {
|
||||
PYBIND11_TYPE_IMPLEMENT(object, pkpy::Property, vm->tp_property);
|
||||
|
||||
public:
|
||||
#if PK_VERSION_MAJOR == 2
|
||||
property(handle getter, handle setter) : object(create(getter.ptr(), setter.ptr())) {}
|
||||
#else
|
||||
property(handle getter, handle setter) : object(create(pkpy::Property{getter.ptr(), setter.ptr()})) {}
|
||||
#endif
|
||||
|
||||
handle getter() const { return self().getter; }
|
||||
|
||||
handle setter() const { return self().setter; }
|
||||
};
|
||||
|
||||
class args : public tuple {
|
||||
PYBIND11_TYPE_IMPLEMENT(tuple, struct empty, vm->tp_tuple);
|
||||
};
|
||||
|
||||
class kwargs : public dict {
|
||||
PYBIND11_TYPE_IMPLEMENT(dict, struct empty, vm->tp_dict);
|
||||
};
|
||||
|
||||
} // namespace pybind11
|
201
include/pybind11/operators.h
Normal file
201
include/pybind11/operators.h
Normal file
@ -0,0 +1,201 @@
|
||||
#pragma once
|
||||
#include "pybind11.h"
|
||||
|
||||
namespace pybind11::impl {
|
||||
|
||||
enum op_id : int {
|
||||
op_add,
|
||||
op_sub,
|
||||
op_mul,
|
||||
op_div,
|
||||
op_mod,
|
||||
op_divmod,
|
||||
op_pow,
|
||||
op_lshift,
|
||||
op_rshift,
|
||||
op_and,
|
||||
op_xor,
|
||||
op_or,
|
||||
op_neg,
|
||||
op_pos,
|
||||
op_abs,
|
||||
op_invert,
|
||||
op_int,
|
||||
op_long,
|
||||
op_float,
|
||||
op_str,
|
||||
op_cmp,
|
||||
op_gt,
|
||||
op_ge,
|
||||
op_lt,
|
||||
op_le,
|
||||
op_eq,
|
||||
op_ne,
|
||||
op_iadd,
|
||||
op_isub,
|
||||
op_imul,
|
||||
op_idiv,
|
||||
op_imod,
|
||||
op_ilshift,
|
||||
op_irshift,
|
||||
op_iand,
|
||||
op_ixor,
|
||||
op_ior,
|
||||
op_complex,
|
||||
op_bool,
|
||||
op_nonzero,
|
||||
op_repr,
|
||||
op_truediv,
|
||||
op_itruediv,
|
||||
op_hash
|
||||
};
|
||||
|
||||
enum op_type : int {
|
||||
op_l, /* base type on left */
|
||||
op_r, /* base type on right */
|
||||
op_u /* unary operator */
|
||||
};
|
||||
|
||||
struct self_t {};
|
||||
|
||||
const static self_t self = self_t();
|
||||
|
||||
/// Type for an unused type slot
|
||||
struct undefined_t {};
|
||||
|
||||
/// Don't warn about an unused variable
|
||||
inline self_t __self() { return self; }
|
||||
|
||||
/// base template of operator implementations
|
||||
template <op_id, op_type, typename B, typename L, typename R>
|
||||
struct op_impl {};
|
||||
|
||||
/// Operator implementation generator
|
||||
template <op_id id, op_type ot, typename L, typename R>
|
||||
struct op_ {
|
||||
constexpr static bool op_enable_if_hook = true;
|
||||
|
||||
template <typename Class, typename... Extra>
|
||||
void execute(Class& cl, const Extra&... extra) const {
|
||||
using Base = typename Class::underlying_type;
|
||||
using L_type = std::conditional_t<std::is_same<L, self_t>::value, Base, L>;
|
||||
using R_type = std::conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
cl.def(op::name(), &op::execute, extra...);
|
||||
}
|
||||
|
||||
template <typename Class, typename... Extra>
|
||||
void execute_cast(Class& cl, const Extra&... extra) const {
|
||||
using Base = typename Class::type;
|
||||
using L_type = std::conditional_t<std::is_same<L, self_t>::value, Base, L>;
|
||||
using R_type = std::conditional_t<std::is_same<R, self_t>::value, Base, R>;
|
||||
using op = op_impl<id, ot, Base, L_type, R_type>;
|
||||
cl.def(op::name(), &op::execute_cast, extra...);
|
||||
}
|
||||
};
|
||||
|
||||
#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \
|
||||
template <typename B, typename L, typename R> \
|
||||
struct op_impl<op_##id, op_l, B, L, R> { \
|
||||
static char const* name() { return "__" #id "__"; } \
|
||||
static auto execute(const L& l, const R& r) -> decltype(expr) { return (expr); } \
|
||||
static B execute_cast(const L& l, const R& r) { return B(expr); } \
|
||||
}; \
|
||||
\
|
||||
template <typename B, typename L, typename R> \
|
||||
struct op_impl<op_##id, op_r, B, L, R> { \
|
||||
static char const* name() { return "__" #rid "__"; } \
|
||||
static auto execute(const R& r, const L& l) -> decltype(expr) { return (expr); } \
|
||||
static B execute_cast(const R& r, const L& l) { return B(expr); } \
|
||||
}; \
|
||||
\
|
||||
inline op_<op_##id, op_l, self_t, self_t> op(const self_t&, const self_t&) { \
|
||||
return op_<op_##id, op_l, self_t, self_t>(); \
|
||||
} \
|
||||
\
|
||||
template <typename T> \
|
||||
op_<op_##id, op_l, self_t, T> op(const self_t&, const T&) { \
|
||||
return op_<op_##id, op_l, self_t, T>(); \
|
||||
} \
|
||||
\
|
||||
template <typename T> \
|
||||
op_<op_##id, op_r, T, self_t> op(const T&, const self_t&) { \
|
||||
return op_<op_##id, op_r, T, self_t>(); \
|
||||
}
|
||||
|
||||
#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \
|
||||
template <typename B, typename L, typename R> \
|
||||
struct op_impl<op_##id, op_l, B, L, R> { \
|
||||
static char const* name() { return "__" #id "__"; } \
|
||||
static auto execute(L& l, const R& r) -> decltype(expr) { return expr; } \
|
||||
static B execute_cast(L& l, const R& r) { return B(expr); } \
|
||||
}; \
|
||||
\
|
||||
template <typename T> \
|
||||
op_<op_##id, op_l, self_t, T> op(const self_t&, const T&) { \
|
||||
return op_<op_##id, op_l, self_t, T>(); \
|
||||
}
|
||||
|
||||
#define PYBIND11_UNARY_OPERATOR(id, op, expr) \
|
||||
template <typename B, typename L> \
|
||||
struct op_impl<op_##id, op_u, B, L, undefined_t> { \
|
||||
static char const* name() { return "__" #id "__"; } \
|
||||
static auto execute(const L& l) -> decltype(expr) { return expr; } \
|
||||
static B execute_cast(const L& l) { return B(expr); } \
|
||||
}; \
|
||||
\
|
||||
inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t&) { \
|
||||
return op_<op_##id, op_u, self_t, undefined_t>(); \
|
||||
}
|
||||
|
||||
PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r)
|
||||
PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r)
|
||||
PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l* r)
|
||||
PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
|
||||
PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r)
|
||||
PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
|
||||
PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
|
||||
PYBIND11_BINARY_OPERATOR(and, rand, operator&, l& r)
|
||||
PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
|
||||
PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r)
|
||||
PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r)
|
||||
PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r)
|
||||
PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r)
|
||||
PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r)
|
||||
PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r)
|
||||
PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r)
|
||||
// PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r))
|
||||
PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r)
|
||||
PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r)
|
||||
PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
|
||||
PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
|
||||
PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
|
||||
PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
|
||||
PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r)
|
||||
|
||||
PYBIND11_UNARY_OPERATOR(neg, operator-, -l)
|
||||
PYBIND11_UNARY_OPERATOR(pos, operator+, +l)
|
||||
|
||||
// WARNING: This usage of `abs` should only be done for existing STL overloads.
|
||||
// Adding overloads directly in to the `std::` namespace is advised against:
|
||||
// https://en.cppreference.com/w/cpp/language/extending_std
|
||||
|
||||
// PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l))
|
||||
// PYBIND11_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
|
||||
// PYBIND11_UNARY_OPERATOR(invert, operator~, (~l))
|
||||
// PYBIND11_UNARY_OPERATOR(bool, operator!, !!l)
|
||||
// PYBIND11_UNARY_OPERATOR(int, int_, (int)l)
|
||||
// PYBIND11_UNARY_OPERATOR(float, float_, (double)l)
|
||||
|
||||
#undef PYBIND11_BINARY_OPERATOR
|
||||
#undef PYBIND11_INPLACE_OPERATOR
|
||||
#undef PYBIND11_UNARY_OPERATOR
|
||||
|
||||
} // namespace pybind11::impl
|
||||
|
||||
namespace pybind11 {
|
||||
using impl::self;
|
||||
}
|
21
include/pybind11/pybind11.h
Normal file
21
include/pybind11/pybind11.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "internal/class.h"
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
namespace literals {
|
||||
inline arg operator""_a (const char* c, size_t) { return arg(c); }
|
||||
} // namespace literals
|
||||
|
||||
struct scoped_interpreter {
|
||||
scoped_interpreter() { interpreter::initialize(); }
|
||||
|
||||
~scoped_interpreter() { interpreter::finalize(); }
|
||||
};
|
||||
|
||||
} // namespace pybind11
|
||||
|
||||
// namespace pybind11
|
||||
|
||||
#undef PYBIND11_TYPE_IMPLEMENT
|
||||
#undef PYBIND11_REGISTER_INIT
|
144
include/pybind11/stl.h
Normal file
144
include/pybind11/stl.h
Normal file
@ -0,0 +1,144 @@
|
||||
#include "pybind11.h"
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <forward_list>
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace pybind11 {
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct type_caster<std::array<T, N>> {
|
||||
|
||||
struct wrapper {
|
||||
std::array<T, N> container = {};
|
||||
|
||||
operator std::array<T, N>&& () { return std::move(container); }
|
||||
};
|
||||
|
||||
wrapper value;
|
||||
|
||||
bool load(const handle& src, bool convert) {
|
||||
if(!isinstance<list>(src)) { return false; }
|
||||
auto list = src.cast<pybind11::list>();
|
||||
|
||||
if(list.size() != N) { return false; }
|
||||
|
||||
for(std::size_t i = 0; i < N; ++i) {
|
||||
type_caster<T> caster;
|
||||
if(!caster.load(list[i], convert)) { return false; }
|
||||
value.container[i] = caster.value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& src, return_value_policy policy, handle parent) {
|
||||
auto list = pybind11::list();
|
||||
for(auto& item: src) {
|
||||
list.append(pybind11::cast(item, policy, parent));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_py_list_like_v = false;
|
||||
|
||||
template <typename T, typename Allocator>
|
||||
constexpr bool is_py_list_like_v<std::vector<T, Allocator>> = true;
|
||||
|
||||
template <typename T, typename Allocator>
|
||||
constexpr bool is_py_list_like_v<std::list<T, Allocator>> = true;
|
||||
|
||||
template <typename T, typename Allocator>
|
||||
constexpr bool is_py_list_like_v<std::deque<T, Allocator>> = true;
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_py_list_like_v<T>>> {
|
||||
|
||||
struct wrapper {
|
||||
T container;
|
||||
|
||||
operator T&& () { return std::move(container); }
|
||||
};
|
||||
|
||||
wrapper value;
|
||||
|
||||
bool load(const handle& src, bool convert) {
|
||||
if(!isinstance<list>(src)) { return false; }
|
||||
auto list = src.cast<pybind11::list>();
|
||||
|
||||
for(auto item: list) {
|
||||
type_caster<typename T::value_type> caster;
|
||||
if(!caster.load(item, convert)) { return false; }
|
||||
value.container.push_back(caster.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& src, return_value_policy policy, handle parent) {
|
||||
auto list = pybind11::list();
|
||||
for(auto& item: src) {
|
||||
list.append(pybind11::cast(item, policy, parent));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_py_map_like_v = false;
|
||||
|
||||
template <typename Key, typename T, typename Compare, typename Allocator>
|
||||
constexpr bool is_py_map_like_v<std::map<Key, T, Compare, Allocator>> = true;
|
||||
|
||||
template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
|
||||
constexpr bool is_py_map_like_v<std::unordered_map<Key, T, Hash, KeyEqual, Allocator>> = true;
|
||||
|
||||
template <typename T>
|
||||
struct type_caster<T, std::enable_if_t<is_py_map_like_v<T>>> {
|
||||
|
||||
struct wrapper {
|
||||
T container;
|
||||
|
||||
operator T&& () { return std::move(container); }
|
||||
};
|
||||
|
||||
wrapper value;
|
||||
|
||||
bool load(const handle& src, bool convert) {
|
||||
if(!isinstance<dict>(src)) { return false; }
|
||||
auto dict = src.cast<pybind11::dict>();
|
||||
|
||||
for(auto item: dict) {
|
||||
type_caster<typename T::key_type> key_caster;
|
||||
if(!key_caster.load(item.first, convert)) { return false; }
|
||||
|
||||
type_caster<typename T::mapped_type> value_caster;
|
||||
if(!value_caster.load(item.second, convert)) { return false; }
|
||||
|
||||
value.container.try_emplace(key_caster.value, value_caster.value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
static handle cast(U&& src, return_value_policy policy, handle parent) {
|
||||
auto dict = pybind11::dict();
|
||||
for(auto& [key, value]: src) {
|
||||
dict[pybind11::cast(key, policy, parent)] = pybind11::cast(value, policy, parent);
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace pybind11
|
||||
|
@ -518,10 +518,11 @@ i64 VM::py_hash(PyVar obj){
|
||||
}
|
||||
|
||||
PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar locals){
|
||||
Frame* frame = &vm->callstack.top();
|
||||
Frame* frame = nullptr;
|
||||
if(!callstack.empty()) frame = &callstack.top();
|
||||
|
||||
// fast path
|
||||
if(globals == vm->None && locals == vm->None){
|
||||
if(frame && globals == vm->None && locals == vm->None){
|
||||
return vm->_exec(code.get(), frame->_module, frame->_callable, frame->_locals);
|
||||
}
|
||||
|
||||
@ -534,7 +535,7 @@ PyVar VM::__py_exec_internal(const CodeObject_& code, PyVar globals, PyVar local
|
||||
Dict* locals_dict = nullptr;
|
||||
|
||||
if(globals == vm->None){
|
||||
globals_obj = frame->_module;
|
||||
globals_obj = frame ? frame->_module : _main;
|
||||
}else{
|
||||
if(is_type(globals, VM::tp_mappingproxy)){
|
||||
globals_obj = PK_OBJ_GET(MappingProxy, globals).obj;
|
||||
|
Loading…
x
Reference in New Issue
Block a user