Receiving Data types as arugments with mruby
Tags: ruby, c
Published: 2020-06-04 22:50:20

It's funny when searching for examples on how to do something you end up getting results for posts you wrote a few days ago and not much else. I suppose that's a sign that I'm doing something bleeding edge and esoteric. Also props on dev.to for good SEO.

A quick hit for today, since apparently I not only accidentally started writing a book and accidentally started this mruby project, I also accidentally became the primary documenter for using mruby in practice. We learned how to create a Ruby class in C with mruby which stores arbitrary C data using the Data class type. What I want to do now is pass a Data instance as a parameter into another method. For example I'm pulling Win32 window management and Vulkan state into Ruby classes. Creating a VulkanState depends on a native window, in this case a Win32 window. The Ruby side is easy enough

```ruby
@native_window = W32Window.new 'Foo', width: 1024, height: 768
@vulkan_state = VulkanState.new(@native_window)
```

Getting started with creating a VulkanState class is also typical

```c++
RClass *VulkanState_C = mrb_define_class(state, "VulkanState", state->object_class);
MRB_SET_INSTANCE_TT(VulkanState_C, MRB_TT_DATA);
mrb_define_method(state, VulkanState_C, "initialize",
                VulkanState_initialize, MRB_ARGS_REQ(1));
```

It's extracting the Data type with `mrb_get_args` which had me stumped for a while as I could not figure out how to get the right data type out. The documentation for `mrb_get_args` states how to get a Data type argument

```
| `d`  | data           | void *, {mrb_data_type} const | 2nd argument will be used to check data type so it won't be modified; when `!` follows, the value may be `nil` |
```

First try is a normal `mrb_value`

```c++
mrb_value
VulkanState_initialize(mrb_state* state, mrb_value self) {
  mrb_value gui_window;
  mrb_get_args(state, "d", &gui_window, &W32Window_data_type);
```

Which creates some wild looking data pointers in `gui_window`

![](https://dev-to-uploads.s3.amazonaws.com/i/ejcq2w3my6e47x0xb22e.png)

Next attempt is try pulling the data out with `DATA_PTR` from the `mrb_value`

```c++
W32Window* window = reinterpret_cast<W32Window *>(DATA_PTR(gui_window));
```

Still wild data

![](https://dev-to-uploads.s3.amazonaws.com/i/wggidqwfr8ew48hibikl.png)

[Digging into the code](https://github.com/mruby/mruby/blob/f32c73ad1fc8a35cbd134c30098f3b1a89db0cd8/src/class.c#L863), `mrb_get_args` automatically pulls out and type casts the `void *` for us using `mrb_data_get_ptr`. Therefore pulling a Data type out doesn't require any extra work from our side.

```c++
W32Window_data* gui_window;
mrb_get_args(state, "d", &gui_window, &W32Window_data_type);
```

Now it's correct

![](https://dev-to-uploads.s3.amazonaws.com/i/5dwzfrk83uchropac8a2.png)

That's all! A complete abstract example on receiving a Data type as a method argument

```c++
struct Foo_data {
  uint32_t baz;
};

static const Foo_data_type = { "Foo", mrb_free };

mrb_value
Bar_get_foo(mrb_state* state, mrb_value* self) {
  Foo_data *foo_data;
  mrb_get_args(state, "d", &foo_data, &Foo_data_type);
  return mrb_nil_value();
}

RClass *Foo = mrb_define_class(state, "Foo", state->object_class);
MRB_SET_INSTANCE_TT(Foo, MRB_TT_DATA);
RClass *Bar = mrb_define_class(state, "Bar", state->object_class);
mrb_define_method(state, Bar, "get_foo", Bar_get_foo, MRB_ARGS_REQ(1));
```