Rustのcbindgenツールのメモ。
|
|
|
cbindgenは、C言語やC++のヘッダーファイルを生成するツール。
C言語で扱えるように定義した以下のような関数や構造体等を収集して、C言語やC++のヘッダーファイルを生成する。
(Rustの関数を呼び出す言語向けにヘッダーファイルを提供する目的)
#[no_mangle]
pub extern "C" fn add(left: i32, right: i32) -> i32 {
left + right
}
#[repr(C)]
pub struct MyStruct1 {
value1: i32,
value2: i32,
}
なお、implブロックでもextern "C"のメソッドを定義することは出来るが、C言語は構造体の中に関数を持てないので、普通にグローバルな関数として扱われてしまう。(関数名に構造体名が付加されるようなことも無い)
cbindgenはCargoを使ってインストールできる。
$ cargo install cbindgen
「ユーザーのホームディレクトリー/.cargo/bin」の下にcbindgenというコマンドがインストールされる。
(Cargoをインストールしていれば、このディレクトリーへのパスは通っている)
cbindgenを実行すればC言語のヘッダーファイルが生成できる。
実行する場所はプロジェクトディレクトリー直下。(cargo buildやcargo runを実行するのと同じ場所)
--outputで、出力するヘッダーファイルのファイル名(パス)を指定する。
(--outputを付けないと、コンソールに出力される)
--typeで対象言語の種類を指定する。CはC言語、C++はC++言語。省略時はC++。
$ cbindgen --lang C --output ffi-example.h
$ cbindgen --lang C++ --output ffi-example.hpp
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct MyStruct1 {
int32_t value1;
int32_t value2;
} MyStruct1;
int32_t add(int32_t left, int32_t right);
定数(const)がヘッダーファイルにどのように出力されるか。[2024-09-25]
pub const MY_CONST1: i32 = 123;
$ cbindgen
↓実行結果
〜 constexpr static const int32_t MY_CONST1 = 123; 〜
$ cbindgen --lang C
↓実行結果
〜 #define MY_CONST1 123 〜
cbindgenは(単純な)列挙型(enum)に対応している。[2024-09-25]
ただし、どこからも使われていない(externの関数で使われていない)列挙型は、デフォルトでは出力されない。[/2024-10-06]
#[repr(C)]
pub enum MyEnum1 {
Default,
Interrupt,
Wait,
InterruptExclude,
WaitExclude,
}
-v(verbose)を付けて見ると、列挙型自体は認識しているが、出力はされない。
$ cbindgen --version cbindgen 0.27.0 $ cbindgen -v 〜 INFO: Take ffi-example-rust::MyEnum1. 〜
cbindgen.tomlに列挙型名を明示してやったら出力される。
[export] include = ["MyEnum1"]
$ cbindgen --lang C
↓実行結果
typedef enum MyEnum1 {
Default,
Interrupt,
Wait,
InterruptExclude,
WaitExclude,
} MyEnum1;
デフォルトでは、ヘッダーファイルに出力される列挙子名は、Rustの列挙子のまま。つまりRustの命名ルールに従っていればCamelCaseである。
C言語的にはSNAKE_CASEにしたいかもしれない。
cbindgen.tomlに設定を入れてやると、列挙子名の変換が行われる。
[enum] rename_variants = "None"
| rename_variants | 出力例 | 説明 |
|---|---|---|
| None |
typedef enum MyEnum1 {
Default,
Interrupt,
Wait,
InterruptExclude,
WaitExclude,
} MyEnum1;
|
無変換。 rename_variantsが指定されていない場合のデフォルト。 |
| CamelCase |
typedef enum MyEnum1 {
default,
interrupt,
wait,
interruptExclude,
waitExclude,
} MyEnum1;
|
先頭が小文字のcamelCase。 この例だと「default」という列挙子名になっているが、このままC言語で使うとコンパイルエラーになりそう^^; |
| SnakeCase |
typedef enum MyEnum1 {
default,
interrupt,
wait,
interrupt_exclude,
wait_exclude,
} MyEnum1;
|
小文字のsnake_case。 |
| ScreamingSnakeCase |
typedef enum MyEnum1 {
DEFAULT,
INTERRUPT,
WAIT,
INTERRUPT_EXCLUDE,
WAIT_EXCLUDE,
} MyEnum1;
|
大文字のSNAKE_CASE。 |
| QualifiedScreamingSnakeCase |
typedef enum MyEnum1 {
MY_ENUM1_DEFAULT,
MY_ENUM1_INTERRUPT,
MY_ENUM1_WAIT,
MY_ENUM1_INTERRUPT_EXCLUDE,
MY_ENUM1_WAIT_EXCLUDE,
} MyEnum1;
|
接頭辞として列挙型名が付いたSNAKE_CASE。 C言語では、enumで定義した列挙子はそのままグローバル変数(定数)のように使える。 したがって、異なる列挙型であっても同名の列挙子は区別できない。 そこで、列挙型名が接頭辞として付くと便利。 |
| LowerCase |
typedef enum MyEnum1 {
default,
interrupt,
wait,
interruptexclude,
waitexclude,
} MyEnum1;
|
全て小文字。 |
| UpperCase |
typedef enum MyEnum1 {
DEFAULT,
INTERRUPT,
WAIT,
INTERRUPTEXCLUDE,
WAITEXCLUDE,
} MyEnum1;
|
全て大文字。 |