I am writing a library that uses NativeCall
, it would be very convenient for me to be able to return a Raku Hash
from an exported function. How can I do this?
For example, in Ruby, if I wanted to return a Hash
from C, I would do something like:
#include "ruby.h"
VALUE make_hash() {
VALUE hash = rb_hash_new();
return hash;
}
I am interested to see if this can be done, I was thinking that maybe I would need to use a MoarVM header or something. But I'm not sure.
What I'm trying to do is write a C function that takes in a String does some stuff, then returns a Raku hash.
I have done roughly this for Rust over here (this is a collection of some Raku-Rust Nativecall code examples, not a module)...
First the raku:
## Rust FFI Omnibus: Objects
## http:##jakegoulding.com/rust-ffi-omnibus/objects/
class ZipCodeDatabase is repr('CPointer') {
sub zip_code_database_new() returns ZipCodeDatabase is native($n-path) { * }
sub zip_code_database_free(ZipCodeDatabase) is native($n-path) { * }
sub zip_code_database_populate(ZipCodeDatabase) is native($n-path) { * }
sub zip_code_database_population_of(ZipCodeDatabase, Str is encoded('utf8'))
returns uint32 is native($n-path) { * }
method new {
zip_code_database_new
}
submethod DESTROY { # Free data when the object is garbage collected.
zip_code_database_free(self);
}
method populate {
zip_code_database_populate(self)
}
method population_of( Str \zip ) {
zip_code_database_population_of(self, zip);
}
}
my \database = ZipCodeDatabase.new;
database.populate;
my \pop1 = database.population_of('90210');
my \pop2 = database.population_of('20500');
say pop1 - pop2;
Then the Rust:
// Rust FFI Omnibus: Objects
// http://jakegoulding.com/rust-ffi-omnibus/objects/
pub struct ZipCodeDatabase {
population: HashMap<String, u32>,
}
impl ZipCodeDatabase {
fn new() -> ZipCodeDatabase {
ZipCodeDatabase {
population: HashMap::new(),
}
}
fn populate(&mut self) {
for i in 0..100_000 {
let zip = format!("{:05}", i);
self.population.insert(zip, i);
}
}
fn population_of(&self, zip: &str) -> u32 {
self.population.get(zip).cloned().unwrap_or(0)
}
}
#[no_mangle]
pub extern "C" fn zip_code_database_new() -> *mut ZipCodeDatabase {
Box::into_raw(Box::new(ZipCodeDatabase::new()))
}
#[no_mangle]
pub extern "C" fn zip_code_database_free(ptr: *mut ZipCodeDatabase) {
if ptr.is_null() {
return;
}
unsafe {
Box::from_raw(ptr);
}
}
#[no_mangle]
pub extern "C" fn zip_code_database_populate(ptr: *mut ZipCodeDatabase) {
let database = unsafe {
assert!(!ptr.is_null());
&mut *ptr
};
database.populate();
}
#[no_mangle]
pub extern "C" fn zip_code_database_population_of(
ptr: *const ZipCodeDatabase,
zip: *const c_char,
) -> u32 {
let database = unsafe {
assert!(!ptr.is_null());
&*ptr
};
let zip = unsafe {
assert!(!zip.is_null());
CStr::from_ptr(zip)
};
let zip_str = zip.to_str().unwrap();
database.population_of(zip_str)
}
Obviously the C side of affairs will need to be quite different, but hopefully this gives enough clues.