diff --git a/amalgamate.py b/amalgamate.py index 150b63d9..08265c58 100644 --- a/amalgamate.py +++ b/amalgamate.py @@ -9,7 +9,7 @@ pipeline = [ ["config.h", "common.h", "memory.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h", "lexer.h"], ["obj.h", "dict.h", "codeobject.h", "frame.h"], ["gc.h", "vm.h", "ceval.h", "expr.h", "compiler.h", "repl.h"], - ["_generated.h", "cffi.h", "iter.h", "base64.h", "random.h", "re.h", "linalg.h", "easing.h", "io.h"], + ["_generated.h", "cffi.h", "bindings.h", "iter.h", "base64.h", "random.h", "re.h", "linalg.h", "easing.h", "io.h"], ["export.h", "pocketpy.h"] ] diff --git a/include/pocketpy/bindings.h b/include/pocketpy/bindings.h new file mode 100644 index 00000000..e3cce869 --- /dev/null +++ b/include/pocketpy/bindings.h @@ -0,0 +1,125 @@ +#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_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) + 1; + using _Fp = Ret(T::*)(Params...); + _Fp func; + NativeProxyMethodC(_Fp func) : func(func) {} + + PyObject* operator()(VM* vm, ArgsView args) override { + PK_ASSERT(args.size() == N); + return call(vm, args, std::make_index_sequence()); + } + + template + PyObject* call(VM* vm, ArgsView args, std::index_sequence){ + T& self = py_cast(vm, args[0]); + 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); +} +/*****************************************************************/ + +template +struct OpaquePointer{ + T* ptr; + OpaquePointer(T* ptr): ptr(ptr){} + + T* operator->(){ return ptr; } +}; + +#define PK_REGISTER_FIELD(T, NAME) \ + type->attr().set(#NAME, vm->property( \ + [](VM* vm, ArgsView args){ \ + T& self = _CAST(T&, args[0]); \ + return VAR(self->NAME); \ + }, \ + [](VM* vm, ArgsView args){ \ + T& self = _CAST(T&, args[0]); \ + self->NAME = CAST(decltype(self->NAME), args[1]); \ + return vm->None; \ + })); + +#define PK_REGISTER_READONLY_FIELD(T, NAME) \ + type->attr().set(#NAME, vm->property( \ + [](VM* vm, ArgsView args){ \ + T& self = _CAST(T&, args[0]); \ + return VAR(self->NAME); \ + })); + +#define PK_REGISTER_PROPERTY(T, NAME) \ + type->attr().set(#NAME, vm->property( \ + [](VM* vm, ArgsView args){ \ + T& self = _CAST(T&, args[0]); \ + return VAR(self->get_##NAME()); \ + }, \ + [](VM* vm, ArgsView args){ \ + T& self = _CAST(T&, args[0]); \ + using __NT = decltype(self->get_##NAME()); \ + self->set_##NAME(CAST(__NT, args[1])); \ + return vm->None; \ + })); + +#define PK_REGISTER_READONLY_PROPERTY(T, NAME) \ + type->attr().set(#NAME, vm->property( \ + [](VM* vm, ArgsView args){ \ + T& self = _CAST(T&, args[0]); \ + return VAR(self->get_##NAME()); \ + })); + +} // namespace pkpy \ No newline at end of file diff --git a/include/pocketpy/cffi.h b/include/pocketpy/cffi.h index 8e5afabd..bff32bdd 100644 --- a/include/pocketpy/cffi.h +++ b/include/pocketpy/cffi.h @@ -166,55 +166,6 @@ std::enable_if_t && !std::is_pointer_v, PyObject*> py_var(VM return VAR_T(C99Struct, std::monostate(), data); } /*****************************************************************/ -struct NativeProxyFuncCBase { - virtual PyObject* operator()(VM* vm, ArgsView args) = 0; - - static void check_args_size(VM* vm, ArgsView args, int n){ - if (args.size() != n){ - vm->TypeError("expected " + std::to_string(n) + " arguments, got " + std::to_string(args.size())); - } - } -}; - -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 { - check_args_size(vm, args, 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)); - } - } -}; - -inline PyObject* _any_c_wrapper(VM* vm, ArgsView args){ - NativeProxyFuncCBase* pf = lambda_get_userdata(args.begin()); - return (*pf)(vm, args); -} - -template -void bind_any_c_fp(VM* vm, PyObject* obj, Str name, T fp){ - static_assert(std::is_pod_v); - static_assert(std::is_pointer_v); - auto proxy = new NativeProxyFuncC(fp); - PyObject* func = VAR(NativeFunc(_any_c_wrapper, proxy->N, false)); - _CAST(NativeFunc&, func).set_userdata(proxy); - obj->attr().set(name, func); -} - void add_module_c(VM* vm); } // namespace pkpy \ No newline at end of file diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index c8f36c07..92921813 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -14,6 +14,7 @@ #include "vm.h" #include "re.h" #include "random.h" +#include "bindings.h" namespace pkpy { diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index 70700913..f8ccefa8 100644 --- a/include/pocketpy/vm.h +++ b/include/pocketpy/vm.h @@ -451,10 +451,10 @@ public: PyObject* _run_top_frame(); void post_init(); PyObject* _py_generator(Frame&& frame, ArgsView buffer); - // new style binding api - PyObject* bind(PyObject*, const char*, const char*, NativeFuncC); - PyObject* bind(PyObject*, const char*, NativeFuncC); void _prepare_py_call(PyObject**, ArgsView, ArgsView, const FuncDecl_&); + // new style binding api + PyObject* bind(PyObject*, const char*, const char*, NativeFuncC, void* userdata=nullptr); + PyObject* bind(PyObject*, const char*, NativeFuncC, void* userdata=nullptr); }; DEF_NATIVE_2(Str, tp_str) diff --git a/src/vm.cpp b/src/vm.cpp index b97a2ffb..fed28252 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -956,17 +956,16 @@ void VM::setattr(PyObject* obj, StrName name, PyObject* value){ obj->attr().set(name, value); } -PyObject* VM::bind(PyObject* obj, const char* sig, NativeFuncC fn){ - return bind(obj, sig, nullptr, fn); +PyObject* VM::bind(PyObject* obj, const char* sig, NativeFuncC fn, void* userdata){ + return bind(obj, sig, nullptr, fn, userdata); } -PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, NativeFuncC fn){ +PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, NativeFuncC fn, void* userdata){ CodeObject_ co; try{ // fn(a, b, *c, d=1) -> None co = compile("def " + Str(sig) + " : pass", "", EXEC_MODE); - }catch(Exception& e){ - PK_UNUSED(e); + }catch(Exception&){ throw std::runtime_error("invalid signature: " + std::string(sig)); } if(co->func_decls.size() != 1){ @@ -978,6 +977,9 @@ PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Native decl->docstring = Str(docstring).strip(); } PyObject* f_obj = VAR(NativeFunc(fn, decl)); + if(userdata != nullptr){ + PK_OBJ_GET(NativeFunc, f_obj).set_userdata(userdata); + } obj->attr().set(decl->code->name, f_obj); return f_obj; } diff --git a/src2/main.cpp b/src2/main.cpp index bdb1c35f..26e59c89 100644 --- a/src2/main.cpp +++ b/src2/main.cpp @@ -10,7 +10,7 @@ std::string f_input(){ int main(int argc, char** argv){ pkpy::VM* vm = pkpy_new_vm(); - pkpy::bind_any_c_fp(vm, vm->builtins, "input", &f_input); + pkpy::_bind(vm, vm->builtins, "input() -> str", &f_input); if(argc == 1){ pkpy::REPL* repl = pkpy_new_repl(vm);