#pragma once #include "cffi.h" namespace pkpy{ struct NativeProxyFuncCBase { virtual PyObject* operator()(VM* vm, ArgsView args) = 0; }; template struct NativeProxyFuncC final: NativeProxyFuncCBase { static constexpr int N = sizeof...(Params); using _Fp = Ret(*)(Params...); _Fp func; NativeProxyFuncC(_Fp func) : func(func) {} PyObject* operator()(VM* vm, ArgsView args) override { PK_DEBUG_ASSERT(args.size() == N); return call(vm, args, std::make_index_sequence()); } template PyObject* call(VM* vm, ArgsView args, std::index_sequence){ if constexpr(std::is_void_v<__Ret>){ func(py_cast(vm, args[Is])...); return vm->None; }else{ __Ret ret = func(py_cast(vm, args[Is])...); return VAR(std::move(ret)); } } }; template struct NativeProxyMethodC final: NativeProxyFuncCBase { static constexpr int N = sizeof...(Params); using _Fp = Ret(T::*)(Params...); _Fp func; NativeProxyMethodC(_Fp func) : func(func) {} PyObject* operator()(VM* vm, ArgsView args) override { PK_DEBUG_ASSERT(args.size() == N+1); return call(vm, args, std::make_index_sequence()); } template PyObject* call(VM* vm, ArgsView args, std::index_sequence){ T& self = PK_OBJ_GET(T, args[0]); // use unsafe cast for derived classes if constexpr(std::is_void_v<__Ret>){ (self.*func)(py_cast(vm, args[Is+1])...); return vm->None; }else{ __Ret ret = (self.*func)(py_cast(vm, args[Is+1])...); return VAR(std::move(ret)); } } }; inline PyObject* proxy_wrapper(VM* vm, ArgsView args){ NativeProxyFuncCBase* pf = lambda_get_userdata(args.begin()); return (*pf)(vm, args); } template void _bind(VM* vm, PyObject* obj, const char* sig, Ret(*func)(Params...)){ auto proxy = new NativeProxyFuncC(func); vm->bind(obj, sig, proxy_wrapper, proxy); } template void _bind(VM* vm, PyObject* obj, const char* sig, Ret(T::*func)(Params...)){ auto proxy = new NativeProxyMethodC(func); vm->bind(obj, sig, proxy_wrapper, proxy); } /*****************************************************************/ #define PY_FIELD_EX(T, NAME, REF, EXPR) \ vm->bind_property(type, NAME, \ [](VM* vm, ArgsView args){ \ T& self = PK_OBJ_GET(T, args[0]); \ return VAR(self.REF()->EXPR); \ }, \ [](VM* vm, ArgsView args){ \ T& self = PK_OBJ_GET(T, args[0]); \ self.REF()->EXPR = CAST(decltype(self.REF()->EXPR), args[1]); \ return vm->None; \ }); #define PY_READONLY_FIELD_EX(T, NAME, REF, EXPR) \ vm->bind_property(type, NAME, \ [](VM* vm, ArgsView args){ \ T& self = PK_OBJ_GET(T, args[0]); \ return VAR(self.REF()->EXPR); \ }); #define PY_PROPERTY_EX(T, NAME, REF, FGET, FSET) \ vm->bind_property(type, NAME, \ [](VM* vm, ArgsView args){ \ T& self = PK_OBJ_GET(T, args[0]); \ return VAR(self.REF()->FGET()); \ }, \ [](VM* vm, ArgsView args){ \ T& self = _CAST(T&, args[0]); \ using __NT = decltype(self.REF()->FGET()); \ self.REF()->FSET(CAST(__NT, args[1])); \ return vm->None; \ }); #define PY_READONLY_PROPERTY_EX(T, NAME, REF, FGET) \ vm->bind_property(type, NAME, \ [](VM* vm, ArgsView args){ \ T& self = PK_OBJ_GET(T, args[0]); \ return VAR(self.REF()->FGET()); \ }); /*****************************************************************/ #define PY_FIELD(T, NAME, EXPR) \ vm->bind_property(type, NAME, \ [](VM* vm, ArgsView args){ \ T& self = PK_OBJ_GET(T, args[0]); \ return VAR(self.EXPR); \ }, \ [](VM* vm, ArgsView args){ \ T& self = PK_OBJ_GET(T, args[0]); \ self.EXPR = CAST(decltype(self.EXPR), args[1]); \ return vm->None; \ }); #define PY_READONLY_FIELD(T, NAME, EXPR) \ vm->bind_property(type, NAME, \ [](VM* vm, ArgsView args){ \ T& self = PK_OBJ_GET(T, args[0]); \ return VAR(self.EXPR); \ }); #define PY_PROPERTY(T, NAME, FGET, FSET) \ vm->bind_property(type, NAME, \ [](VM* vm, ArgsView args){ \ T& self = PK_OBJ_GET(T, args[0]); \ return VAR(self.FGET()); \ }, \ [](VM* vm, ArgsView args){ \ T& self = _CAST(T&, args[0]); \ using __NT = decltype(self.FGET()); \ self.FSET(CAST(__NT, args[1])); \ return vm->None; \ }); #define PY_READONLY_PROPERTY(T, NAME, FGET) \ vm->bind_property(type, NAME, \ [](VM* vm, ArgsView args){ \ T& self = PK_OBJ_GET(T, args[0]); \ return VAR(self.FGET()); \ }); /*****************************************************************/ #define PY_STRUCT_LIKE(wT) \ static_assert(std::is_trivially_copyable::value); \ type->attr().set("__struct__", vm->True); \ vm->bind_func<1>(type, "from_struct", [](VM* vm, ArgsView args){ \ C99Struct& s = CAST(C99Struct&, args[0]); \ if(s.size != sizeof(wT)) vm->ValueError("size mismatch"); \ PyObject* obj = vm->new_user_object(); \ memcpy(&_CAST(wT&, obj), s.p, sizeof(wT)); \ return obj; \ }, {}, BindType::STATICMETHOD); \ vm->bind_method<0>(type, "to_struct", [](VM* vm, ArgsView args){ \ wT& self = _CAST(wT&, args[0]); \ return vm->new_user_object(&self, sizeof(wT)); \ }); \ vm->bind_method<0>(type, "addr", [](VM* vm, ArgsView args){ \ wT& self = _CAST(wT&, args[0]); \ return vm->new_user_object(&self); \ }); \ vm->bind_method<0>(type, "copy", [](VM* vm, ArgsView args){ \ wT& self = _CAST(wT&, args[0]); \ return vm->new_user_object(self); \ }); \ vm->bind_method<0>(type, "sizeof", [](VM* vm, ArgsView args){ \ return VAR(sizeof(wT)); \ }); \ vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){ \ wT& self = _CAST(wT&, _0); \ if(!vm->isinstance(_1, vm->_tp_user())) return vm->NotImplemented; \ wT& other = _CAST(wT&, _1); \ return VAR(self == other); \ }); \ #define PY_POINTER_SETGETITEM(T) \ vm->bind__getitem__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){ \ VoidP& self = PK_OBJ_GET(VoidP, _0); \ i64 i = CAST(i64, _1); \ T* tgt = reinterpret_cast(self.ptr); \ return VAR(tgt[i]); \ }); \ vm->bind__setitem__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1, PyObject* _2){ \ VoidP& self = PK_OBJ_GET(VoidP, _0); \ i64 i = CAST(i64, _1); \ T* tgt = reinterpret_cast(self.ptr); \ tgt[i] = CAST(T, _2); \ }); \ } // namespace pkpy