如何从本机调用返回 Hash/Raku 对象?

Raw*_*ler 6 moarvm raku

我正在编写一个使用 的库NativeCall,如果能够Hash从导出函数返回 Raku 对我来说会非常方便。我怎样才能做到这一点?

例如,在 Ruby 中,如果我想Hash从 C 返回 a,我会这样做:

#include "ruby.h"

VALUE make_hash() {
    VALUE hash = rb_hash_new();
    return hash;
}
Run Code Online (Sandbox Code Playgroud)

我有兴趣看看这是否可以完成,我在想也许我需要使用 MoarVM 标头或其他东西。但我不确定。

我想做的是编写一个 C 函数,它接受一个 String 来做一些事情,然后返回一个 Raku 哈希。

Håk*_*and 6

如果能够从导出函数返回 Raku Hash 对我来说会非常方便

一种解决方法是让 C 函数返回一个包含键和值的结构,然后编写一个 Raku 包装器,将其转换为 Raku 哈希,如下所示:

use v6;
use NativeCall;

constant LIB  = ('./libmylib.so');

class HInfo is repr('CStruct') is export {
    has Str $.key1;
    has num64 $.value1;
    has Str $.key2;
    has num64 $.value2;
}

sub foo_(Str--> HInfo) is native(LIB) is symbol('foo') { * }
sub foo(Str $str --> Hash) {
    my HInfo $hinfo = foo_($str);
    my %h;
    %h{$hinfo.key1} = $hinfo.value1;
    %h{$hinfo.key2} = $hinfo.value2;
    return %h;
}

my %h = foo("bar");
dd %h;
Run Code Online (Sandbox Code Playgroud)

  • @raiph *“大概可以做 HInfo is Hash is repr('CStruct') { ... } 类的事情”* 我认为 `Hash` 类型不能表示为 CStruct。如果我尝试这样做,我会收到一条错误消息:*“CStruct 表示仅处理以下类型的属性:(u)int8、(u)int16、(u)int32、(u)int64、(u)long、(u)longlong、 num32、num64、(s)size_t、bool、Str 和具有表示形式的类型:CArray、CPointer、CStruct、CPPStruct 和 CUnion"* (2认同)

p6s*_*eve 5

我在这里为 Rust 做了大致的操作(这是一些 Raku-Rust Nativecall 代码示例的集合,而不是模块)...

首先是乐:

## 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;
Run Code Online (Sandbox Code Playgroud)

然后是铁锈:

// 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)
}
Run Code Online (Sandbox Code Playgroud)

显然,C 方面的事情需要完全不同,但希望这能提供足够的线索。