唐突だがMATLABのMATファイルをRustで作りたくなった。
pythonだとscipy.io.savematとscipy.io.loadmatで保存と読み込みができる。
Rustだとどうなのだろうと思って調べてみたところ、こんなクレートを発見した。
良さそうと思って使ってみたが、どうもまだ読み込みしかできないようだ。
自作するのもめんどくさいので、MATLAB C API を使えないかな〜と思って試行錯誤したメモ帳。
Cで書いた関数をRustで呼ぶ
Rust はCの関数をFFIで呼び出せる。
#include <stdio.h>
int hello (void) {
printf ("Hello World! [from C]\n");
return 0;
}
Rustからは以下のようにして呼ぶ。
extern {
fn hello ();
}
fn main() {
unsafe { hello () };
println! ("Hello World! [from Rust]");
}
cargo でCのソースも一緒にビルドするために、Cargo.toml を以下のように書く。
[package]
name = "ffi_c"
version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = "0.2"
[build-dependencies]
cc = "1.0"
また、Cargo.tomlと同じディレクトリにbuild.rsというファイルを作る。
extern crate cc;
fn main () {
cc::Build::new()
// .cpp(true) // in the case of C++
.file ("src/hello.c")
.compile ("libfoo.a");
}
これで cargo build すると libfoo.a が作られ、本体のプログラムがコンパイル&リンクされる。
Cで書いた引数・返り値ありの関数をRustで呼ぶ
引数や返り値を与えたい場合は以下のようにする。
double double_input (double input) {
return 2 * input;
}
extern crate libc;
extern {
fn hello ();
fn double_input (input: libc::c_double) -> libc::c_double;
}
fn main() {
unsafe { hello () };
println! ("Hello World! [from Rust]");
let input = 4.0;
let output = unsafe { double_input (input) };
println! ("{} * 2 = {}", input, output);
}
extern crate cc;
fn main () {
cc::Build::new()
// .cpp(true) // in the case of C++
.files (vec!["src/hello.c","src/double.c"])
.compile ("libfoo.a");
}
libcクレート でCの型をが定義されているので、これを用いる。
複数のCファイルを用いる場合は、 build.rs のfileをfiles に変えて引数をイテレータにすればいいらしい。
MATファイルを作る
MATLAB C API のサンプルである matlabroot/extern/examples/eng_mat/matcreat.c を使ってMATファイルを作らせる。
matcreat.c をsrcフォルダにコピーして、main関数をmatcreat関数に名前を変えておく。
extern crate libc;
extern {
fn matcreat () -> libc::c_int;
}
fn main() {
unsafe { matcreat () };
}
matcreat.cはMATLABのライブラリを呼び出さないといけないので、それをbuild.rsに追記する。
extern crate cc;
fn main () {
println!("cargo:rustc-link-search=native=matlabroot/bin/maci64");
println!("cargo:rustc-link-lib=mx");
println!("cargo:rustc-link-lib=mat");
cc::Build::new()
// .cpp(true) // in the case of C++
.file ("src/matcreat.c")
// .object ("matlabroot/bin/maci64/libmx.dylib")
// .object ("matlabroot/bin/maci64/libmat.dylib")
.include ("matlabroot/extern/include")
.compile ("libfoo.a");
}
私はMacを用いているのでmaci64ディレクトリに色々入っているが、LinuxやWindowsだと別の名前になる。
途中でコメントアウトされているobject() は静的ライブラリのパスを引数にとるらしく、動的ライブラリのパスを渡したらwarningを吐かれた。
これで無事matファイルがされる。
終わりに
できればRustから直でMATファイルを読み書きできるやつが欲しい。
参考サイト
https://scrapbox.io/yuta0801/Rust%E3%81%A7C%2FC++%E3%81%AE%E9%96%A2%E6%95%B0%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B
http://mmi.hatenablog.com/entry/2017/02/28/213656
https://qiita.com/kjunichi/items/31aef0cf3f4f7fe6dc32
https://teratail.com/questions/157950