Vala

なんか GTK の UI を作ろうかと思って、Valaで書くのがよさそうな気がしたのでValaというのを触ってみたが、なんか思ってたよりよかったな。

まあRustが打倒C++できそうな今の時代に、わざわざマイナーなValaを使ってもあんま良いことないと思うけど、まあ良かったと思う点を書いておく

リファレンスカウンタと文字列があってコンパイルがはやい

個人的には、プログラミング言語の機能って、C99 に自明な場合だけでもいいのでうまくやってくれるリファレンスカウンタと、文字列のconcatが簡単にできるぐらいで十分だと思ってたのだけど、大体そのぐらいの機能になっててよかった。

リファレンスカウンタやunownedは、Rustの所有権と比べたら雰囲気ぐらいの機能しかなくて、安全でないパターンが書けてしまうのはたしかにそうなんだけど、実際プログラム書いてると所有権は雰囲気ぐらいでなんとかするふんわりしたダーティなプログラム書きたくなることってないですか?まあそういうのはあとから自分を苦しめるだけだからあんまよくないけど。

あとC++とかRustってコンパイル遅くていやになることあるけど、Valaはまあそんなにコンパイル遅くない

typeinfo の API が決まってる

静的型チェックがある言語で、これ実現してるプログラミング言語ってあんま無くない?

中身はGLibのgobjectではあるけど、これのおかげで、Vala で書いたオブジェクトをPythonから呼び出すみたいなのはコード書かなくても実現できる。これは、他のgir binding (glibmmとか、gtk-rs) にはない機能のはず。

Makefile

all: test_shared-0.1.typelib

test_shared-0.1.typelib: libtest_shared-0.1.so
	g-ir-compiler --shared-library=$< --output=$@ test_shared-0.1.gir

libtest_shared-0.1.so: gir.vala
	valac --gir test_shared-0.1.gir --library=test_shared $< -X -fPIC -X -shared -o $@

gir.vala

namespace TestNS {

public class TestCLS : Object{
  public int y {get; set;}
}

}

run.py

import gi
gi.require_version('test_shared', '0.1')
from gi.repository import test_shared

v = test_shared.TestCLS.new()

v.set_y(10)
print(v.get_y())

Cからbinding作るのがエモい

なんか色々楽しい機能が付いていて色々なC関数をバインディング用のコード書かなくてもValaっぽく書けるようになるのがエモくていい。

https://wiki.libsdl.org/SDL_AddTimer

こういう、コールバックに、void *として汎用オブジェクトを渡すようなのを、なんかうまく書ける。

class Obj {
  public uint32 timer_callback(uint32 interval) {
    stdout.printf("callback : %p\n", this);
    return 0;
  }
}

public int main(string [] args) {
  SDL.init();

  var obj = new Obj();
  stdout.printf("init : %p\n", obj);

  SDL.Timer(1000, obj.timer_callback);

  SDL.Timer.delay(2000);

  return 0;
}

obj と紐付いたtimer_callbackが呼ばれるようになっている。これは、

static guint32
_obj_timer_callback_sdl_timercallback (guint32 interval,
                                       gpointer self)
{
	guint32 result;
	result = obj_timer_callback ((Obj*) self, interval);
	return result;
}

gint
_vala_main (gchar** args,
            gint args_length1)
{
	Obj* obj = NULL;
	Obj* _tmp0_;
	FILE* _tmp1_;
	SDL_TimerID _tmp2_;
	gint result = 0;
	SDL_Init ((guint32) SDL_INIT_EVERYTHING);
	_tmp0_ = obj_new ();
	obj = _tmp0_;
	_tmp1_ = stdout;
	fprintf (_tmp1_, "init : %p\n", obj);
	_tmp2_ = SDL_AddTimer ((guint32) 1000, _obj_timer_callback_sdl_timercallback, obj);
	SDL_Delay ((guint32) 2000);
	result = 0;
	_obj_unref0 (obj);
	return result;
}

このような形にコンパイルされる。SDL_AddTimer の void * param を、なんかいい感じにうまいことやってくれてるというのがわかるだろうか。

これは、どうなってるかというと、コールバックの際にそのコールバックと紐付けられる this の引数の位置を指定できるようになっている。

https://github.com/GNOME/vala/blob/4c63d52fb24b2b9847c5615c38f8e9f707bddd65/vapi/sdl2.vapi#L4439

delegate_target_pos がそれ。

他にも、Cから配列を返すときに、その配列の長さを格納するポインタが何番目の引数かを指定するみたいなことができる。

こういう細かい気配りがエモくていい。

まとめ

まあなんかそういうあたりが好きだった。

GTKで何か書くときは、Cで書くより圧倒的に楽だし、glibmm とか gtk-rs で書くよりコンパイル時間短くできるので、嫌いでなければ使ってみるのも楽しいのではないかと思います。