Skip to content

Instantly share code, notes, and snippets.

@nemerle
Last active December 15, 2015 11:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nemerle/5255024 to your computer and use it in GitHub Desktop.
Save nemerle/5255024 to your computer and use it in GitHub Desktop.
C++11 MRuby template based ruby->c++ call mapper and structure bind class;
/*
The CallMapper::extract_args_and_call works like this:
It builds a tuple whose members are based on types of arguments of the passed in 'func'
After that, for each tuple member, it calls appropriate extract_arg function to fill that particular tuple member.
When all members are extracted, it calls the func with 'unpacked' tuple.
*/
struct CallMapper {
/*
Ruby arg format chars
o - Object
S - String
A - Array *
H - Hash
s - string 'char *,int' *
z - 0 terminated string 'char *'
a - array [mrb_value *,mrb_int]
f - float
i - integer *
b - binary ?? boolean ?
n - symbol
& - block
* - rest [mrb_value *,int]
Arg modifiers:
| - optional
*/
template<typename T>
static std::function< typename std::enable_if<std::is_function<T>::value, T>::type >
make_function(T *t) {
return { t };
}
template<typename R,class SELF,typename... Args>
static void extract_args_and_call_method(mrb_state * mrb,SELF *self,R (SELF::*func)(Args...))
{
std::tuple<Args...> values;
typedef typename make_indices<Args...>::type Indices;
extract_args_tuple(mrb,Indices(),values);
return forward_impl_method(Indices(), func,self,values);
}
template<typename R,typename... Args>
static R extract_args_and_call(mrb_state * mrb,const std::function<R(Args...)> &func)
{
std::tuple<Args...> values;
typedef typename make_indices<Args...>::type Indices;
extract_args_tuple(mrb,Indices(),values);
return forward_impl_call(Indices(), func,values);
}
protected:
template<size_t...> struct index_tuple{};
template<size_t I, typename IndexTuple, typename... Types>
struct make_indices_impl;
template<size_t I, size_t... Indices, typename T, typename... Types>
struct make_indices_impl<I, index_tuple<Indices...>, T, Types...>
{
typedef typename
make_indices_impl<I + 1,
index_tuple<Indices..., I>,
Types...>::type type;
};
template<size_t I, size_t... Indices>
struct make_indices_impl<I, index_tuple<Indices...> >
{
typedef index_tuple<Indices...> type;
};
template<typename... Types>
struct make_indices
: make_indices_impl<0, index_tuple<>, Types...>
{};
// this function's role is to extract the parameters from ruby given list of typed references
template <size_t... Indices, class... Args, class SELF,class Ret>
static Ret forward_impl_method(index_tuple<Indices...>,
Ret (SELF::*func)(Args...),
SELF *self,
std::tuple<Args...> tuple
)
{
return (self->*func)(std::get<Indices>(tuple)...);
}
template <size_t... Indices, class... Args, class Ret>
static Ret forward_impl_call(index_tuple<Indices...>,
const std::function<Ret(Args...)> &func,
std::tuple<Args...> tuple
)
{
return func(std::get<Indices>(tuple)...);
}
template<size_t ...indices, typename... Args>
static void extract_args_tuple(mrb_state * mrb, index_tuple<indices...>,std::tuple<Args... > &args)
{
extract_args(mrb,std::get<indices>(args)...);
}
static void extract_args(mrb_state * mrb)
{
}
template<typename T, typename... Args>
static void extract_args(mrb_state * mrb,T &value, Args... args)
{
extract_arg(mrb,value);
extract_args(mrb,args...);
}
static void extract_arg(mrb_state * mrb,char *& tgt)
{
// get the c++ version of the args out of the arguments
mrb_value arg0;
mrb_get_args(mrb, "S", &arg0);
// call the C++ method with the correct arguments
tgt=mrb_str_ptr(arg0)->ptr;
}
static void extract_arg(mrb_state * mrb,const char *& tgt)
{
// get the c++ version of the args out of the arguments
mrb_value arg0;
mrb_get_args(mrb, "S", &arg0);
// call the C++ method with the correct arguments
tgt=mrb_str_ptr(arg0)->ptr;
}
static void extract_arg(mrb_state * mrb,const uint8_t *& tgt)
{
// get the c++ version of the args out of the arguments
mrb_value arg0;
mrb_get_args(mrb, "S", &arg0);
// call the C++ method with the correct arguments
tgt=(uint8_t *)mrb_str_ptr(arg0)->ptr;
}
static void extract_arg(mrb_state * mrb,std::string& tgt)
{
// get the c++ version of the args out of the arguments
char* arg0;
int len;
mrb_get_args(mrb, "s", &arg0,&len);
tgt = std::string(arg0,len);
}
static void extract_arg(mrb_state * mrb,int & tgt)
{
mrb_get_args(mrb, "i", &tgt);
}
static void extract_arg(mrb_state * mrb,size_t & tgt)
{
int value;
mrb_get_args(mrb, "i", &value);
tgt=value;
}
static void extract_arg(mrb_state * mrb,void *& tgt)
{
mrb_value value;
mrb_get_args(mrb, "o", &value);
tgt=mrb_voidp(value);
}
static void extract_arg(mrb_state * mrb,const void *& tgt)
{
mrb_value value;
mrb_get_args(mrb, "o", &value);
tgt=mrb_voidp(value);
}
};
template<class TYPE>
struct TypeBinding {
TYPE * m_p = nullptr;
static mrb_value initialize(mrb_state * mrb, mrb_value self) {
/* search the incomming value for a new binding struct */
auto p = (TypeBinding<TYPE> *)mrb_get_datatype(mrb, self, &binding);
/* if it already has one, get rid of it */
if (p)
free_wrap(mrb, p);
/* grab a new struct from the mrb managed memory pool */
p = (TypeBinding<TYPE> *)mrb_malloc(mrb, sizeof(TypeBinding<TYPE>));
/* new up our c++ class on this struct */
p->m_p = wrapped_constructor(mrb,self);
/* set the outgoing value with the correct data pointers */
DATA_PTR(self) = p;
DATA_TYPE(self) = &binding;
return self;
}
static void free_wrap(mrb_state * mrb, void * ptr) {
auto p = (TypeBinding<TYPE> *)ptr;
if (p && p->m_p){
delete p->m_p;
p->m_p = nullptr;
}
mrb_free(mrb, ptr);
}
static TYPE *fromRuby(mrb_state * mrb, mrb_value self) {
TypeBinding<TYPE> * p = (TypeBinding<TYPE> *)mrb_get_datatype(mrb, self, &binding);
if( p && p->m_p)
return p->m_p;
return nullptr;
}
static mrb_value toRuby(mrb_state * mrb, TYPE *self) {
//TODO: this should find already mapped TYPE values and return exisitng wrappers
return mrb_nil_value();
}
static TYPE *wrapped_constructor(mrb_state * mrb, mrb_value self) {
//ArgMapper::extract_args_and_call_method
}
// A struct that configures the cleanup method on our data
static constexpr mrb_data_type binding = { TYPE::name, free_wrap };
};
template<class T>
constexpr mrb_data_type TypeBinding<T>::binding;
@nemerle
Copy link
Author

nemerle commented Mar 27, 2013

This code is not meant as a generic purpose c++ <-> mruby bridge. It's main goal is to ease the wrapping of c++ objects and using them from embedded mruby interpreters.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment