Rust programming language

C/C++ Rust Tech

【Rust】C++ライブラリを呼び出す方法

2023年3月8日

この記事でわかること

Rustは高速かつ安全なシステムプログラミング言語ですが、既存のC++のライブラリと連携する必要がある場合もあります。

しかし、RustとC++は異なる言語仕様やメモリ管理方式を持っており、直接的に相互運用することは困難です。

そのため、C++コードを一度静的ライブラリに変換して、Rustから呼び出す必要があります。

そこで、この記事ではRustからC++のライブラリを呼び出すために必要な手順やツールについて紹介します。

RustでC++を利用するメリット

RustとC++は、それぞれ優れたプログラミング言語です。

Rustはメモリ安全性に優れており、C++は高速な処理が可能です。

そこで、RustでC++のライブラリを利用することで、両言語の得意分野を生かしたプログラミングが可能になります。

また、Rustで実装されているライブラリが少ないため、C++のライブラリを利用できるのは、Rustをメイン言語にした場合の実装コストを下げることができます。

Rustからライブラリを呼び出す方法

呼び出す方法としては、2つあります。

  1. Rustから直接C++ライブラリを呼び出す
  2. cxxを利用して、一度C++コードを介して呼び出す

後者はC++コードを他に実装したい場合に、同様な作りで導入できる

前者はRustから直接呼び出すので簡潔、

といったメリットがあります。

それぞれ解説していきます。

前準備

チュートリアル形式で実行できるように、Rustのプロジェクトと簡単なC++ライブラリを先に作成しておきます。(大まかなやり方がわかればいいという人は飛ばしてもOK

プロジェクトを作成


cargo new cxx_example
cd cxx_example

静的ライブラリを作成

細かい説明は抜きなので、もう少し詳しくはこちらを参考にしてください。

programming
【C++】静的ライブラリを生成する方法

この記事でわかること g++コマンドとarコマンドを利用して静的ライブラリを生成する方法について紹介します。 静的ライブラリを生成するには? 次の手順で。静的ライブラリを作成することができます。 1. ...

こちらのコードを静的ライブラリにします。


// cxx_example/src/hello.h
#pragma once

extern "C" {
void hello();
}

// cxx_example/src/hello.cpp
#include "hello.h"

#include 

void hello() { std::cout << "Hello" << std::endl; }

Cライブラリとしてコンパイルしてあげる必要があるので、extern "C" {}で宣言を囲っています。

静的ライブラリを生成するコマンドはこちら。


cd src
c++ hello.cpp -o hello.o -std=c++20 -c
ar rcs libhello.a hello.o 

Rustから直接C++ライブラリを呼び出す方法

1. Rustの実装

Rustの標準機能であるffi (foreign function interface)を利用することで、C++ライブラリを呼び出すことができます。

Rustのコードは次の通り。


// cxx_example/src/main.rs
mod ffi {
    #[link(name = "hello")]
    extern "C" {
        pub fn hello();
    }
}

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

#[link(name = "hello")]でライブラリ名を指定(lib.aを除いた名前)。

extern "C" {}内に、C++で定義されている関数を記述。

また、Rustで呼び出す際は、unsafe {}で囲う必要があります。

2. ビルドスクリプトの実装と実行

build.rsで静的ライブラリの情報を入力します。


// cxx_example/build.rs
use std::env;

fn main() {
    let project_dir = env::current_dir().unwrap();
    println!( "cargo:rustc-link-search=native={}/src", project_dir.display() );
    println!("cargo:rustc-link-lib=static=hello");

    println!("cargo:rustc-link-lib=c++");
    println!("cargo:rerun-if-changed=/src/*");
}

let project_dir = env::current_dir().unwrap();

は、プロジェクトのルートディレクトリを取得します(今回は、cxx_example/)。

println!("cargo:rustc-link-search=native={}/src", project_dir.display() );

は、静的ライブラリのパスを指定しています(今回は、cxx_example/src/)。

println!("cargo:rustc-link-lib=static=hello");

で、さらに静的ライブラリ名を指定しています。(lib.aを除いた名前)

今回は、C++の標準ライブラリiostreamを利用しているので、

println!("cargo:rustc-link-lib=c++");

で、標準ライブラリを指定します。(パスはデフォルトで指定されていると思います)

実行コマンドは次の通りです。


// cxx_exampleに移動してから実行してください
cargo run

コードはこちらに公開しています。

https://github.com/ishikawa-takumi/cxx_example/tree/call_lib

cxxを利用して、一度C++コードを介して呼び出す

1. C++コードを準備

静的ライブラリを呼び出すC++コードはこちら。


// cxx_example/src/hello_wrapper.h
#pragma once

void hello_wrapper();

// cxx_example/src/hello_wrapper.cpp
#include "hello_wrapper.h"

#include 

#include "hello.h"

void hello_wrapper() {
  std::cout << "Hello Wrapper" << std::endl;
  hello();
}

2. Rustの実装

cxxの利用方法の詳細はこちらを参考にしてください。

Rust programming language
【Rust】RustからC++ を呼び出す方法

この記事でわかること Rustに移行したいけど、全部を移行することはできない場合、一部のC++コードを呼び出したいと考える方も多いでしょう。 特に、外部のライブラリを使用する場合は、Rustのライブラ ...

先ほどのRustコードをcxx対応にするとこのようなコードになります。


// cxx_example/src/main.rs
#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("cxx_example/src/hello_wrapper.h");

        fn hello_wrapper();
    }
}

fn main() {
    ffi::hello_wrapper();
}

3. ビルドスクリプトの実装と実行

cxxを利用するために、Cargo.tomlcxxライブラリを追加します。


// cxx_example/Cargo.toml
[package]
name = "cxx_example"
version = "0.1.0"
edition = "2021"

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

[dependencies]
cxx = "1.0"

[build-dependencies]
cxx-build = "1.0"

build.rsもcxx向けに修正を加えます。


// cxx_example/build.rs
use std::env;

fn main() {
    let project_dir = env::current_dir().unwrap();
    println!("cargo:rustc-link-search=native={}/src", project_dir.display() );

    cxx_build::bridge("src/main.rs")
        .file("src/hello_wrapper.cpp")
        .flag_if_supported("-std=c++20")
        .compile("cxx-example");

    println!("cargo:rustc-link-lib=c++");
    println!("cargo:rustc-link-lib=static=hello");

    println!("cargo:rerun-if-changed=/src/*");
}

最後に下記コマンドで実行します。


// cxx_exampleに移動してから実行してください
cargo run


全コードをこちらで公開しています。

https://github.com/ishikawa-takumi/cxx_example/tree/call_lib_with_cxx

終わりに

今回は、RustからC++ライブラリを呼ぶ方法について2点紹介しました。

  1. Rustから直接C++ライブラリを呼び出す
  2. cxxを利用して、一度C++コードを介して呼び出す

それぞれ長所短所があるので、プロジェクトにあった方法を採用してください。

-C/C++, Rust, Tech
-, , , , , ,