How to create Ruby Object from C Structure - c

I am trying to make a Ruby module using the C API.
I must admit, I am having trouble fully understanding the documentation online for it, but I am trying to return a Ruby Object using data from a C Structure from another class method (sorry if that doesn't make sense). Here is an example of my problem:
example.c
#include "ruby.h"
#include "extconf.h"
typedef struct example1_t
{
int x;
} example1_t;
typedef struct example2_t
{
char *name;
} example2_t;
void example1_free(example1_t *e1);
void example2_free(example2_t *e2);
static VALUE rb_example1_alloc(VALUE klass)
{
return Data_Wrap_Struct(klass, NULL, example1_free, ruby_xmalloc(sizeof(example1_t)));
}
static VALUE rb_example1_init(VALUE self, VALUE x)
{
example1_t *e1;
Check_Type(x, T_FIXNUM);
Data_Get_Struct(self, example1_t, e1);
e1->x = NUM2INT(x);
return self;
}
static VALUE rb_example1_x(VALUE self)
{
example1_t *e1;
Data_Get_Struct(self, example1_t, e1);
return INT2NUM(e1->x);
}
static VALUE rb_example2_alloc(VALUE klass)
{
return Data_Wrap_Struct(klass, NULL, example2_free, ruby_xmalloc(sizeof(example2_t)));
}
static VALUE rb_example2_init(VALUE self, VALUE s)
{
example2_t *e2;
Check_Type(s, T_STRING);
Data_Get_Struct(self, example2_t, e2);
e2->name = (char*)malloc(RSTRING_LEN(s) + 1);
memcpy(e2->name, StringValuePtr(s), RSTRING_LEN(s) + 1);
return self;
}
static VALUE rb_example2_name(VALUE self)
{
example2_t *e2;
Data_Get_Struct(self, example2_t, e2);
return rb_str_new_cstr(e2->name);
}
static VALUE rb_example2_name_len(VALUE self)
{
example1_t *len;
example2_t *e2;
Data_Get_Struct(self, example2_t, e2);
len->x = strlen(e2->name);
/*
How do I make a new Example1 Ruby Class from the "len"
structure and return it with the length of e2->name
assigned to len->x?
*/
return it?
}
void Init_example()
{
VALUE mod = rb_define_module("Example");
VALUE example1_class = rb_define_class_under(mod, "Example1", rb_cObject);
VALUE example2_class = rb_define_class_under(mod, "Example2", rb_cObject);
rb_define_alloc_func(example1_class, rb_example1_alloc);
rb_define_alloc_func(example2_class, rb_example2_alloc);
rb_define_method(example1_class, "initialize", rb_example1_init, 1);
rb_define_method(example1_class, "x", rb_example1_x, 0);
rb_define_method(example2_class, "initialize", rb_example2_init, 1);
rb_define_method(example2_class, "name", rb_example2_name, 0);
rb_define_method(example2_class, "name_len", rb_example2_name_len, 0);
}
void example1_free(example1_t *e1)
{
memset(e1, 0, sizeof(example1_t));
}
void example2_free(example2_t *e2)
{
memset(e2, 0, sizeof(example2_t));
}
As you can see in the rb_example2_name_len, I would like to create an Example1 class and return that from an Example2 method. How would I be able to do this?
Any help is much appreciated.

You can use rb_class_new_instance to create new objects.
You will need the VALUE representing the class object. One way to get this would be to store it in a global or static variable and then initialize it in the init function rather than just having a local variable:
static VALUE example1_class;
//...
void Init_example()
{
//...
example1_class = rb_define_class_under(mod, "Example1", rb_cObject);
//...
}
You will also need to convert the arguments to Ruby form. rb_class_new_instance takes an array of VALUES.
static VALUE rb_example2_name_len(VALUE self)
{
example2_t *e2;
Data_Get_Struct(self, example2_t, e2);
// Format arguments as array of VALUES
VALUE args[1];
args[0] = INT2NUM((int)strlen(e2->name));
// args are length of array, pointer to array and class you are
// creating an instance of. example1_class is available here because
// we made it a static variable.
VALUE e1 = rb_class_new_instance(1, args, example1_class);
return e1;
}
An alternative to making example1_class a static would be to use rb_const_get. In this case you would have to get the containing module as well:
VALUE mExample = rb_const_get(rb_cObject, rb_intern("Example"));
VALUE cExample1 = rb_const_get(mExample, rb_intern("Example1"));
VALUE e1 = rb_class_new_instance(1, args, cExample1);
rb_class_new_instance basically just calls your allocation function followed by your initializer, so you could just reproduce that code yourself inside rb_example2_name_len:
example1_t *e1_struct = ruby_xmalloc(sizeof(example1_t));
e1_struct->x = (int)strlen(e2->name);
VALUE e1 = Data_Wrap_Struct(example1_class, NULL, example1_free, e1_struct);
return e1;
This would avoid needing to convert the data to ruby format just for it to be converted back straight away, but I don’t think you would gain much and using rb_class_new_instance is probably clearer.
You should also be aware that the Data_Wrap_Struct struct macros have been deprecated:
The old (non-Typed) Data_XXX macro family has been deprecated. In the future version of Ruby, it is possible old macros will not work.
I’ve used them here for simplicity but you might want to loo into using the newer TypedData_ macros.

Related

how to detect what struct a function is being called from

The title is confusing, i tried my best to explain it in a few words but i failed. Here is a better explenation of my problem.
Lets say there's a struct named Object with a bool variable named _active and a function named SetActive().
typedef struct Object
{
bool _active;
void (*SetActive)(bool)
} Object;
Object someObject;
Object someOtherObject;
void SetActive(bool set)
{
/*
if function is being called from someObject, then
someObject._active = set
if function is being called from someOtherObject, then
someOtherObject._active = set
*/
}
(This is an example)
I want SetActive() to set _active of the struct its being called from to set
For example when i call structname.SetActive(true), structname._active = true
How do i do something like this?
void (*SetActive)(bool); is a pointer to a free function. It has no association with any particular object.
In C it's pretty common to supply the object as the first or last argument to the functions acting as member functions. This is needed because C doesn't have actual member functions. To make the association clear to other programmers reading the code, you can prepend all acting "member functions" with the name of the type each function acts upon.
It could look like this:
#include <stdbool.h>
#include <stdlib.h>
typedef struct Object Object;
struct Object {
bool _active;
};
Object *Object_create() {
Object *obj = malloc(sizeof *obj);
if(obj) {
// provide some default init values
*obj = (Object){ ._active = false };
}
return obj;
}
void Object_destroy(Object *obj) {
free(obj);
}
void Object_SetActive(Object *obj, bool set) {
obj->_active = set;
}
int main(void) {
Object *obj = Object_create();
Object_SetActive(obj, true);
Object_destroy(obj);
}
If you really really want to have a poor man OOP, you can do it. But why not switching to a more friendly language?
Basically you would include a function pointer in a struct, iif you plan to override that function in a subclass. This is needed only for polymorphism. In that case you will probably need also a polymorphic destructor for your class.
The problem is that you get pointers to these polymorphic functions in every instance of your objects, so better alternatives are required (vtables, pointer to class CPython style, ...).
The bad news is that now you need to specify the object to access the function pointer and to pass it to the function itself. Which really requires some syntax sugar.
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
typedef struct Object Object;
struct Object {
bool active_;
void (*SetActive)(Object *this, bool status); // This is a virtual method
void (*Destruct)(Object *this); // You will need this for polymorphism
};
void Object_SetActive(Object *this, bool status);
void Object_Destructor(Object *this);
void Object_Constructor(Object *this) { // Default constructor
this->active_ = false;
this->SetActive = Object_SetActive;
this->Destruct = Object_Destructor;
}
void Object_Destructor(Object *this) {
// Nothing to be done here, but maybe when subclassing?
}
void Object_SetActive(Object *this, bool status) {
this->active_ = status;
}
int main(void)
{
Object someObject;
Object_Constructor(&someObject);
Object someOtherObject;
Object_Constructor(&someOtherObject);
someObject.SetActive(&someObject, true);
someOtherObject.SetActive(&someOtherObject, false);
printf("someObject.active_ = %s\n", someObject.active_ ? "true" : "false");
printf("someOtherObject.active_ = %s\n", someOtherObject.active_ ? "true" : "false");
someObject.Destruct(&someObject);
someOtherObject.Destruct(&someOtherObject);
return 0;
}
If you want to waste some time with OOP in C, with macro abuse, you can read this post.
Warning: I don't want to be held responsible for nausea or vomiting caused by reading that post.
With respect to Ted Lyngmo's answer, I'm constructing and destructing already allocated objects. This would need also some new and delete clones.

Why does my pointer doesn't want to be dereferenced?

Actually I am registering Callback to lower layer of code from upper layer.
So In a c "config file" file I have this :
typedef enum eActivationType
{
TYPE_A = 0,
TYPE_B,
TYPE_C,
}ActivationType_t;
typedef struct Callbacks_s
{
void (*OnJoin)(ActivationType_t Mode);
}NemeusCallbacks_t;
static Callbacks_t Callbacks = {
.OnJoin = OnJoin,
.//I have other callbacks here but I the first one works the other will work
};
static Params_t Params = {
.DefaultActivationType = TYPE_A,
//other params
}
Callbacks_t* GetCallbacks(void)
{
return &Callbacks;
}
Params_t* GetParams(void)
{
return &Params;
}
From the main file I call
typedef void (*OnJoin)(ActivationType_t Mode);
static OnJoinRequest JoinCB;
void Nemeus_Init_ReceiveCallback(OnJoin joinCB)
{
JoinCB = joinCB;
}
And then when I want to use my register callback I can't
...
if( 0 == memcmp(&addr, temp_uint8_array, sizeof(addr)) && NULL != JoinCB)
{
JoinCB(&(*(GetParams())->DefaultActivationType));
}
...
The error I have is : invalid type argument of '->' (have 'int').
I think it deals with the adress of GetParams that I want to dereference as adress are store in int.
It is quite complicated for me to understand whats going on but as I am a begginer I want to learn.
I thought that by writing this *(GetParams()) , I can acceed the structure and then by writing this &(*(GetParams())->DefaultActivationType) I can pass the adress of the structur field to the callback

Declare a pointer to structure in const expression

I am new to C and can't yet freely navigate trough my program memory. Anyways, I am creating a static memory data type (gc_menu) that should hold a pointer to created at execution time structure (mcl_items).
For simplicity mcl_items structure have one virtual method (push) that is going to be run inside of gc_menu_add_item and also assigned to the gc_menu static space. push saves an menu item name (letter) and method to mcl_item virtual object.
mcl_items.h code:
[...]
typedef struct Items_t {
int8_t size;
char names[64];
void (*methods[64])();
// Interface
void (*push)(struct Items_t *self, char c, void (*method)());
}mcl_items;
mcl_items *new_mcl_items();
void mcl_items_push(mcl_items *self, char c, void (*method)());
mcl_items.c code:
[...]
#include "mcl_items.h"
mcl_items *new_mcl_items() {
fprintf(stderr, "MCL_Items: Generating a new set of mcl_items..");
// Build a virtual object
mcl_items *items = calloc(1, sizeof(struct Items_t));
items->push = mcl_items_push;
// Set data
items->size = 0;
return items;
}
void mcl_items_push(mcl_items *self, char c, void (*method)()) {
fprintf(stderr, "MCL_Items: pushing a new item..");
self->names[self->size] = c;
self->methods[self->size] = method;
self->size ++;
}
gc_menu.h code:
#include "items.h"
typedef struct {
// Interface
void (*add_item)(char c, void (*method)());
// Data
mcl_items *items;
}__gc_menu;
extern __gc_menu const gc_menu;
gc_menu.c code:
static void gc_menu_add_item(char c, void (*method)) {
fprintf(stderr, "GC_Menu: Passing an new item..");
fprintf(stderr, "length = %i\n", gc_menu.items->size);
gc_menu.items->push(gc_menu.items, c, method);
}
__gc_menu const gc_menu = {gc_menu_add_item, // Virtual methods
new_mcl_items}; // Data
After callng gc_menu.add_item the segmentation fault occurs and gc_menu.items->size is equal to 72, not 0 as is defined in the definition of new_mcl_items.
main.c code:
gc_menu.add_item('q', xw->end(xw));
GC_Menu: Passing an new item..length = 72
[1] 66021 segmentation fault (core dumped) ./3D_scean
So what am I doing wrong? Why is there such a weird data written to instances of my gc_menu.items?
You've initialized gc_menu.items to new_mcl_items, i.e. a pointer to the function new_mcl_items (which should give you a warning since it is of type mcl_items *(*)(void) and not mcl_items *).
It looks like what you want is to actually call the function new_mcl_items() and set gc_menu.items to the value that new_mcl_items() returns. You can't do this with an initializer; initializers of global or static objects must be known at compile or link time. Standard C doesn't have "constructors".
So you'll have to remove the const from the declaration and definition of gc_menu, and add code to main (or some function called by main, etc) to initialize gc_menu.items at run time.
gc_menu.h:
extern __gc_menu gc_menu;
gc_menu.c:
__gc_menu gc_menu = {
gc_menu_add_item,
NULL // or whatever else you like
};
main.c or whatever you have called it:
int main(void) {
// ...
gc_menu.items = new_mcl_items();
// ...
}

Swift: convert already allocated char array to char pointer

I am attempting to interface an existing C library to Swift 5.0.1 code on iOS. The C Header files have the following definitions:
char hostname[SFL_MAX_HOSTNAME_CHARS+1];
char os_release[SFL_MAX_OSRELEASE_CHARS+1];
int readHidCounters(HSP *sp, SFLHost_hid_counters *hid, char *hbuf, int hbufLen, char *rbuf, int rbufLen);
typedef struct _HSP {
[Many other elements omitted for brevity]
char hostname[SFL_MAX_HOSTNAME_CHARS+1];
char os_release[SFL_MAX_OSRELEASE_CHARS+1];
} HSP;
readHidCounters has an implementation (edited for brevity) like this:
int readHidCounters(HSP *sp, SFLHost_hid_counters *hid, char *hbuf, int hbufLen, char *rbuf, int rbufLen) {
int gotData = NO;
size_t len = hbufLen;
if(sysctlbyname("kern.hostname", hbuf, &len, NULL, 0) != 0) {
myLog(LOG_ERR, "sysctl(<kern.hostname>) failed : %s", strerror(errno));
}
else {
gotData = YES;
hid->hostname.str = hbuf;
hid->hostname.len = strlen(hbuf);
}
// UUID
memcpy(hid->uuid, sp->uuid, 16);
[...]
}
I have created an HSP struct and attempting to call readHidCounters in Swift like this
var sp = HSP()
[...]
readHidCounters(&sp,
&hidElem.counterBlock.host_hid,
&sp.hostname, // This is the error line
SFL_MAX_HOSTNAME_CHARS,
&sp.os_release,
SFL_MAX_OSRELEASE_CHARS)
I am attempting to pass in &sp.hostname results in the compiler error Cannot convert value of type '(Int8, Int8, Int8, [...], Int8)' to expected argument type 'Int8'. The problem is that hostname is a tuple of Int8's and I can't seem to get it properly converted to char *. I have tried various incarnations of UnsafeMutablePointer, withUnsafeMutablePointer and can't see how to get hostname properly recognized. Any suggestions greatly appreciated!
[Solved]
MartinR pretty much nailed it with his suggestion, but it did have a compiler error: Overlapping accesses to 'sp.hostname', but modification requires exclusive access; consider copying to a local variable. Updated code that compiles is
var myHostName = sp.hostname
var myOsRelease = sp.os_release
let _ = withUnsafeMutablePointer(to: &myHostName) {
$0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.hostname)) {
hostNamePtr in
withUnsafeMutablePointer(to: &myOsRelease) {
$0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.os_release)) {
osReleasePtr in
readHidCounters(&sp,
&hidElem.counterBlock.host_hid,
hostNamePtr, SFL_MAX_HOSTNAME_CHARS,
osReleasePtr, SFL_MAX_OSRELEASE_CHARS)
}
}
}
}
The “problem” is that C arrays are imported to Swift as tuples, and there is no simple method to treat a tuple as a Swift array, or get a pointer to the element storage (because a tuple can be inhomogeneous).
Similarly as in Converting a C char array to a String one can use the fact that Swift preserves the memory layout of structures imported from C, and with some pointer juggling and rebinding you'll get
let result = withUnsafeMutablePointer(to: &sp.hostname) {
$0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.hostname)) {
hostNamePtr in
withUnsafeMutablePointer(to: &sp.os_release) {
$0.withMemoryRebound(to: Int8.self, capacity: MemoryLayout.size(ofValue: sp.os_release)) {
osReleasePtr in
readHidCounters(&sp,
&hidElem.counterBlock.host_hid,
hostNamePtr, SFL_MAX_HOSTNAME_CHARS,
osReleasePtr, SFL_MAX_OSRELEASE_CHARS)
}
}
}
}
Another “trick” is to define C helper functions which return the array addresses as a pointers, and use the
SE-0044 Import as member
feature to make these helper functions available to Swift as computed properties. In the bridging header file you have to add
__attribute__((swift_name("getter:HSP.hostNamePtr(self:)")))
static inline char * _Nonnull hostNamePtr(HSP * _Nonnull hsp)
{
return hsp->hostname;
}
__attribute__((swift_name("getter:HSP.osReleasePtr(self:)")))
static inline char * _Nonnull osReleasePtr(HSP * _Nonnull hsp)
{
return hsp->os_release;
}
and then you can use these easily from Swift:
var sp = HSP()
let result = readHidCounters(&sp,
&hidElem.counterBlock.host_hid,
sp.hostNamePtr, SFL_MAX_HOSTNAME_CHARS,
sp.osReleasePtr, SFL_MAX_OSRELEASE_CHARS)
Try removing the & from the call, sp.hostname is already a pointer because it is an array, if it works you will have to remove the & from &sp.os_release too

