はじめに
CrystalではC言語やC++のライブラリを簡単に呼び出すことができます。
では、逆にC++からCrystalのメソッドを呼び出すことはできるでしょうか。
パフォーマンスやライブラリの充実度からあえてC++からCrystal使う理由はないでしょ・・・という声が聞こえてきそうですが、C++は使っていて辛いこともあるので、気持ちよくかけるCrystalを使いたくなるのです。
そうするとはじめからCrystalを使えばいいでしょ・・・となりそうですが、いろんな経緯でプロジェクトがメインでC++でかくことになってしまった、もしくは、使いたいフレームワークの関係上C++をメインで書くしかない、そういうケースに置いてC++で書きつつ部分的にCrystalで書いて連携したいという場合があるかもしれません。
C++からCrystalを呼び出す例はあまりみなかったので、方法をまとめてみました。
C++とCrystal連携なんてしないよ・・という方も多そうですが、Ruby拡張をCrystalで書くときもこの内容が理解を助けると思います。これは次の記事で解説します。
この記事はMacで検証しました。特にビルドまわりは環境にあわせて、適宜修正をしてください。
検証環境
macOS 12.6
Crystal 1.6
clang 14.0.0
プログラム
Crystal
fun crystal_init : Void
# GC初期化が必要
GC.init
# Crystalの「main」関数を呼び出す必要があります。
# トップレベルのコードを実行する関数です
# 引数はargcとargv.今回はないので0とnullを渡す
LibCrystalMain.__crystal_main(0, Pointer(Pointer(UInt8)).null)
end
# cから呼び出す関数の定義
fun sum(a : Int32, b : Int32): Int32
puts "Calculate sum"
a + b
end
Crystalのメソッドで外部で使う場合は、Crystalの初期化を事前にする必要があります。そのためにcrystal_initとして定義しておきます。この中で必ず行うことはGC初期化GC.init
とCrystalのトップレベルのコードを実行する__crystal_main
を呼び出すことです。
この2つの初期化が必須であり、他にも初期化が必要なことはこの関数に記入しておきます。
次はC++から呼び出す関数ですが、今回は合計を計算するsum関数を定義してみました。
C++
#include <iostream>
extern "C" {
// Crystalで定義された関数を書いておく
void crystal_init(void);
int sum(int x, int y);
}
int main(int argc, char *argv[])
{
// Crystalの初期化
crystal_init();
// crystalで定義された関数を呼ぶ
int ans = sum(1, 5);
// 表示
std::cout << ans;
return 0;
}
extern
でCrystal側で定義した2つのC関数crystal_init
とsum
のextern宣言をします。main
関数では、初期化用のcrystal_init()
を先に呼び出してください。その後はsum
関数を使えます。戻り値を受け取って表示しています。
Makefile
今回は.soのような動的リンクを使う場合でやってみます。
Makefile
として以下のよう内容を記述します。
CXX = g++
# CXX = clang
CXXFLAGS = -lstdc++
TARGET = cppmain
OBJS = cppmain.o
CRLIBS = libcr.so
cppmain: $(OBJS) $(CRLIBS)
$(CXX) $(CXXFLAGS) $(OBJS) -L. -lcr -o cppmain
%.o : %.cpp
$(CXX) $(CXXFLAGS) -c $<
libcr.so: crlib.cr
crystal build $(CRFLAGS) crlib.cr --single-module --link-flags="-shared" -o libcr.so
clean:
rm -f *.o $(TARGET) $(CRLIBS)
ビルド
以上のcrlib.cr
, cppmain.cpp
, Makefile
の3つのファイルが揃ったら
make
コマンドでビルドします。
crlib.soができますので、それをcppmain.cppにリンクしています。動的リンクですので実行時にcrlib.soが必要です。
CXXはC++コンパイラの設定です。使用するコンパイルに応じて適宜修正してください。ちなみにMacの場合はgccとかg++とかいてもclangとなります。別途gcc11をインストールしてチェックしましたがこちらでも動作確認しました。
実行結果
無事CrystalのメソッドをC++から呼び出せました。戻り値も取得できています。
Calculate sum
6
おわりに
Static Linkの場合はまた別途執筆したいと思います。
次の記事ではRubyとCrystalの連携を解説します。
参考