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/) が分かりやすいなと思いました.