他言語からJulia言語を呼び出すヒント(前編 : 実行形式でC言語から呼び出すよ)

# Juliaを他の言語から呼びたいじゃん？？？

`PyCall.jl` をご存知でしょうか．
JuliaからPythonのAPIを呼び出す事ができる便利なパッケージです．大抵の場合意図している挙動をするような仕組みを提供します．
環境によっては（例えば，何も考えず，miniconda をインストールした時やpyenvで環境を入れた場合）`python-jl` を使えというメッセージが出るでしょう．[Troubleshooting
Your Python interpreter is statically linked to libpython](https://pyjulia.readthedocs.io/en/latest/troubleshooting.html) が参考になります．

さて，AからBを呼び出すのは気楽でもBからAを呼び出すのは難しいケースが多いです．例えば，Juliaの機能をC言語から呼び出したい！そういう需要はあるでしょう．Cから呼びだせば最悪それを経由してC以外の言語からJuliaを呼び出せることができるわけです．本日はその試みをしたのでその経過報告をQiitaにまとめます．

## PackageCompiler「出番か？？？」

と思うじゃないですか，実行形式は問題なく動くんです．ただ，[このIssue](https://github.com/JuliaLang/PackageCompiler.jl/issues/257) の通りにすると一応ビルドはできるんですが，呼び出しの時にセグメンテーションフォルトで失敗します．原因は９９．９９９９９９９％ `jl_init` をしていないからだと思われます．PackageCompiler.jl のソースコードを追わないとわからないですが，snoop_fileのオプションで `jl_init` を呼び出せるインターフェースを共有ライブラリに持たせるようなロジックは必要でしょう．

追記：build_shared_lib の オプションに `init_shared=true` を追加すれば良いみたいですが．手元のマシンでは解決していません．`juliaac.jl`から呼び出せばいいのかな・・・？？？

# ここでは何を話すか

ひとまず [Embedding Julia](https://docs.julialang.org/en/v1/manual/embedding/index.html) を元にC言語からJuliaを呼び出す事ができたのでそれの報告をします．

## CからJuliaを呼び出す(まずは動かせるようになろう)

プログラミングはまがいなりでも動いたら嬉しいわけです．見た目立派でも動かなかったらどうしようもないと言いますが・・・いやはや．使う立場から見れば何かしらの成功体験が欲しいわけです．

まずはこれをビルド＆動かそう


```julia:embed_example.c
#include <julia.h>
JULIA_DEFINE_FAST_TLS() // only define this once, in an executable (not in a shared library) if you want fast code.

int main(int argc, char *argv[])
{
    /* required: setup the Julia context */
    jl_init();

    /* run Julia commands */
    (void)jl_eval_string("println(sqrt(2.0))");
    /* strongly recommended: notify Julia that the
         program is about to terminate. this allows
         Julia time to cleanup pending write requests
         and run all finalizers
    */
    jl_atexit_hook(0);
    return 0;
}
```

ひとまず公式サンプルがビルドできるようにします． 公式ドキュメントを見ると

```console
$ gcc -o test -fPIC -I$JULIA_DIR/include/julia -L$JULIA_DIR/lib test.c -ljulia $JULIA_DIR/lib/julia/libstdc++.so.6
```

と書かれてますが，Macですと `libstdc++.so.6` が見つかりません（Ubuntuでは見つかりました）．
実はこっちを見るんじゃなくてこのあと数行以降に書いてあるやり方

```console
/usr/local/julia/share/julia/julia-config.jl --cflags --ldflags --ldlibs | xargs gcc embed_example.c
```

を見つけてそれに従うのが吉です．Macの場合ですと `julia-config.jl` は　`$which julia` なんかで検索して

```
/Applications/Julia-1.1.app/Contents/Resources/julia/share/julia/julia-config.jl `
```

などが見つかるでしょうこれをお元に手元のMacでは

```console 
$ /Applications/Julia-1.1.app/Contents/Resources/julia/share/julia/julia-config.jl --cflags --ldflags --ldlibs | xargs gcc test.c 
```

とするとビルドができます.`./a.out` とすると $\sqrt{2}$ の値がでます．

# お？できたやん？もっと面白い例が欲しい．

trivial な例に止まらず，何か一つできると別の例を試したくなるのは自然な発想だと思うので公式ドキュメントに出現する例を関数化してみます．

## コードはどんなものになる？

そうですね下記の通りになります. `call_julia_from.c` みたいなのを作りましょう．

```C:call_julia_from.c
include <julia.h>
JULIA_DEFINE_FAST_TLS() // only define this once, in an executable (not in a shared library) if you want fast code.


void evaluate_julia_scripts(){
    jl_eval_string("x=3");
    jl_eval_string("y = -10; z=x+y; @show z");
}

void evaluate_julia_package(){
    jl_eval_string("using Pkg; Pkg.add(\"UnicodePlots\");using UnicodePlots");
    jl_eval_string("println(lineplot(1:100, sin.(range(0, stop=2π, length=100))))");
}

double get_float64(double x) {
    jl_value_t *argument = jl_box_float64(2.0);
    jl_function_t *func = jl_get_function(jl_base_module, "sqrt");
    jl_value_t *ret = jl_call1(func, argument);
    double unboxed = jl_unbox_float64(ret);
    return unboxed;
}

void array_example(size_t sz) {
    jl_value_t* array_type = jl_apply_array_type((jl_value_t*)jl_float64_type, 1);
    jl_array_t* x          = jl_alloc_array_1d(array_type, sz);
    double *xData = (double*)jl_array_data(x);

    printf("allocate\n");
    for (size_t i = 0; i < jl_array_len(x); i++) {
        xData[i] = i;
    }
    for (size_t i = 0; i < jl_array_len(x); i++) {
        printf("xData[%zu]=%f\n", i, xData[i]);
    }
    jl_function_t *func  = jl_get_function(jl_base_module, "reverse");
    jl_array_t *y = (jl_array_t*)jl_call1(func, (jl_value_t*)x);
    printf("print result\n");
    double* yData = (double*)jl_array_data(y);
    for (size_t i = 0; i < jl_array_len(y); i++) {
        printf("yData[%zu]=%f\n", i, yData[i]);
    }
}

void matrix_example(size_t row, size_t col) {
    // Create 2D array of float64 type
    jl_value_t *array_type = jl_apply_array_type(jl_float64_type, 2);
    jl_array_t *x  = jl_alloc_array_2d(array_type, row, col);

    jl_function_t *func  = jl_get_function(jl_base_module, "println");

    // Get array pointer
    double *p = (double*)jl_array_data(x);
    // Get number of dimensions
    int ndims = jl_array_ndims(x);
    // Get the size of the i-th dim
    size_t rows = jl_array_dim(x, 0);
    size_t cols = jl_array_dim(x, 1);

    // Fill array with data
    for (size_t r = 0; r < rows; r++) {
        for (size_t c = 0; c < cols; c++) {
            double value = c + cols * r;
            p[r + rows * c] = value;
            printf("p[%zu,%zu]=%f\n", 1 + r, 1 + c, value);
        }
    }
    (void*)jl_call1(func, (jl_value_t*)x);
}

int main(int argc, char *argv[])
{
    /* required: setup the Julia context */
    jl_init();

    /* run Julia commands */
    evaluate_julia_scripts();
    evaluate_julia_package();
    printf("%f\n", get_float64(2.0));
    array_example(15);
    matrix_example(10, 5);
    /* strongly recommended: notify Julia that the
         program is about to terminate. this allows
         Julia time to cleanup pending write requests
         and run all finalizers
    */
    jl_atexit_hook(0);
    return 0;
}
```

# ざらりと述べる

## `jl_init` を呼び出しておく

`main` 関数中に記述していますが，`jl_init` を呼び出す必要があります

## evaluate_julia_scripts.jl 

```C
void evaluate_julia_scripts(){
    jl_eval_string("x=3");
    jl_eval_string("y = -10; z=x+y; @show z");
}
```

"と"で挟まれる式をJulia中のREPLと同じ感覚で操作できるようです．`"y = -10; z=x+y; @show z"` で x が見た目未定義ですが，`jl_eval_string("x=3");` によってスコープに `x` が入っているからだと思われます．もちろん

```julia
jl_eval_string("using Pkg; Pkg.add(\"UnicodePlots\");using UnicodePlots");
```

のようにしてパッケージをインストールすることも可能ですしそれを `using` でインポートして呼び出したいということも可能です．
ここでは `eval` をしてるだけでCとJulia明示的なデータをやりと知りてませんが，Float64の型をもつ値との変換や，１，２次元テンソルの扱いなどは `matrix_example` や　`array_example` などを参照ください．なお，このサンプルコードを作った後に気づきましたが，

[/julia/test/embeddin](https://github.com/JuliaLang/julia/tree/master/test/embedding)

を見ると公式ドキュメントのコードを塊としてまとめてたみたいです，

# Makefileでビルド・実行・クリーンを管理したい

個人開発の範囲ではちょろっとMakefileを書いてビルドの手順を書いておくと色々楽です．下記のファイルは特にオリジナルな部分はなく[ここのMakefileのヒントを参考にして動くようにしています．](https://docs.julialang.org/en/v1/manual/embedding/index.html#Use-in-Makefiles-1)

```Makefile
JL_SHARE = $(shell julia -e 'print(joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia"))')
CFLAGS   += $(shell $(JL_SHARE)/julia-config.jl --cflags)
CXXFLAGS += $(shell $(JL_SHARE)/julia-config.jl --cflags)
LDFLAGS  += $(shell $(JL_SHARE)/julia-config.jl --ldflags)
LDLIBS   += $(shell $(JL_SHARE)/julia-config.jl --ldlibs)

TARGET_NAME=call_julia_from
C_PROGRAM_FILE=call_julia_from.c

all: run

embed_example: $(C_PROGRAM_FILE)
	gcc $< -o $(TARGET_NAME) $(CFLAGS) $(LDFLAGS) $(LDLIBS)

run: embed_example
	./$(TARGET_NAME)

clean:
	rm -f ./$(TARGET_NAME)
```

これを `call_julia_from.c` と同じディレクトリに配置して

```
$ make
# または
$ make run
# 無事ビルドが通るとプログラムが動く・・・・
```

をします．これは gcc コンパイラに必要なパス・オプションを指定してビルドしてから生成されたバイナリーを走らせることと同じです．

# 力尽きたので別記事で

次は共有ライブラリとコンパイルされたC言語のプログラムにJuliaを埋め込む方法を書いていきます．
何もわからん(´・ω・｀)

[中編を書きました](https://qiita.com/SatoshiTerasaki/items/50dc38e6a8d45f0741dd)