From 72db94928cb28f277a786f355a6f9c6d85d91cb2 Mon Sep 17 00:00:00 2001 From: zekexiao Date: Wed, 22 Oct 2025 22:11:58 +0800 Subject: [PATCH] fix failed bind method in gcc/clang --- CMakeLists.txt | 1 + mrubypp_bind_class.h | 157 +++++++++++++++++++++++-------------------- test/test_class.cpp | 22 ++++-- test/test_engine.cpp | 2 +- 4 files changed, 102 insertions(+), 80 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fa6a40..e531a7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project(mrubypp LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(mruby_ROOT "/mnt/d/Sources/mruby/build/host") option(MRUBYPP_BUILD_TEST "Build Catch2 Test" OFF) diff --git a/mrubypp_bind_class.h b/mrubypp_bind_class.h index 7eec5c2..4de8680 100644 --- a/mrubypp_bind_class.h +++ b/mrubypp_bind_class.h @@ -15,16 +15,16 @@ #include #include -template +template std::tuple mrubypp_get_args_helper_impl(mrb_state *mrb, const mrb_value *argv, std::index_sequence) { return std::make_tuple( - mrubypp_converter::type>::from_mrb( - mrb, argv[Is])...); + mrubypp_converter::type>::from_mrb( + mrb, argv[Is])...); } -template +template std::tuple::type...> mrubypp_get_args_helper(mrb_state *mrb) { mrubypp_arena_guard guard(mrb); @@ -36,24 +36,37 @@ mrubypp_get_args_helper(mrb_state *mrb) { } return mrubypp_get_args_helper_impl::type...>( - mrb, argv, std::index_sequence_for{}); + mrb, argv, std::index_sequence_for{}); } -template +template struct mrubypp_method_wrapper { + using MethodType = Ret (T::*)(MethodArgs...); + + // TODO, fix memory leak + static void *method_to_ptr(MethodType method) { + auto ptr = malloc(sizeof(MethodType)); + memcpy(ptr, &method, sizeof(MethodType)); + return ptr; + } + + static MethodType ptr_to_method(void *ptr) { + MethodType method; + memcpy(&method, ptr, sizeof(MethodType)); + return method; + } + 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"); } - union { - Ret (T::*method)(MethodArgs...); - void *ptr; - } converter; - converter.ptr = mrb_cptr(mrb_cfunc_env_get(mrb, 0)); - auto method = converter.method; + + void *method_ptr = mrb_cptr(mrb_cfunc_env_get(mrb, 0)); + auto method = ptr_to_method(method_ptr); + if constexpr (std::is_same_v) { std::apply(method, std::tuple_cat(std::make_tuple(instance), args)); return mrb_nil_value(); @@ -65,25 +78,36 @@ struct mrubypp_method_wrapper { } }; -template +template struct mrubypp_const_method_wrapper { + using MethodType = Ret (T::*)(MethodArgs...) const; + + // TODO, fix memory leak + static void *method_to_ptr(MethodType method) { + auto ptr = malloc(sizeof(MethodType)); + memcpy(ptr, &method, sizeof(MethodType)); + return ptr; + } + + static MethodType ptr_to_method(void *ptr) { + MethodType method; + memcpy(&method, ptr, sizeof(MethodType)); + return method; + } + + 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"); } - union { - Ret (T::*method)(MethodArgs...) const; - void *ptr; - } converter; - converter.ptr = mrb_cptr(mrb_cfunc_env_get(mrb, 0)); - auto method = converter.method; + void *method_ptr = mrb_cptr(mrb_cfunc_env_get(mrb, 0)); + auto method = ptr_to_method(method_ptr); if constexpr (std::is_same_v) { std::apply(method, std::tuple_cat(std::make_tuple(instance), args)); - return mrb_nil_value(); } else { Ret result = std::apply(method, std::tuple_cat(std::make_tuple(instance), args)); @@ -92,16 +116,12 @@ struct mrubypp_const_method_wrapper { } }; -template +template struct mrubypp_function_wrapper { - static mrb_value wrapper(mrb_state *mrb, mrb_value self) { - union { - Ret (*func)(FuncArgs...); - void *ptr; - } converter; + using FunctionType = Ret (*)(FuncArgs...); - converter.ptr = mrb_cptr(mrb_cfunc_env_get(mrb, 0)); - auto func = converter.func; + static mrb_value wrapper(mrb_state *mrb, mrb_value self) { + auto func = (FunctionType) mrb_cptr(mrb_cfunc_env_get(mrb, 0)); auto args = mrubypp_get_args_helper(mrb); if constexpr (std::is_same_v) { std::apply(func, args); @@ -113,9 +133,11 @@ struct mrubypp_function_wrapper { } }; -template class mrubypp_class_builder; +template +class mrubypp_class_builder; -template struct mrubypp_constructor_wrapper { +template +struct mrubypp_constructor_wrapper { static mrb_value wrapper(mrb_state *mrb, mrb_value self) { auto args = mrubypp_get_args_helper(mrb); T *instance = std::apply([](Args... args) { return new T(args...); }, args); @@ -126,49 +148,47 @@ template struct mrubypp_constructor_wrapper { } }; -template class mrubypp_class_builder { +template +class mrubypp_class_builder { public: static const struct mrb_data_type data_type; mrubypp_class_builder(mrb_state *mrb, std::string name) - : mrb_(mrb), name_(std::move(name)) { + : mrb_(mrb), name_(std::move(name)) { rclass_ = mrb_define_class(mrb_, name_.c_str(), mrb_->object_class); MRB_SET_INSTANCE_TT(rclass_, MRB_TT_DATA); mrb_define_method( - mrb_, rclass_, "initialize", - [](mrb_state *mrb, mrb_value self) -> mrb_value { - mrb_raise(mrb, E_NOTIMP_ERROR, - "initialize must be defined explicitly"); - return self; - }, - MRB_ARGS_ANY()); + mrb_, rclass_, "initialize", + [](mrb_state *mrb, mrb_value self) -> mrb_value { + mrb_raise(mrb, E_NOTIMP_ERROR, + "initialize must be defined explicitly"); + return self; + }, + MRB_ARGS_ANY()); } - template mrubypp_class_builder &def_constructor() { + template + mrubypp_class_builder &def_constructor() { mrb_define_method(mrb_, rclass_, "initialize", &mrubypp_constructor_wrapper::wrapper, MRB_ARGS_REQ(sizeof...(Args))); return *this; } - template + template mrubypp_class_builder &def_method(const std::string &name, Ret (T::*method)(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; + using wrap = mrubypp_method_wrapper; + auto ptr = wrap::method_to_ptr(method); mrb_value env[] = { - mrb_cptr_value(mrb_, converter.ptr), + mrb_cptr_value(mrb_, ptr), }; struct RProc *p = mrb_proc_new_cfunc_with_env( - mrb_, &mrubypp_method_wrapper::wrapper, 1, env); + mrb_, &wrap::wrapper, 1, env); mrb_method_t m; MRB_METHOD_FROM_PROC(m, p); mrb_define_method_raw(mrb_, rclass_, method_sym, m); @@ -176,45 +196,39 @@ public: return *this; } - template + template mrubypp_class_builder &def_method(const std::string &name, Ret (T::*method)(MethodArgs...) const) { mrb_sym method_sym = mrb_intern_cstr(mrb_, name.c_str()); - union { - Ret (T::*method_ptr)(MethodArgs...) const; - void *ptr; - } converter; + using wrap = mrubypp_const_method_wrapper; - converter.method_ptr = method; + auto ptr = wrap::method_to_ptr(method); + auto obj = mrb_cptr_value(mrb_, ptr); + mrb_gc_protect(mrb_, obj); mrb_value env[] = { - mrb_cptr_value(mrb_, converter.ptr), + obj }; + struct RProc *p = mrb_proc_new_cfunc_with_env( - mrb_, &mrubypp_const_method_wrapper::wrapper, 1, - env); + mrb_, &wrap::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 + template mrubypp_class_builder &def_class_method(const std::string &name, Ret (*func)(FuncArgs...)) { - - union { - Ret (*funcPtr)(FuncArgs...); - void *ptr; - } converter; - - converter.funcPtr = func; + using wrap = mrubypp_function_wrapper; mrb_value env[] = { - mrb_cptr_value(mrb_, converter.ptr), + mrb_cptr_value(mrb_, (void *) func), }; 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_, &wrap::wrapper, 1, env); mrb_method_t m; MRB_METHOD_FROM_PROC(m, p); @@ -225,11 +239,10 @@ public: } // get set - template + template mrubypp_class_builder &def_property(const std::string &name, Ret (T::*getter)() const, void (T::*setter)(Ret)) { - def_method(name, getter); def_method(name + "=", setter); return *this; @@ -258,14 +271,14 @@ private: delete instance; } } - static std::map method_map; + mrb_state *mrb_; struct RClass *rclass_; std::string name_; }; -template +template const struct mrb_data_type mrubypp_class_builder::data_type = { typeid(T).name(), mrubypp_class_builder::free_instance }; diff --git a/test/test_class.cpp b/test/test_class.cpp index c97b790..4e89f9d 100644 --- a/test/test_class.cpp +++ b/test/test_class.cpp @@ -8,7 +8,8 @@ class Point { public: - Point(int x, int y) : x_(x), y_(y) {} + Point(int x, int y) : x_(x), y_(y) { + } void set_values(int x, int y) { x_ = x; @@ -16,10 +17,16 @@ 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_; @@ -41,11 +48,12 @@ static mrb_value point_native_div(mrb_state *mrb, mrb_value self) { return self; } -template <> struct mrubypp_converter { +template<> +struct mrubypp_converter { static mrb_value to_mrb(mrb_state *mrb, const Point &var) { mrb_value obj = mrb_obj_value( - mrb_data_object_alloc(mrb, mrb->object_class, new Point(var), - &mrubypp_class_builder::data_type)); + mrb_data_object_alloc(mrb, mrb->object_class, new Point(var), + &mrubypp_class_builder::data_type)); return obj; } @@ -112,4 +120,4 @@ TEST_CASE("Point", "[class]") { auto result = engine.call("test_class_method"); REQUIRE(result == 1); } -} \ No newline at end of file +} diff --git a/test/test_engine.cpp b/test/test_engine.cpp index 6d71bd0..ca71390 100644 --- a/test/test_engine.cpp +++ b/test/test_engine.cpp @@ -47,4 +47,4 @@ TEST_CASE("call benchmark", "[!benchmark]") { auto b = engine.call("get_same", 1); REQUIRE(b == 1); }; -} \ No newline at end of file +}