読者です 読者をやめる 読者になる 読者になる

rustでCで書いた関数を呼ぶ / Cからrustで書いた関数を呼ぶ

rustからCの関数を呼ぶ方法(あるいは,その逆)は公式のドキュメント (https://doc.rust-lang.org/book/ffi.html)に書いてあるのでそれを読めばいいんで すが,自分で書いたCの関数を呼ぶ方法について直接は書いてないのでここに簡単に まとめてみます.

rustでCで書いた関数を呼ぶ

以下のCで書いた関数をrustから呼ぶことを考えます.

foo.c

#include<stdio.h>

void foo(){
    printf("foo!!\n");
}

rustから以下のようにして関数の呼び出しをおこないます.

main.rs

#[link(name="foo", kind="static")]
extern{
    fn foo();
}

fn main() {
    unsafe {foo();};
}

#[link(name="foo", kind="static")] とするとリンカがlibfoo.aをリンクしてく れるようになります.また呼び出すCの関数はunsafeで囲む必要があります.

ここで,実際にプログラムをコンパイルする方法ですが,

1. rustcで直接リンクする

まずfoo.cからアーカイブを作成します.

$ gcc -o foo.c
$ ar rcs libfoo.a foo.c

それから,rustc-Lフラグでライブラリが置いてある場所を指定してあげればコ ンパイルできます(今回は全て同じディレクトリにあるとしています)

$ rustc -L. main.rs
$ ./main
foo!!

2. CargoでCのソースも一緒にビルドする

実際にはrustcを直接呼ぶ人はほとんどいないと思います. http://doc.crates.io/build-script.html に書いてある通り,build.rsというファイ ルにビルド処理を書けばcargoを使ってCのプログラムをコンパイルすることができま す.

ビルド処理を書く方法はいくつかありますが, https://github.com/alexcrichton/gcc-rs を利用するのが簡単だと思います. gcc-rsという名前ですがwindowsならMSVCを実行してくれるらしいです.ただ自分が windows環境で試したことはないです.

具体的には,以下のようなフォルダ構成にします.

Cargo.toml
build.rs
src/
 |----- main.rs
 |----- c
        |------- foo.c

ここで,Cargo.tomlに以下のように記述します.

[package]
links = "foo"
build = "build.rs"

[dependencies]
libc = "0.2.0"

[build-dependencies]
gcc = "0.3"

また,build.rsには以下のように記述します.

extern crate gcc;

fn main(){
    gcc::Config::new()
                .file("src/c/foo.c")
                .include("src")
                .compile("libfoo.a");
}

これで,cargo buildとすれば勝手にlibfoo.aコンパイルされて,さらに 本体のプログラムがコンパイル&リンクされます.もちろん,Cのコードを修正 すればcargo buildで自動で再コンパイルしてくれます.

Cからrustで書いた関数を呼ぶ方法

以下のrustの関数をCで呼ぶことを考えます.

foo.rs

#[no_mangle]
pub extern fn foo(){
   println!("foo");
}

ここで,extern をつけて関数がCの呼び出し規約に従うようにします.また, #[no_mangle]でマングリングされないようにしています.

呼び出し元のCの関数は以下の通りです.

main.c

#include <stdio.h>

void foo(void);

int main(){
    foo();
}

以下のようにすれば,プログラムがコンパイルできます.

$ rustc --crate_type="dylib" foo.rs
$ gcc -o main -L. -lfoo main.c
$ ./main

staticにリンクする場合は以下のようにします.

$ rustc --crate_type="staticlib" foo.rs
$ gcc -o main main.c libfoo.a
$ ./main

ということでrustからCの関数を呼ぶ方法(or その逆)について簡単に書きました. 実際には引数をどうするかとかいろいろ問題がありますが,公式のドキュメントに 書いてあると思うのでそちらを参照してみてください.というか自分が書けるほどまだ 詳しくないですし.. とりあえず何か既にあるライブラリの関数を呼びたいという場 合はlibcpuidを読ぶ例(http://siciarz.net/ffi-rust-writing-bindings-libcpuid/) が分かりやすいなと思いました.