PREfast annotation for structure members

In my company's code we have general get() and set() methods for interop between certain components. However, if I try to run PREfast I get inundated with warnings because PREfast doesn't realize that the get() method initializes the given parameters.
The problem is that since those methods are very general, they don't simply take a parameter (which I could mark with _Out_ or similar, but an array of structs which holds the data as to which data should be returned.
In code (greatly simplified):
typedef struct
{
int type;
int* data;
} ARGS;
void get(int count, ARGS* args)
{
for (int i = 0; i < count; i++)
*(args[i].data) = 42; // Actually handled by internal methods
}
// Sample Usage
void foo()
{
int value;
ARGS args[1];
args[0].type = 1234;
args[0].data = &value;
get(1, args);
// Do something with value
// PREfast complains that value is uninitialized (error C6001)
printf("%d", value);
}
Is there any way to annotate this so PREfast knows that args.data is initialized by get()? Or is this too complex for PREfast to handle?
EDIT: If I use get(1, &args), then the warning goes away. So there is some heuristic in PREfast which can handle this case, but I haven't found out if it is possible to trigger it externally:
void get2(int count, ARGS(* args)[1]) // Needs the size of args, or it won't compile below
{
for (int i = 0; i < count; i++)
*(*args)[i].data = 42; // Actually handled by internal methods
}
// Sample Usage
void foo2()
{
int value;
ARGS args[1];
args[0].type = 1234;
args[0].data = &value;
get2(1, &args);
// Do something with value
printf("%d", value);
}
This should fix the warning.
void foo()
{
int value=0;
...
}
Note that get() will be called in runtime only. Since, PREfast is a static analysis tool, it might report that the value is uninitialized. Nevertheless, initializing a variable before use is always a best practice in C.
Another way would be to use the PREfast suppress as below:
void foo()
{
int value;
ARGS args[1];
args[0].type = 1234;
args[0].data = &value;
get(1, args);
// Do something with value
// PREfast complains that value is uninitialized (error C6001)
#pragma prefast(suppress:C6001 , "PREfast noise: the variable value will be initialized by get method in a line above")
printf("%d", value);
}
It suppresses the warnings in the next line after the suppress statement.
Also, do add the following code in you header files(or source files) just before using the pragma prefast in your code:
#ifndef _PREFAST_
#pragma warning(disable:4068)
#endif
to avoid 4068 warning to be flagged. NOTE: pragma prefast is an extension to the PREfast AST compiler only and may not be supported by other compilers.

Resources