One question though: here you are calling mrb_data_init on the mrb_value self in the Foo constructor (Foo_initialize), so I suppose this mrb_value is already setup to receive a data pointer. How do you initialize an mrb_value with custom C data outside of a constructor, e.g. from within a normal function (doing the equivalent of a Foo.new inside a C function)?
but the mrb_data_init is producing a segfault. I suppose there is something to do with mrb_data_object_alloc first, but this function returns an RData*, not an mrb_value, and I haven't found any function to convert an RData* into an mrb_value.
I think you're running into confusion on what an mrb_value is, which is understandable. There is little documentation about mruby and the api is inconsistent with either using the mrb_value or the raw pointers. An mrb_value type is temporary. It's a convenience wrapper for interpreting a region of allocated memory. The tt member of the mrb_value informs you, the programmer, how to proceed with the mrb_value For instance, if the tt is MRB_TT_FIXNUM then you read the value.i member directly. However, with other values you cast the value.pvoid * based upon the tt type. Another example, if the tt is MRB_TT_CLASS you'd do RClass *Foo = (RClass *)obj.value.p. The same for MRB_TT_DATA, the value.p is an RData * and should be cast as such RData *foo = (RData *)obj.value.p. (Note, strings are really weird in that they have lots of hidden optimizations underneath, don't ever work with an mruby string directly no matter what even if it looks like a normal char *)
In the few months of working with mruby I've never constructed an mrb_value myself. I let the API or convenience macros build them and do the right thing instead. Most API functions return an mrb_value pre-populated. When I need to create an mrb_value based upon C data I use a provided API function or C macro. boxing_no.h contains some C macros for making an mrb_value from C data. Otherwise the type specific header file contains C macros for creating an mrb_value for that type, like string.h
As to your question when defining an mruby method in C the calling signature is (mrb_state*, mrb_value). The mruby VM boxes up the current self object into an mrb_value and passes it along as the second parameter of the C function. In the case of the initialize method the mruby VM already created and allocated the object and passed it along as the self giving you a chance to work with it more.
Knowing that you can achieve what you're after. You cannot call mrb_data_init on an unpopulated mrb_value. It doesn't point to anything useful in your example and has the classic C problem of uninitialized garbage memory data. mrb_data_init is following the v.value.p to some random location and then crashing.
Instead, use the self value passed into every function which the VM populates properly. If you set the class type using MRB_SET_INSTANCE_TT then the selfmrb_value is a properly constructed mrb_value wrapping an RData *. I hope that I helped your understanding and you got a little farther.
One bit of note. Calling a method on an object other than new and having the object allocate memory for itself is surprising to me. If you think about it in pure Ruby land, calling methods on a constructed object will only construct other objects, which their constructors allocate memory, and optionally save the references as instance variables. I'd never expect doing something like foo = Foo.new; foo.some_operation to allocate more memory, I'd expect Foo.new to do all the allocations required for an instance of Foo. Something to consider that may simplify your designs and help your understanding.
Thanks for your answer! I also asked the question as an issue on the mruby repo and got an answer about using the C macros. My code works, now.
My use-case for allocating an object in a function that isn't a constructor was not to allocate more memory for "self", but to allocate a new object. Imagine the following code:
class Foo
...
end
class Bar
...
def do_something
...
return Foo.new
end
end
Now imagine Foo and Bar are actually not defined in Ruby but in C, and do_something is a C function, you would need to create an instance of Foo from inside a C function.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
That's a pretty useful tutorial!
One question though: here you are calling mrb_data_init on the mrb_value self in the Foo constructor (Foo_initialize), so I suppose this mrb_value is already setup to receive a data pointer. How do you initialize an mrb_value with custom C data outside of a constructor, e.g. from within a normal function (doing the equivalent of a Foo.new inside a C function)?
I have tried this:
mystruct* val = ... ; // allocation
mrb_value v;
mrb_data_init(v, (void*)val, &mydatatype);
but the mrb_data_init is producing a segfault. I suppose there is something to do with mrb_data_object_alloc first, but this function returns an RData*, not an mrb_value, and I haven't found any function to convert an RData* into an mrb_value.
I think you're running into confusion on what an
mrb_valueis, which is understandable. There is little documentation about mruby and the api is inconsistent with either using themrb_valueor the raw pointers. Anmrb_valuetype is temporary. It's a convenience wrapper for interpreting a region of allocated memory. Thettmember of themrb_valueinforms you, the programmer, how to proceed with themrb_valueFor instance, if thettisMRB_TT_FIXNUMthen you read thevalue.imember directly. However, with other values you cast thevalue.pvoid *based upon thetttype. Another example, if thettisMRB_TT_CLASSyou'd doRClass *Foo = (RClass *)obj.value.p. The same forMRB_TT_DATA, thevalue.pis anRData *and should be cast as suchRData *foo = (RData *)obj.value.p. (Note, strings are really weird in that they have lots of hidden optimizations underneath, don't ever work with an mruby string directly no matter what even if it looks like a normalchar *)In the few months of working with mruby I've never constructed an
mrb_valuemyself. I let the API or convenience macros build them and do the right thing instead. Most API functions return anmrb_valuepre-populated. When I need to create anmrb_valuebased upon C data I use a provided API function or C macro.boxing_no.hcontains some C macros for making anmrb_valuefrom C data. Otherwise the type specific header file contains C macros for creating anmrb_valuefor that type, likestring.hAs to your question when defining an mruby method in C the calling signature is
(mrb_state*, mrb_value). The mruby VM boxes up the currentselfobject into anmrb_valueand passes it along as the second parameter of the C function. In the case of theinitializemethod the mruby VM already created and allocated the object and passed it along as theselfgiving you a chance to work with it more.Knowing that you can achieve what you're after. You cannot call
mrb_data_initon an unpopulatedmrb_value. It doesn't point to anything useful in your example and has the classic C problem of uninitialized garbage memory data.mrb_data_initis following thev.value.pto some random location and then crashing.Instead, use the
selfvalue passed into every function which the VM populates properly. If you set the class type usingMRB_SET_INSTANCE_TTthen theselfmrb_valueis a properly constructedmrb_valuewrapping anRData *. I hope that I helped your understanding and you got a little farther.One bit of note. Calling a method on an object other than
newand having the object allocate memory for itself is surprising to me. If you think about it in pure Ruby land, calling methods on a constructed object will only construct other objects, which their constructors allocate memory, and optionally save the references as instance variables. I'd never expect doing something likefoo = Foo.new; foo.some_operationto allocate more memory, I'd expectFoo.newto do all the allocations required for an instance ofFoo. Something to consider that may simplify your designs and help your understanding.Thanks for your answer! I also asked the question as an issue on the mruby repo and got an answer about using the C macros. My code works, now.
My use-case for allocating an object in a function that isn't a constructor was not to allocate more memory for "self", but to allocate a new object. Imagine the following code:
class Foo
...
end
class Bar
...
def do_something
...
return Foo.new
end
end
Now imagine Foo and Bar are actually not defined in Ruby but in C, and do_something is a C function, you would need to create an instance of Foo from inside a C function.