better call template & move to include/murbypp dir & fix test namepsace
Some checks failed
ubuntu / Explore-Gitea-Actions (push) Failing after 1m47s
Some checks failed
ubuntu / Explore-Gitea-Actions (push) Failing after 1m47s
This commit is contained in:
29
include/mrubypp/arena_guard.h
Normal file
29
include/mrubypp/arena_guard.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Created by ZekeXiao on 2025/10/17.
|
||||
//
|
||||
|
||||
#ifndef MRUBYPP_MRUBYPP_UTILS_H
|
||||
#define MRUBYPP_MRUBYPP_UTILS_H
|
||||
|
||||
#include <mruby.h>
|
||||
|
||||
namespace mrubypp {
|
||||
// gc arena
|
||||
class arena_guard {
|
||||
public:
|
||||
explicit arena_guard(mrb_state *mrb) : mrb(mrb) {
|
||||
ai = mrb_gc_arena_save(mrb);
|
||||
}
|
||||
arena_guard(arena_guard &&other) = delete;
|
||||
arena_guard(const arena_guard &other) = delete;
|
||||
~arena_guard() { mrb_gc_arena_restore(mrb, ai); }
|
||||
|
||||
// arena_idx
|
||||
[[nodiscard]] int get_ai() const { return ai; }
|
||||
|
||||
private:
|
||||
mrb_state *mrb;
|
||||
int ai;
|
||||
};
|
||||
} // namespace mrubypp
|
||||
#endif // MRUBYPP_MRUBYPP_UTILS_H
|
||||
266
include/mrubypp/bind_class.h
Normal file
266
include/mrubypp/bind_class.h
Normal file
@@ -0,0 +1,266 @@
|
||||
#ifndef MRUBYPP_BIND_CLASS
|
||||
#define MRUBYPP_BIND_CLASS
|
||||
|
||||
#include "arena_guard.h"
|
||||
#include "converters.h"
|
||||
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/data.h>
|
||||
#include <mruby/proc.h>
|
||||
|
||||
namespace mrubypp {
|
||||
template <typename... Args, size_t... Is>
|
||||
std::tuple<Args...> get_args_helper_impl(mrb_state *mrb, const mrb_value *argv,
|
||||
std::index_sequence<Is...>) {
|
||||
return std::make_tuple(
|
||||
converter<typename std::decay<Args>::type>::from_mrb(mrb, argv[Is])...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::tuple<typename std::decay<Args>::type...> get_args_helper(mrb_state *mrb) {
|
||||
arena_guard guard(mrb);
|
||||
auto argc = mrb_get_argc(mrb);
|
||||
auto argv = mrb_get_argv(mrb);
|
||||
|
||||
if (argc != sizeof...(Args)) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "argument count mismatch");
|
||||
}
|
||||
|
||||
return get_args_helper_impl<typename std::decay<Args>::type...>(
|
||||
mrb, argv, std::index_sequence_for<Args...>{});
|
||||
}
|
||||
|
||||
template <typename T, typename Ret, typename... MethodArgs>
|
||||
struct 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 = get_args_helper<MethodArgs...>(mrb);
|
||||
T *instance = static_cast<T *>(DATA_PTR(self));
|
||||
if (!instance) {
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "invalid instance");
|
||||
}
|
||||
|
||||
void *method_ptr = mrb_cptr(mrb_cfunc_env_get(mrb, 0));
|
||||
auto method = ptr_to_method(method_ptr);
|
||||
|
||||
if constexpr (std::is_same_v<Ret, void>) {
|
||||
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));
|
||||
return converter<Ret>::to_mrb(mrb, result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Ret, typename... MethodArgs>
|
||||
struct 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 = get_args_helper<MethodArgs...>(mrb);
|
||||
T *instance = static_cast<T *>(DATA_PTR(self));
|
||||
if (!instance) {
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "invalid instance");
|
||||
}
|
||||
|
||||
void *method_ptr = mrb_cptr(mrb_cfunc_env_get(mrb, 0));
|
||||
auto method = ptr_to_method(method_ptr);
|
||||
if constexpr (std::is_same_v<Ret, void>) {
|
||||
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));
|
||||
return converter<Ret>::to_mrb(mrb, result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Ret, typename... FuncArgs>
|
||||
struct function_wrapper {
|
||||
using FunctionType = Ret (*)(FuncArgs...);
|
||||
|
||||
static mrb_value wrapper(mrb_state *mrb, mrb_value self) {
|
||||
auto func = (FunctionType)mrb_cptr(mrb_cfunc_env_get(mrb, 0));
|
||||
auto args = get_args_helper<FuncArgs...>(mrb);
|
||||
if constexpr (std::is_same_v<Ret, void>) {
|
||||
std::apply(func, args);
|
||||
return mrb_nil_value();
|
||||
} else {
|
||||
Ret result = std::apply(func, args);
|
||||
return converter<Ret>::to_mrb(mrb, result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> class bind_class;
|
||||
|
||||
template <typename T, typename... Args> struct constructor_wrapper {
|
||||
static mrb_value wrapper(mrb_state *mrb, mrb_value self) {
|
||||
auto args = get_args_helper<Args...>(mrb);
|
||||
T *instance = std::apply([](Args... args) { return new T(args...); }, args);
|
||||
|
||||
DATA_PTR(self) = instance;
|
||||
DATA_TYPE(self) = &bind_class<T>::data_type;
|
||||
return self;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> class bind_class {
|
||||
public:
|
||||
static const struct mrb_data_type data_type;
|
||||
|
||||
bind_class(mrb_state *mrb, std::string 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());
|
||||
}
|
||||
|
||||
template <typename... Args> bind_class &def_constructor() {
|
||||
mrb_define_method(mrb_, rclass_, "initialize",
|
||||
&constructor_wrapper<T, Args...>::wrapper,
|
||||
MRB_ARGS_REQ(sizeof...(Args)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Ret, typename... MethodArgs>
|
||||
bind_class &def_method(const std::string &name,
|
||||
Ret (T::*method)(MethodArgs...)) {
|
||||
mrb_sym method_sym = mrb_intern_cstr(mrb_, name.c_str());
|
||||
|
||||
using wrap = method_wrapper<T, Ret, MethodArgs...>;
|
||||
auto ptr = wrap::method_to_ptr(method);
|
||||
|
||||
mrb_value env[] = {
|
||||
mrb_cptr_value(mrb_, ptr),
|
||||
};
|
||||
struct RProc *p = mrb_proc_new_cfunc_with_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);
|
||||
// MRB_ARGS_REQ(sizeof...(MethodArgs))
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Ret, typename... MethodArgs>
|
||||
bind_class &def_method(const std::string &name,
|
||||
Ret (T::*method)(MethodArgs...) const) {
|
||||
mrb_sym method_sym = mrb_intern_cstr(mrb_, name.c_str());
|
||||
using wrap = const_method_wrapper<T, Ret, MethodArgs...>;
|
||||
|
||||
auto ptr = wrap::method_to_ptr(method);
|
||||
auto obj = mrb_cptr_value(mrb_, ptr);
|
||||
mrb_gc_protect(mrb_, obj);
|
||||
mrb_value env[] = {obj};
|
||||
|
||||
struct RProc *p = mrb_proc_new_cfunc_with_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 <typename Ret, typename... FuncArgs>
|
||||
bind_class &def_class_method(const std::string &name,
|
||||
Ret (*func)(FuncArgs...)) {
|
||||
using wrap = function_wrapper<T, Ret, FuncArgs...>;
|
||||
mrb_value env[] = {
|
||||
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_, &wrap::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;
|
||||
}
|
||||
|
||||
// get set
|
||||
template <typename Ret>
|
||||
bind_class &def_property(const std::string &name, Ret (T::*getter)() const,
|
||||
void (T::*setter)(Ret)) {
|
||||
def_method(name, getter);
|
||||
def_method(name + "=", setter);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bind_class &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<T *>(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<T *>(ptr);
|
||||
delete instance;
|
||||
}
|
||||
}
|
||||
|
||||
mrb_state *mrb_;
|
||||
struct RClass *rclass_;
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
template <typename T> const struct mrb_data_type bind_class<T>::data_type = {
|
||||
typeid(T).name(), bind_class<T>::free_instance
|
||||
};
|
||||
} // namespace mrubypp
|
||||
|
||||
#endif
|
||||
105
include/mrubypp/converters.h
Normal file
105
include/mrubypp/converters.h
Normal file
@@ -0,0 +1,105 @@
|
||||
#ifndef MRUBYPP_STD_CONVERTERS_H
|
||||
#define MRUBYPP_STD_CONVERTERS_H
|
||||
|
||||
#include "arena_guard.h"
|
||||
|
||||
#include <mruby.h>
|
||||
#include <mruby/array.h>
|
||||
#include <mruby/string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mrubypp {
|
||||
|
||||
template <typename T> struct converter {
|
||||
static mrb_value to_mrb(mrb_state *mrb, const T &var) {
|
||||
static_assert(sizeof(T) == 0, "Specialization required");
|
||||
return mrb_nil_value();
|
||||
}
|
||||
static T from_mrb(mrb_state *mrb, mrb_value value) {
|
||||
static_assert(sizeof(T) == 0, "Specialization required");
|
||||
return T();
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct converter<int> {
|
||||
static mrb_value to_mrb(mrb_state *mrb, int var) {
|
||||
return mrb_fixnum_value(var);
|
||||
}
|
||||
static int from_mrb(mrb_state *mrb, mrb_value value) {
|
||||
return mrb_fixnum(value);
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct converter<unsigned int> {
|
||||
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 converter<float> {
|
||||
static mrb_value to_mrb(mrb_state *mrb, float var) {
|
||||
return mrb_float_value(mrb, var);
|
||||
}
|
||||
static double from_mrb(mrb_state *mrb, mrb_value value) {
|
||||
return mrb_float(value);
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct converter<double> {
|
||||
static mrb_value to_mrb(mrb_state *mrb, double var) {
|
||||
return mrb_float_value(mrb, var);
|
||||
}
|
||||
static double from_mrb(mrb_state *mrb, mrb_value value) {
|
||||
return mrb_float(value);
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct converter<std::string> {
|
||||
static mrb_value to_mrb(mrb_state *mrb, const std::string &var) {
|
||||
return mrb_str_new(mrb, var.data(), (mrb_int)var.size());
|
||||
}
|
||||
|
||||
static std::string from_mrb(mrb_state *mrb, mrb_value value) {
|
||||
if (mrb_string_p(value)) {
|
||||
return std::string(RSTRING_PTR(value), RSTRING_LEN(value));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct converter<const char *> {
|
||||
static mrb_value to_mrb(mrb_state *mrb, const char *var) {
|
||||
return mrb_str_new(mrb, var, (mrb_int)strlen(var));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct converter<std::vector<T>> {
|
||||
static mrb_value to_mrb(mrb_state *mrb, const std::vector<T> &var) {
|
||||
mrb_value ary = mrb_ary_new_capa(mrb, static_cast<mrb_int>(var.size()));
|
||||
for (const T &el : var) {
|
||||
arena_guard guard(mrb);
|
||||
mrb_ary_push(mrb, ary, converter<T>::to_mrb(mrb, el));
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
|
||||
static std::vector<T> from_mrb(mrb_state *mrb, mrb_value value) {
|
||||
arena_guard guard(mrb);
|
||||
std::vector<T> result;
|
||||
if (mrb_array_p(value)) {
|
||||
int len = RARRAY_LEN(value);
|
||||
for (int i = 0; i < len; ++i) {
|
||||
result.append(converter<T>::from_mrb(mrb, mrb_ary_ref(mrb, value, i)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mrubypp
|
||||
|
||||
#endif // MRUBYPP_STD_CONVERTERS_H
|
||||
60
include/mrubypp/engine.h
Normal file
60
include/mrubypp/engine.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#ifndef MRUBYPP_H
|
||||
#define MRUBYPP_H
|
||||
|
||||
#include "arena_guard.h"
|
||||
#include "converters.h"
|
||||
|
||||
#include <functional>
|
||||
#include <mruby.h>
|
||||
#include <string>
|
||||
|
||||
#include <mruby/compile.h>
|
||||
|
||||
namespace mrubypp {
|
||||
class engine {
|
||||
public:
|
||||
engine() { mrb_ = mrb_open(); }
|
||||
~engine() { mrb_close(mrb_); }
|
||||
void load(const std::string &str) {
|
||||
if (!mrb_)
|
||||
return;
|
||||
mrb_load_string(mrb_, str.c_str());
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
typename std::enable_if<!std::is_same<T, void>::value, T>::type
|
||||
call(const std::string &funcName, Args... args) {
|
||||
arena_guard guard(mrb_);
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
mrb_value argv[] = {converter<Args>::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 converter<T>::from_mrb(mrb_, result);
|
||||
}
|
||||
mrb_value result =
|
||||
mrb_funcall(mrb_, mrb_top_self(mrb_), funcName.data(), 0);
|
||||
return converter<T>::from_mrb(mrb_, result);
|
||||
}
|
||||
|
||||
template <typename T = void, typename... Args>
|
||||
typename std::enable_if<std::is_same<T, void>::value, T>::type
|
||||
call(const std::string &funcName, Args... args) {
|
||||
if constexpr (sizeof...(Args) > 0) {
|
||||
arena_guard guard(mrb_);
|
||||
mrb_value argv[] = {converter<Args>::to_mrb(mrb_, args)...};
|
||||
mrb_sym sym = mrb_intern_cstr(mrb_, funcName.data());
|
||||
mrb_funcall_argv(mrb_, mrb_top_self(mrb_), sym, sizeof...(Args), argv);
|
||||
} else {
|
||||
mrb_funcall(mrb_, mrb_top_self(mrb_), funcName.data(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] mrb_state *get_mrb() const { return mrb_; }
|
||||
|
||||
private:
|
||||
mrb_state *mrb_;
|
||||
};
|
||||
} // namespace mrubypp
|
||||
|
||||
#endif // MRUBYPP_H
|
||||
Reference in New Issue
Block a user