From a172949a3df87950e182fc4d6fd817867a955e86 Mon Sep 17 00:00:00 2001 From: zekexiao Date: Tue, 21 Oct 2025 19:29:36 +0800 Subject: [PATCH] fix method bind --- mrubypp.h | 32 +++---- mrubypp_arena_guard.h | 1 + mrubypp_bind_class.h | 197 ++++++++++++++++++++++-------------------- mrubypp_converters.h | 15 ++++ test/test_class.cpp | 64 ++++++++++++-- 5 files changed, 192 insertions(+), 117 deletions(-) diff --git a/mrubypp.h b/mrubypp.h index 02f8b07..8060057 100644 --- a/mrubypp.h +++ b/mrubypp.h @@ -11,42 +11,42 @@ #include -// 支持自定义类型注册 class mrubypp { public: - mrubypp() { mrb = mrb_open(); } - ~mrubypp() { mrb_close(mrb); } + mrubypp() { mrb_ = mrb_open(); } + ~mrubypp() { mrb_close(mrb_); } void load(const std::string &str) { - if (!mrb) + if (!mrb_) return; - mrb_load_string(mrb, str.c_str()); + mrb_load_string(mrb_, str.c_str()); } template T call(const std::string &funcName) { - mrubypp_arena_guard guard(mrb); - mrb_value result = mrb_funcall(mrb, mrb_top_self(mrb), funcName.data(), 0); - return mrubypp_converter::from_mrb(mrb, result); + mrubypp_arena_guard guard(mrb_); + mrb_value result = + mrb_funcall(mrb_, mrb_top_self(mrb_), funcName.data(), 0); + return mrubypp_converter::from_mrb(mrb_, result); } template T call(const std::string &funcName, Args... args) { - mrubypp_arena_guard guard(mrb); - mrb_value argv[] = {mrubypp_converter::to_mrb(mrb, args)...}; - mrb_sym sym = mrb_intern_cstr(mrb, funcName.data()); + mrubypp_arena_guard guard(mrb_); + mrb_value argv[] = {mrubypp_converter::to_mrb(mrb_, args)...}; + mrb_sym sym = mrb_intern_cstr(mrb_, funcName.data()); mrb_value result = - mrb_funcall_argv(mrb, mrb_top_self(mrb), sym, sizeof...(Args), argv); - return mrubypp_converter::from_mrb(mrb, result); + mrb_funcall_argv(mrb_, mrb_top_self(mrb_), sym, sizeof...(Args), argv); + return mrubypp_converter::from_mrb(mrb_, result); } template mrubypp_class_builder class_builder(const std::string &class_name) { - return mrubypp_class_builder(mrb, class_name); + return mrubypp_class_builder(mrb_, class_name); } - [[nodiscard]] mrb_state *get_mrb() const { return mrb; } + [[nodiscard]] mrb_state *get_mrb() const { return mrb_; } private: - mrb_state *mrb; + mrb_state *mrb_; }; #endif // MRUBYPP_H diff --git a/mrubypp_arena_guard.h b/mrubypp_arena_guard.h index 68fd490..15f3588 100644 --- a/mrubypp_arena_guard.h +++ b/mrubypp_arena_guard.h @@ -7,6 +7,7 @@ #include +// gc arena class mrubypp_arena_guard { public: explicit mrubypp_arena_guard(mrb_state *mrb) : mrb(mrb) { diff --git a/mrubypp_bind_class.h b/mrubypp_bind_class.h index 41d4987..7eec5c2 100644 --- a/mrubypp_bind_class.h +++ b/mrubypp_bind_class.h @@ -13,6 +13,7 @@ #include #include +#include template std::tuple mrubypp_get_args_helper_impl(mrb_state *mrb, @@ -40,15 +41,19 @@ mrubypp_get_args_helper(mrb_state *mrb) { template struct mrubypp_method_wrapper { - static Ret (T::*method)(MethodArgs...); - static mrb_value wrapper(mrb_state *mrb, mrb_value self) { auto args = mrubypp_get_args_helper(mrb); T *instance = static_cast(DATA_PTR(self)); if (!instance) { - mrb_raise(mrb, E_RUNTIME_ERROR, "Invalid instance"); + mrb_raise(mrb, E_RUNTIME_ERROR, "invalid instance"); } + union { + Ret (T::*method)(MethodArgs...); + void *ptr; + } converter; + converter.ptr = mrb_cptr(mrb_cfunc_env_get(mrb, 0)); + auto method = converter.method; if constexpr (std::is_same_v) { std::apply(method, std::tuple_cat(std::make_tuple(instance), args)); return mrb_nil_value(); @@ -62,14 +67,19 @@ struct mrubypp_method_wrapper { template struct mrubypp_const_method_wrapper { - static Ret (T::*method)(MethodArgs...) const; - static mrb_value wrapper(mrb_state *mrb, mrb_value self) { auto args = mrubypp_get_args_helper(mrb); T *instance = static_cast(DATA_PTR(self)); if (!instance) { - mrb_raise(mrb, E_RUNTIME_ERROR, "Invalid instance"); + mrb_raise(mrb, E_RUNTIME_ERROR, "invalid instance"); } + union { + Ret (T::*method)(MethodArgs...) const; + void *ptr; + } converter; + + converter.ptr = mrb_cptr(mrb_cfunc_env_get(mrb, 0)); + auto method = converter.method; if constexpr (std::is_same_v) { std::apply(method, std::tuple_cat(std::make_tuple(instance), args)); @@ -82,10 +92,16 @@ struct mrubypp_const_method_wrapper { } }; -template struct mrubypp_function_wrapper { - static Ret (*func)(FuncArgs...); - +template +struct mrubypp_function_wrapper { static mrb_value wrapper(mrb_state *mrb, mrb_value self) { + union { + Ret (*func)(FuncArgs...); + void *ptr; + } converter; + + converter.ptr = mrb_cptr(mrb_cfunc_env_get(mrb, 0)); + auto func = converter.func; auto args = mrubypp_get_args_helper(mrb); if constexpr (std::is_same_v) { std::apply(func, args); @@ -97,34 +113,6 @@ template struct mrubypp_function_wrapper { } }; -template struct mrubypp_getter_wrapper { - static Ret (T::*getter)() const; - - static mrb_value wrapper(mrb_state *mrb, mrb_value self) { - T *instance = static_cast(DATA_PTR(self)); - if (!instance) { - mrb_raise(mrb, E_RUNTIME_ERROR, "Invalid instance"); - } - - Ret result = (instance->*getter)(); - return mrubypp_converter::to_mrb(mrb, result); - } -}; - -template struct mrubypp_setter_wrapper { - static void (T::*setter)(Ret); - - static mrb_value wrapper(mrb_state *mrb, mrb_value self) { - auto args = mrubypp_get_args_helper(mrb); - T *instance = static_cast(DATA_PTR(self)); - if (!instance) { - mrb_raise(mrb, E_RUNTIME_ERROR, "Invalid instance"); - } - - std::apply(setter, std::tuple_cat(std::make_tuple(instance), args)); - return self; - } -}; template class mrubypp_class_builder; template struct mrubypp_constructor_wrapper { @@ -139,18 +127,6 @@ template struct mrubypp_constructor_wrapper { }; template class mrubypp_class_builder { -private: - mrb_state *mrb_; - struct RClass *rclass_; - std::string name_; - - static void free_instance(mrb_state *mrb, void *ptr) { - if (ptr) { - T *instance = static_cast(ptr); - delete instance; - } - } - public: static const struct mrb_data_type data_type; @@ -163,7 +139,7 @@ public: mrb_, rclass_, "initialize", [](mrb_state *mrb, mrb_value self) -> mrb_value { mrb_raise(mrb, E_NOTIMP_ERROR, - "Initialize must be defined explicitly"); + "initialize must be defined explicitly"); return self; }, MRB_ARGS_ANY()); @@ -173,67 +149,120 @@ public: mrb_define_method(mrb_, rclass_, "initialize", &mrubypp_constructor_wrapper::wrapper, MRB_ARGS_REQ(sizeof...(Args))); - return *this; } template mrubypp_class_builder &def_method(const std::string &name, Ret (T::*method)(MethodArgs...)) { - mrubypp_method_wrapper::method = method; - mrb_define_method(mrb_, rclass_, name.c_str(), - &mrubypp_method_wrapper::wrapper, - MRB_ARGS_REQ(sizeof...(MethodArgs))); + mrb_sym method_sym = mrb_intern_cstr(mrb_, name.c_str()); + union { + Ret (T::*method_ptr)(MethodArgs...); + void *ptr; + } converter; + converter.method_ptr = method; + + mrb_value env[] = { + mrb_cptr_value(mrb_, converter.ptr), + }; + struct RProc *p = mrb_proc_new_cfunc_with_env( + mrb_, &mrubypp_method_wrapper::wrapper, 1, env); + mrb_method_t m; + MRB_METHOD_FROM_PROC(m, p); + mrb_define_method_raw(mrb_, rclass_, method_sym, m); + // MRB_ARGS_REQ(sizeof...(MethodArgs)) return *this; } template mrubypp_class_builder &def_method(const std::string &name, Ret (T::*method)(MethodArgs...) const) { - mrubypp_const_method_wrapper::method = method; - - mrb_define_method( - mrb_, rclass_, name.c_str(), - &mrubypp_const_method_wrapper::wrapper, - MRB_ARGS_REQ(sizeof...(MethodArgs))); + mrb_sym method_sym = mrb_intern_cstr(mrb_, name.c_str()); + union { + Ret (T::*method_ptr)(MethodArgs...) const; + void *ptr; + } converter; + converter.method_ptr = method; + mrb_value env[] = { + mrb_cptr_value(mrb_, converter.ptr), + }; + struct RProc *p = mrb_proc_new_cfunc_with_env( + mrb_, &mrubypp_const_method_wrapper::wrapper, 1, + env); + mrb_method_t m; + MRB_METHOD_FROM_PROC(m, p); + mrb_define_method_raw(mrb_, rclass_, method_sym, m); return *this; } template mrubypp_class_builder &def_class_method(const std::string &name, Ret (*func)(FuncArgs...)) { - mrubypp_function_wrapper::func = func; - mrb_define_class_method( - mrb_, rclass_, name.c_str(), - &mrubypp_function_wrapper::wrapper, - MRB_ARGS_REQ(sizeof...(FuncArgs))); + union { + Ret (*funcPtr)(FuncArgs...); + void *ptr; + } converter; + converter.funcPtr = func; + mrb_value env[] = { + mrb_cptr_value(mrb_, converter.ptr), + }; + + mrb_sym func_sym = mrb_intern_cstr(mrb_, name.c_str()); + struct RProc *p = mrb_proc_new_cfunc_with_env( + mrb_, &mrubypp_function_wrapper::wrapper, 1, env); + mrb_method_t m; + MRB_METHOD_FROM_PROC(m, p); + + // register and replace + mrb_define_class_method_id(mrb_, rclass_, func_sym, NULL, MRB_ARGS_ANY()); + mrb_define_method_raw(mrb_, rclass_->c, func_sym, m); return *this; } - template + // get set + template mrubypp_class_builder &def_property(const std::string &name, Ret (T::*getter)() const, void (T::*setter)(Ret)) { - mrubypp_getter_wrapper::getter = getter; - mrb_define_method(mrb_, rclass_, name.c_str(), - &mrubypp_getter_wrapper::wrapper, - MRB_ARGS_NONE()); - std::string setter_name = name + "="; - mrubypp_setter_wrapper::setter = setter; - mrb_define_method(mrb_, rclass_, setter_name.c_str(), - &mrubypp_setter_wrapper::wrapper, - MRB_ARGS_REQ(1)); + def_method(name, getter); + def_method(name + "=", setter); + return *this; + } + mrubypp_class_builder &def_native(const std::string &name, mrb_func_t func, + mrb_aspec aspec = MRB_ARGS_ANY()) { + mrb_define_method(mrb_, rclass_, name.c_str(), func, aspec); return *this; } [[nodiscard]] struct RClass *get_rclass() const { return rclass_; } + + [[nodiscard]] static T *get_this(mrb_state *mrb, mrb_value value) { + T *instance = static_cast(DATA_PTR(value)); + if (!instance) { + mrb_raise(mrb, E_RUNTIME_ERROR, "invalid instance"); + } + return instance; + } + +private: + static void free_instance(mrb_state *mrb, void *ptr) { + if (ptr) { + T *instance = static_cast(ptr); + delete instance; + } + } + static std::map method_map; + + mrb_state *mrb_; + struct RClass *rclass_; + std::string name_; }; template @@ -241,20 +270,4 @@ const struct mrb_data_type mrubypp_class_builder::data_type = { typeid(T).name(), mrubypp_class_builder::free_instance }; -template -Ret (T::*mrubypp_method_wrapper::method)(MethodArgs...); - -template -Ret (T::*mrubypp_const_method_wrapper::method)( - MethodArgs...) const; - -template -Ret (*mrubypp_function_wrapper::func)(FuncArgs...); - -template -Ret (T::*mrubypp_getter_wrapper::getter)() const; - -template -void (T::*mrubypp_setter_wrapper::setter)(Ret); - #endif diff --git a/mrubypp_converters.h b/mrubypp_converters.h index 2f417eb..e884ee8 100644 --- a/mrubypp_converters.h +++ b/mrubypp_converters.h @@ -29,6 +29,15 @@ template <> struct mrubypp_converter { } }; +template <> struct mrubypp_converter { + static mrb_value to_mrb(mrb_state *mrb, unsigned int var) { + return mrb_fixnum_value(var); + } + static int from_mrb(mrb_state *mrb, mrb_value value) { + return mrb_fixnum(value); + } +}; + template <> struct mrubypp_converter { static mrb_value to_mrb(mrb_state *mrb, float var) { return mrb_float_value(mrb, var); @@ -60,6 +69,12 @@ template <> struct mrubypp_converter { } }; +template <> struct mrubypp_converter { + static mrb_value to_mrb(mrb_state *mrb, const char *var) { + return mrb_str_new(mrb, var, (mrb_int)strlen(var)); + } +}; + template struct mrubypp_converter> { static mrb_value to_mrb(mrb_state *mrb, const std::vector &var) { mrb_value ary = mrb_ary_new_capa(mrb, static_cast(var.size())); diff --git a/test/test_class.cpp b/test/test_class.cpp index 17e3087..ca150d7 100644 --- a/test/test_class.cpp +++ b/test/test_class.cpp @@ -16,15 +16,22 @@ public: } int get_x() const { return x_; } - void set_x(int x) { x_ = x; } + void set_x(int x) { + // + x_ = x; + } int get_y() const { return y_; } - void set_y(int y) { y_ = y; } + void set_y(int y) { + // + y_ = y; + } void add(const Point &other) { this->x_ += other.x_; this->y_ += other.y_; } + static int none() { return 1; } private: @@ -32,6 +39,14 @@ private: int y_; }; +static mrb_value point_native_div(mrb_state *mrb, mrb_value self) { + auto point = mrubypp_class_builder::get_this(mrb, self); + auto divisor = mrubypp_converter::from_mrb(mrb, mrb_get_arg1(mrb)); + point->set_x(point->get_x() / divisor); + point->set_y(point->get_y() / divisor); + return self; +} + template <> struct mrubypp_converter { static mrb_value to_mrb(mrb_state *mrb, const Point &var) { mrb_value obj = mrb_obj_value( @@ -55,20 +70,51 @@ TEST_CASE("Point", "[class]") { .def_constructor() .def_method("add", &Point::add) .def_class_method("none", &Point::none) - .def_property("x", &Point::get_x, &Point::set_x); + .def_property("x", &Point::get_x, &Point::set_x) + .def_property("y", &Point::get_y, &Point::set_y) + .def_native("div", point_native_div, MRB_ARGS_REQ(1)); engine.load(R"( - def test_point() + def test_method() + p = Point.new(3, 4) + p.add(p) + return p + end + def test_property() p = Point.new(3, 4) p.x = 10 - p.add(p) - p.x += Point::none() + p.y = p.y + p.y return p end + def test_native() + p = Point.new(4, 8) + p.div(2) + return p + end + def test_class_method() + return Point::none() + end )"); - auto point_result = engine.call("test_point"); + SECTION("test_method") { + auto point_result = engine.call("test_method"); + REQUIRE(point_result.get_x() == 6); + REQUIRE(point_result.get_y() == 8); + } - REQUIRE(point_result.get_x() == 21); - REQUIRE(point_result.get_y() == 8); + SECTION("test_property") { + auto point_result = engine.call("test_property"); + REQUIRE(point_result.get_x() == 10); + REQUIRE(point_result.get_y() == 8); + } + + SECTION("test_native") { + auto point_result = engine.call("test_native"); + REQUIRE(point_result.get_x() == 2); + REQUIRE(point_result.get_y() == 4); + } + SECTION("test_class_method") { + auto result = engine.call("test_class_method"); + REQUIRE(result == 1); + } } \ No newline at end of file