LoginSignup
4
0

More than 3 years have passed since last update.

RustでMATLAB C APIを呼び出してMATファイルを作る

Last updated at Posted at 2019-12-18

唐突だがMATLABのMATファイルをRustで作りたくなった。
pythonだとscipy.io.savematとscipy.io.loadmatで保存と読み込みができる。
Rustだとどうなのだろうと思って調べてみたところ、こんなクレートを発見した。
良さそうと思って使ってみたが、どうもまだ読み込みしかできないようだ。

自作するのもめんどくさいので、MATLAB C API を使えないかな〜と思って試行錯誤したメモ帳。

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

Rust はCの関数をFFIで呼び出せる。

src/hello.c
#include <stdio.h>

int hello (void) {
    printf ("Hello World! [from C]\n");
    return 0;
}

Rustからは以下のようにして呼ぶ。

src/main.rs

extern {
    fn hello ();
}

fn main() {
    unsafe { hello () };
    println! ("Hello World! [from Rust]");
}

cargo でCのソースも一緒にビルドするために、Cargo.toml を以下のように書く。

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というファイルを作る。

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で呼ぶ

引数や返り値を与えたい場合は以下のようにする。

src/double.c
double double_input (double input) {
    return 2 * input;
}
src/smain.rs
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);
}
build.rs
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関数に名前を変えておく。

src/main.rs

extern crate libc;

extern {
    fn matcreat () -> libc::c_int;
}

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

matcreat.cはMATLABのライブラリを呼び出さないといけないので、それをbuild.rsに追記する。

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

4
0
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
4
0