Vala
なんか GTK の UI を作ろうかと思って、Valaで書くのがよさそうな気がしたのでValaというのを触ってみたが、なんか思ってたよりよかったな。
まあRustが打倒C++できそうな今の時代に、わざわざマイナーなValaを使ってもあんま良いことないと思うけど、まあ良かったと思う点を書いておく
リファレンスカウンタと文字列があってコンパイルがはやい
個人的には、プログラミング言語の機能って、C99 に自明な場合だけでもいいのでうまくやってくれるリファレンスカウンタと、文字列のconcatが簡単にできるぐらいで十分だと思ってたのだけど、大体そのぐらいの機能になっててよかった。
リファレンスカウンタやunownedは、Rustの所有権と比べたら雰囲気ぐらいの機能しかなくて、安全でないパターンが書けてしまうのはたしかにそうなんだけど、実際プログラム書いてると所有権は雰囲気ぐらいでなんとかするふんわりしたダーティなプログラム書きたくなることってないですか?まあそういうのはあとから自分を苦しめるだけだからあんまよくないけど。
typeinfo の API が決まってる
静的型チェックがある言語で、これ実現してるプログラミング言語ってあんま無くない?
中身はGLibのgobjectではあるけど、これのおかげで、Vala で書いたオブジェクトをPythonから呼び出すみたいなのはコード書かなくても実現できる。これは、他のgir binding (glibmmとか、gtk-rs) にはない機能のはず。
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から配列を返すときに、その配列の長さを格納するポインタが何番目の引数かを指定するみたいなことができる。
こういう細かい気配りがエモくていい。