5
1

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 3 years have passed since last update.

RustからC/C++を使う

Last updated at Posted at 2021-08-13

目的

C/C++で書かれているコードをRustから使用するにはどうすればよいかを知る。

方法① CのコードをRustと一緒にビルドする方法

cc-rsを使った方法
The Crgo Bookに記載してある方法で実行できた。

流れ
①構成を以下のようにする
image.png

②ccクレートを使うことをtomlファイルに記述。CファイルにHello World実装
ccクレートを使うことでcargoにリンクオプションを自動追加してくれ、Cコードから生成されたlib.aファイルを自動でリンクしてくれるようになる。

Cargo.toml
[package]
name = "tmp"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[build-dependencies]
cc = "1.0"
hello.c
# include <stdio.h>

void hello() {
    printf("Hello, World!\n");
}
main.rs
extern { fn hello(); }

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

②build.rsファイルにどのCファイルをビルドするか記述する

Build.rs
extern crate cc;

fn main() {
    cc::Build::new()
        .file("src/hello.c")
        .compile("hello");
    println!("cargo:rerun-if-changed=src/hello.c");
}

③ビルド実行
ビルドを実行すると、outフォルダにhello.cから作成されたlibhello.aファイルが作成され自動でリンクしてくれる。
image.png

Hello Worldが表示される
image.png

詰まった箇所としてはcc-rsはclang MSVCで実行されるようで、vsCodeのビルドのデフォルトをgccにしていると上手くいかなかった。clangが必要。

方法② CのAPIを、Rustで使えるようにインターフェース定義する方法

bindgenを使い、自動でインターフェースが生成することができる。
RustでCのどの機能を使っているか分かりやすい。

①フォルダ構成
image.png
src下
image.png

②Rustで使いたいインターフェースやデータ型定義を全てのCヘッダ-を1つの.hファイルに集め記載する。

hello.h
void hello();

②実装は.cに書く

hello.c
# include <stdio.h>

void hello() {
    printf("Hello, World!\n");
}

③tomlファイルにbindgenを追加する

Cargo.toml
[package]
name = "tmp"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[build-dependencies]
bindgen = "0.59.1"

④ビルド情報を記載する。
OUTディレクトリに.hから変換されたbindings.rsが生成される

Build.rs
extern crate bindgen;

use std::env;
use std::path::PathBuf;

fn main() {
     let bindings = bindgen::Builder::default()
        .header("src/hello.h")
        // Tell cargo to invalidate the built crate whenever any of the
        // included header files changed.
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        // Finish the builder and generate the bindings.
        .generate()
        // Unwrap the Result and panic on failure.
        .expect("Unable to generate bindings");

    // Write the bindings to the $OUT_DIR/bindings.rs file.
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());

        bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");

}

main.rs
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));


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

⑤cargo buildでビルドするとoutフォルダ下にbuilding.rsが生成されている。Build.rsは自動生成されたbindings.rsをインクルードしてコンパイルされる。
image.png
image.png

環境構築で少しはまった。
LLVM clangがないと上手くいかない。以下のようなエラーメッセージが出る。
image.png
bindgenにはLLVMとclangが必要とあったのでLLVMとclangをインストール。

5
1
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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?