Home > programming > D & Ruby FFI, part 2: working with arrays and strings

D & Ruby FFI, part 2: working with arrays and strings

Firstly, string in D is nothing but immutable(char)[], that is, an array of particular type.
Secondly, the memory layout of an array is simple: size_t (for storing length) + pointer (which points to the array elements).

The immediate conclusion is that D strings are not necessarily zero-terminated. It’s not very convenient when we’re working via FFI; the bright side is, together with immutability that gives the D compiler some opportunities to optimize operations with strings.

Strings

Let’s play a bit with the struct from the previous post:

public struct Foo {
    public string hello = "hello, world!";
}

Using FFI::Struct, it can be represented in Ruby code as

class Foo < FFI::Struct
    layout :hello_length, :size_t,
           :hello,        :string

    def initialize
        @ptr = MyLibrary.foo_new
        ObjectSpace.define_finalizer @ptr, Foo.finalize(@ptr)
        super(@ptr) # init FFI::Struct with the pointer
    end
    # ... finalize stuff ...
end

Then we may access our string as Foo.new[:hello]. Luckily, it’s zero-terminated — as all string literals in D.

Let’s now do something involving slicing:

string str;
extern (C) immutable(char)* foo_hello(Foo* p) {
    str = std.array.split(p.hello)[0];
    return str.ptr;
}

If you now bind it to Ruby by means of

...
    attach_function :foo_hello, [:pointer], :string
...
    def hello
        MyLibrary.foo_hello @ptr
    end
...

you’ll see that Foo.new.hello returns “hello, world!” instead of expected “hello,”.

In order to return a zero-terminated string, use std.string.toStringz.
To do the converse (that is to convert char* into a string), use std.conv.to!string (for some reasons, std.string.toString was deprecated).

 

Arrays

Now let’s invent a method to send arrays from D.

For instance, the array of strings which we get after calling split. Firstly, we have to convert all the strings into zero-terminated ones. Then we can return a struct with array as a field. (I don’t know if it’s possible to return the array without packing it into a struct)

import std.algorithm : map;
import std.array : array;

alias immutable(char)* cstring;

struct WordsArray {
    cstring[] arr;
}

static WordsArray words;

extern (C) WordsArray foo_hello(Foo* p) {
    words.arr = array(map!(std.string.toStringz)(std.array.split(p.hello)));
    return words;
}

The Ruby code is also not very sophisticated:

class WordsArray < FFI::Struct 
    layout :length, :size_t, # dynamic array layout
           :data,   :pointer # 

    def to_a
        self[:data].get_array_of_string(0, self[:length])
    end
end

...
    attach_function :foo_hello, [:pointer], WordsArray.by_value
...

    def hello
        (MyLibrary.foo_hello @ptr).to_a
    end
...
Advertisements
Categories: programming Tags: , ,
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: