vlang コードを .so ビルドし, C/C++ などから呼べるようにする(println や builtin 関数いくつか動くのを確認).

## 背景

vlang の関数を, C から呼びたい

* vlang でプロトタイピングして動作確認したのち, C/C++ に組み込みたい
* vlang で live coding して動作確認したのち, リリース向けでは C/C++ バイナリ単体にしたい

など.

vlang 自体は, 一度 C コードに変換して実行しているため, C/C++ と連携はやりやすいと想像できます.
とりあえず基本的なコードは動きました.

## 環境

v 0.2 を仮定します(2020/03/31 時点ではまだリリースはされていない.  git `master` branch 利用)

Linux 64bit を仮定します.

0.2 から, モジュールのビルド方法が変わりました. ast パーサなど内部構造を変えたからでしょうか. より汎用的に使えるようになっています.

<blockquote class="twitter-tweet"><p lang="et" dir="ltr">Of course.<br><br>module mymodule<br><br>fn sayhello() { ... }<br><br>v -o mymodule.c mymodule.v<br><br>cc -c mymodule.c<br><br>// main.c<br>void mymodule__sayhello();<br><br>int main() {<br> mymodule__sayhello();<br>}<br><br>cc main.c mymodule.o</p>&mdash; The V Programming Language (@v_language) <a href="https://twitter.com/v_language/status/1244280660707160066?ref_src=twsrc%5Etfw">March 29, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## モジュールをコンパイルする.

通常の C コードと同様な感じでモジュールを作ることができます.

* .v から .c を生成する
* C compiler で .so にコンパイルする

```
// fibrec.v
module fibrec
pub fn fib(n int) int {
  if n <= 2 {
    return 1
  } else {
    return fib(n-1) + fib(n-2)
  }
}
```

```
$ ./v -shared -o mymodule.c fibrec/fibrec.v
```

`-shared` を付けて, main 関数などが生成されないようにします.

あとは通常の C と同じように扱います.
`-fPIC` は基本付けておきましょう. 付けないと一部 vlang 関数のコンパイルでうまくいかないです. `-lm` など必要なライブラリもリンクしておきます.

これで完成です!

python から呼んでみます.

```
import ctypes

lib = ctypes.cdll.LoadLibrary("./fibrec.so")

arg_int = ctypes.c_int(10)
res = lib.fibrec__fib(arg_int)
print(res)
```

という感じで python から呼んで試してみましょう!

少なくとも, fibrec に `println` や組み込み関数の array を追加して, うまく扱えるのを確認しました.

## TODO

* [ ] 複数 `.v` をコンパイルして `.so` が作れるか確認する
* [ ] ほかの vlang 組み込み関数がコンパイルできるか確認する
 
以下は, 古い情報です. 記録まで.

===============================================================


## 環境

v 0.1.25 を仮定します(2020/02/26)

Linux 64bit を仮定します.

Exporting function to library (dll/so) #2379
https://github.com/vlang/v/issues/2379

は, このままではうまくいきません.

## module

まず, 普通に `.v` を `-shared` でビルドすると, main 関数など全部入りになります.

vlang では, module の機能(Java 的な感じ?)がありますので, module の機能を使います.

```
// fibrec.v
module fibrec
pub fn fib(n int) int {
  if n <= 2 {
    return 1
  } else {
    return fib(n-1) + fib(n-2)
  }
}
```

フォルダの構成としては, `<module>/file.v` が想定されます((Java 的な感じ? golang も同等かしら?). フォルダ名をモジュールと合わせる必要がありますが, 実態のコードがあるファイル名は `.v` であればなんでもいいかもしれません.

```
$ mkdir fibrec
$ mv fibrec.v fibrec/
./v build module fibrec
```

とすると, `~/.vmodules/` に .o ができます. パスは変更できないようです.

```
$ ./v -verbose
```

として, どのようなコマンドが呼ばれるか確認しましょう.

`-shared` をつけると, relocatable(`-fPIC` つき)な `<module>.o` が生成されます.

```
$ gcc -o fibrec.so -shared ~/.vmodules/fibrec.o
```

として, .so を作ります. nm でシンボルを確認しておきます.

python から呼んでみます.

```
import ctypes

lib = ctypes.cdll.LoadLibrary("./fibrec.so")

arg_int = ctypes.c_int(10)
res = lib.fibrec__fib(arg_int)
print(res)
```

55 と表示されれば成功です!

## 制限

`println` や配列など, builtin や vlib の機能を使うと, シンボルが見つからないエラーになります. `vlib` のいくつかを .so に取り込む必要があるようですが, 現状 `_wyp0` シンボルが重複するなどのエラーになります. より一般的に vlang のコードを .so にするにはまだ開発に時間かかりそうです.

## TODO

* [ ] Windows(llvm-mingw など)でうまくいくか確認する
* [ ] vlang の標準ライブラリが使えるようにする
* [ ] vlang を, C/C++ に組み込んでスクリプト実行する(libv みたいなのが作れればいけるか?)