mirror of
				https://github.com/pocketpy/pocketpy
				synced 2025-10-26 14:30:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			208 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			208 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #pragma once
 | |
| 
 | |
| #include "types.h"
 | |
| 
 | |
| namespace pkbind {
 | |
| 
 | |
| template <typename... Args>
 | |
| inline void print(Args&&... args) {
 | |
|     handle print = py_getbuiltin(py_name("print"));
 | |
|     print(std::forward<Args>(args)...);
 | |
| }
 | |
| 
 | |
| inline object eval(std::string_view code, handle globals = none(), handle locals = none()) {
 | |
|     if(globals.is_none() && locals.is_none()) {
 | |
|         std::string src{code};
 | |
|         raise_call<py_eval>(src.c_str(), nullptr);
 | |
|         return object::from_ret();
 | |
|     } else {
 | |
|         handle eval = py_getbuiltin(py_name("eval"));
 | |
|         return eval(str(code),
 | |
|                     globals.is_none() ? dict() : globals,
 | |
|                     locals.is_none() ? dict() : locals);
 | |
|     }
 | |
| }
 | |
| 
 | |
| inline object exec(std::string_view code, handle globals = none(), handle locals = none()) {
 | |
|     if(globals.is_none() && locals.is_none()) {
 | |
|         std::string src{code};
 | |
|         raise_call<py_exec>(src.c_str(), "exec", EXEC_MODE, nullptr);
 | |
|         return object::from_ret();
 | |
|     } else {
 | |
|         handle exec = py_getbuiltin(py_name("exec"));
 | |
|         return exec(str(code), globals, locals);
 | |
|     }
 | |
| }
 | |
| 
 | |
| inline object locals() {
 | |
|     handle locals = py_getbuiltin(py_name("locals"));
 | |
|     return locals();
 | |
| }
 | |
| 
 | |
| inline object globals() {
 | |
|     handle globals = py_getbuiltin(py_name("globals"));
 | |
|     return globals();
 | |
| }
 | |
| 
 | |
| inline bool hasattr(handle obj, name name) {
 | |
|     auto pc = py_peek(0);
 | |
|     auto result = py_getattr(obj.ptr(), name.index());
 | |
|     if(result) {
 | |
|         return true;
 | |
|     } else {
 | |
|         py_clearexc(pc);
 | |
|         return false;
 | |
|     }
 | |
| }
 | |
| 
 | |
| inline object getattr(handle obj, name name) {
 | |
|     raise_call<py_getattr>(obj.ptr(), name.index());
 | |
|     return object::from_ret();
 | |
| }
 | |
| 
 | |
| inline void setattr(handle obj, name name, handle value) {
 | |
|     raise_call<py_setattr>(obj.ptr(), name.index(), value.ptr());
 | |
| }
 | |
| 
 | |
| inline void delattr(handle obj, name name) { raise_call<py_delattr>(obj.ptr(), name.index()); }
 | |
| 
 | |
| inline py_i64 hash(handle obj) {
 | |
|     py_i64 result = 0;
 | |
|     raise_call<py_hash>(obj.ptr(), &result);
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| inline bool isinstance(handle obj, type type) { return py_isinstance(obj.ptr(), type.index()); }
 | |
| 
 | |
| inline bool python_error::match(type type) const { return isinstance(m_exception.ptr(), type); }
 | |
| 
 | |
| inline bool error_already_set::match(type type) const { return py_matchexc(type.index()); }
 | |
| 
 | |
| template <typename T>
 | |
| constexpr inline bool is_pyobject_v =
 | |
|     std::is_base_of_v<object, std::decay_t<T>> || std::is_same_v<type, T>;
 | |
| 
 | |
| template <typename T>
 | |
| inline type type::of() {
 | |
|     if constexpr(is_pyobject_v<T>) {
 | |
|         if constexpr(is_check_v<T>) {
 | |
|             return type(tp_object);
 | |
|         } else {
 | |
|             return type(T::type_or_check());
 | |
|         }
 | |
|     } else {
 | |
|         auto it = m_type_map.find(typeid(T));
 | |
|         if(it != m_type_map.end()) {
 | |
|             return type(it->second);
 | |
|         } else {
 | |
|             // if not found, raise error
 | |
|             std::string msg = "can not c++ instance cast to object, type: {";
 | |
|             msg += type_name<T>();
 | |
|             msg += "} is not registered.";
 | |
|             throw std::runtime_error(msg);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| inline bool type::isinstance(handle obj) {
 | |
|     if constexpr(is_pyobject_v<T>) {
 | |
|         // for every python object wrapper type, there must be a `type_or_check` method.
 | |
|         // for some types, it returns the underlying type in pkpy, e.g., `int_` -> `tp_int`.
 | |
|         // for other types that may not have a corresponding type in pkpy, it returns a check
 | |
|         // function. e.g., `iterable` -> `[](handle h){ return hasattr(h, "iter"); }`.
 | |
|         auto type_or_check = T::type_or_check();
 | |
|         if constexpr(is_check_v<T>) {
 | |
|             return type_or_check(obj);
 | |
|         } else {
 | |
|             return pkbind::isinstance(obj, type(type_or_check));
 | |
|         }
 | |
|     } else {
 | |
|         return pkbind::isinstance(obj, of<T>());
 | |
|     }
 | |
| }
 | |
| 
 | |
| inline bool issubclass(type derived, type base) {
 | |
|     return py_issubclass(derived.index(), base.index());
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| inline bool isinstance(handle obj) {
 | |
|     return type::isinstance<T>(obj);
 | |
| }
 | |
| 
 | |
| template <>
 | |
| inline bool isinstance<handle>(handle) = delete;
 | |
| 
 | |
| template <typename T, typename SFINAE = void>
 | |
| struct type_caster;
 | |
| 
 | |
| template <typename T>
 | |
| object cast(T&& value,
 | |
|             return_value_policy policy = return_value_policy::automatic_reference,
 | |
|             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> &&
 | |
|                  !is_pyobject_v<underlying_type>) {
 | |
|         return object(std::forward<T>(value), object::realloc_t{});
 | |
|     } else if constexpr(is_unique_pointer_v<underlying_type>) {
 | |
|         using pointer = typename underlying_type::pointer;
 | |
|         return type_caster<pointer>::cast(value.release(),
 | |
|                                           return_value_policy::take_ownership,
 | |
|                                           parent);
 | |
|     } else {
 | |
|         static_assert(!is_multiple_pointer_v<underlying_type>,
 | |
|                       "multiple pointer is not supported.");
 | |
|         static_assert(!std::is_void_v<std::remove_pointer_t<underlying_type>>,
 | |
|                       "void* is not supported, consider using py::capsule.");
 | |
| 
 | |
|         // 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(handle obj, bool convert = true) {
 | |
|     using caster_t = type_caster<T>;
 | |
|     constexpr auto is_dangling_v =
 | |
|         (std::is_reference_v<T> || is_pointer_v<T>) && caster_t::is_temporary_v;
 | |
|     static_assert(!is_dangling_v, "dangling reference or pointer is not allowed.");
 | |
|     assert(obj.ptr() != nullptr);
 | |
| 
 | |
|     caster_t caster;
 | |
|     if(caster.load(obj, convert)) {
 | |
|         if constexpr(std::is_reference_v<T>) {
 | |
|             return caster.value();
 | |
|         } else {
 | |
|             return std::move(caster.value());
 | |
|         }
 | |
|     } else {
 | |
|         std::string msg = "cast python instance to c++ failed, obj type is: {";
 | |
|         msg += type::of(obj).name();
 | |
|         msg += "}, target type is: {";
 | |
|         msg += type_name<T>();
 | |
|         msg += "}.";
 | |
|         throw cast_error(msg);
 | |
|     }
 | |
| }
 | |
| 
 | |
| template <typename Derived>
 | |
| template <typename T>
 | |
| T interface<Derived>::cast() const {
 | |
|     return pkbind::cast<T>(handle(this->ptr()), true);
 | |
| }
 | |
| 
 | |
| }  // namespace pkbind
 |