10
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JuliaからRustの関数を呼び出す

Last updated at Posted at 2019-01-31

これのC++をRustに置き換えた版です。
https://github.com/termoshtt/julia.rs

追記 (2022/11/24)

以下の記述はFFIがどのような仕組みで動いているかを理解するには有用ですが、実用上は jlrs 等のメンテナンスされているcrateを使うことをおすすめします。

julia-sys crate

まず/usr/include/julia/julia.hからbindgenを使ってRust bindingを生成します

bindgen \
  --whitelist-function "jl_.*" \
  --whitelist-type "jl_.*"     \
  --whitelist-var "jl_.*"      \
  /usr/include/julia/julia.h   \
> src/julia.rs

bindgenはclangを使って#includeしているヘッダーのバインディングまで作ってしまうので、ホワイトリストで生成する型や関数名を指定します。これをinclude!で取り込みます

#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]
include!("julia.rs");

bindgenbuild.rsで使って動的に生成することも出来ますが今回は静的に作ることにしました。一部自動生成されたコードを手直ししているのでこの方が都合がいいことが多いです。

bindgenはCのマクロ部分やstatic inlineなコードを生成出来ないので、この部分は手で変換します(´・ω・`)

pub unsafe fn jl_astaggedvalue(ptr: *mut jl_value_t) -> *mut jl_taggedvalue_t {
    let p = ptr as *mut jl_taggedvalue_t;
    p.offset(-1)
}

pub unsafe fn jl_typeof(ptr: *mut jl_value_t) -> *mut jl_datatype_t {
    let tagged = jl_astaggedvalue(ptr);
    let t = (*tagged).header & !15_usize;
    t as *mut jl_datatype_t
}

pub unsafe fn jl_is_array(ptr: *mut jl_value_t) -> bool {
    let ty = jl_typeof(ptr);
    (*ty).name == jl_array_typename
}

とりあえず必要そうな最小限だけ実装しました。他のAPIもそのうち追加するかもしれません(予定は未定)

example crate

Rustで共有ライブラリを作るにはcdylib形式にします

Cargo.toml
[lib]
crate-type = ["cdylib"]

[dependencies.julia-sys]
path = "../julia-sys"

そして前回のmake_twice_arrayをRustで実装したのがこちら

use julia_sys::*;

#[no_mangle]
pub extern "C" fn make_twice_array(ptr: *mut jl_value_t) {
    if !unsafe { jl_is_array(ptr) } {
        panic!("This is not an array")
    }
    let array = ptr as *mut jl_array_t;
    let data = unsafe {
        let n = (*array).length;
        let data = (*array).data as *mut f32;
        std::slice::from_raw_parts_mut(data, n)
    };

    for v in data {
        *v *= 2.0;
    }
}

Juliaの方は共有ライブラリの名前だけ直します

b = ones(Float32, 2, 3)
ccall((:make_twice_array, "libexample"), Cvoid, (Any,), b)
println(b)

これでJuliaから呼び出せます

cargo build
LD_LIBRARY_PATH=$PWD/target/debug julia example.jl

やったぜ(/・ω・)/

課題

  • エラー処理
  • 配布方法
  • Rust側のSafe wrapperの整備
10
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?