简体   繁体   中英

Convert C character into Ruby string

I am writing a sample C Ruby extension which needs a C character to Ruby string conversion.

I have created extconf.rb and a file called x.c.

extconf.rb:

require 'mkmf'
create_makefile 'x'

x.c:

#include "ruby.h"

void Init_x() {
    char c = 'b' ;

    VALUE b = rb_str_new_cstr(c) ;
    rb_gv_set("$a", b) ;
    rb_global_variable(&b) ;
}

rb_str_new_cstr() expects a C string, not a character. When I compile and run this code, I get segmentation fault, and Ruby crashes.

I can do this instead, which works just fine:

#include "ruby.h"

void Init_x() {
    char *c = "b" ;

    VALUE b = rb_str_new_cstr(c) ;
    rb_gv_set("$a", b) ;
    rb_global_variable(&b) ;
}

But the problem is if I have something like fscanf(file, "%c", &char) which sets char to a character, I have to convert it to a string first and change %c to %1s , which sounds like a bit slower approach.

Is there a way to directly convert a C character into a Ruby string?

The rb_str_new_cstr() function is only for null-terminated strings - so called C strings (thus the suffix). Besides various other variations the unsuffixed rb_str_new() function exists for use cases like yours in which you have characters and you know how many:

#include "ruby.h"

void Init_x() {
    char c = 'b' ;

    VALUE b = rb_str_new(&c, 1); // or sizeof(c)
    rb_gv_set("$a", b) ;
}

Btw. you don't need to call rb_global_variable() here. It has nothing to do with Ruby global variables. It is used to manually tell the garbage collector about Ruby objects not exposed to Ruby. The garbage collector obviously knows about the created object since the string is accessible via $a in Ruby code.

I wouldn't worry much about the performance of reading 1 character, but char *c = "b"; is a string literal and it is not mutable.
There are many detailed answers if you follow the duplicates.

However, to read 1 byte you don't need fscanf() , because fgetc() does exactly that.

char c[2] = "b";    // 1 character + NULL

c[0] = fgetc(file); // returns the character read

And it's already C string that you can pass, without & because arrays decay to pointers to their first element

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM