2
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 1 year has passed since last update.

CrystalAdvent Calendar 2022

Day 10

CrystalのメソッドをC++から呼び出す

Last updated at Posted at 2022-12-09

はじめに

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

crlib.cr
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++

cppmain.cpp
#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_initsumの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の連携を解説します。

参考

2
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
2
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?