本記事は、現時点ではマイナーだが優れたプログラミング言語Valaについて書かれたものとなる。
元の記事は2009年4月に書かれたが、2つに分かれていた記事を統合した上で多数の記述を追加している。ただし、言語の仕様や機能などを全て扱うことはできないため、内容が偏っていたり不足していたりする可能性がある。
Valaについて
- Vala言語は、C言語でオブジェクト指向プログラミングを行う仕組みを提供するGObjectライブラリ[1]を用いてC#言語に近い文法で記述できるオブジェクト指向言語
- Valaのコンパイラ[2]を用いると、Vala言語もしくはGenie言語という独自言語によって記述されたプログラムからC言語の中間コードを作り、それをC言語のコンパイラに渡してOSネイティブな実行形式が作られる
- コンパイルしてできた実行形式はGObjectライブラリに依存するが、元々同ライブラリを用いるGTK+などを使用したプログラムは依存ライブラリが増えることがない
- C++言語と異なり、実行形式のABI互換性が頻繁に問題になることはない(C言語と同じ扱い)
- Mono/.NETやJavaはソースがOSやアーキテクチャに非依存な形式に変換されるが実行時の動作が遅く、これに対しValaのプログラムはOSネイティブな実行形式として動作し、OSやアーキテクチャに依存するが高速に動作する
- GObjectライブラリをC言語で直接用いるのは扱いにくいが、Valaのプログラムから使われる際にはこの使い方を意識する必要はなく、コードの生産性も高い
- Valaコンパイラのパッケージは公式サイト(英語)で配布されているが、ディストリのパッケージとしてインストールできる場合が多い
- Debian/Ubuntuでは “valac(-バージョン)” パッケージでコンパイラがインストールできる
- C#で使える “基本クラスライブラリ” に相当するものはなく、文法が近いといってもライブラリ部分の関係で扱い方はかなり異なる
- GLibやGIOのライブラリが色々な機能を提供しているが、これだけでは基本クラスライブラリとは同等ではなく、外部ライブラリを言語バインディングから用いる場面もある
- 既存の統合開発環境(IDE)の幾つかはValaに対応しているが、Valamaという次世代のIDEが開発されている
- Val(a)IDEと呼ばれるIDEが以前開発されていたが、開発は停滞して公式サイトもなくなった
Vala言語の機能(一部)
- C#言語に近い文法
- 基本的な文法やデータ型が似ている
- 名前空間,構造体,クラスなど
for
,switch
,while
,do
-while
,if
,foreach
などの構文switch
文における文字列による分岐- ローカル変数の型推論(
var
キーワード) - 例外の扱い
- コンストラクタの書き方に独特な部分がある(名前付きコンストラクタ)
- 特定の型を中身とするリスト型などが標準で利用できるが、libgeeライブラリを用いることでこれらよりも高度なコレクションなどの機能が利用できる
- オブジェクト指向プログラミング
- クラスとその継承
- インターフェース
- プロパティ
- メモリ管理は参照カウントの方式で動作
- オブジェクトは波括弧内から出たときなど、変数が寿命になったところで他から参照されていない限り自動的かつ即座に破棄される
- デストラクタに資源の開放処理を記述すると寿命になったタイミングでその処理が行われる
- GIOライブラリのファイル入出力関係のクラスをValaで用いるとそのオブジェクトが寿命になると自動的に閉じられ、開放処理を手動で記述しない形のコードになる
- 参照の循環には注意が必要で、弱い参照(“
weak
” や “unowned
” を付ける)を用いる必要のある場面がある
- オブジェクトは波括弧内から出たときなど、変数が寿命になったところで他から参照されていない限り自動的かつ即座に破棄される
- GObjectシグナル(イベント)の扱いに関する機能
- UTF-8エンコーディングの文字列型
- スレッド
- 無名関数/クロージャ
- 外側で宣言されたローカル変数が内側の無名関数から参照可
詳しい情報はチュートリアル(英語)を参照。
Vala/Genieで使用可能なライブラリ
- 言語バインディングの定義ファイル(.vapiファイル)を通してC言語のライブラリ[3]の機能を用いることができる
- 各ライブラリの開発ファイルもコンパイル時に別途必要
- .vapiファイルはライブラリを用いるC言語ソースを出力するためのものなので、言語バインディングが用意されていないライブラリ用のバインディングを比較的容易に自作でき、C言語で直接記述するよりもコードの可読性が向上したり資源開放処理の記述が省ける分コードが書きやすかったりする
- 多数のライブラリの言語バインディングがValaのコンパイラに同梱されている[4]
- Vala言語向けのリファレンスはライブラリごとにValadocで参照できる他、詳しい説明などはC言語向けのリファレンスを参考にすることもできる(いずれも基本的に英語)
- GLibライブラリの中の機能はGLibの.vapiファイルを通して常に使用可能
- 文字列を組み立てるのには
StringBuilder
クラスや[フォーマット文字列].printf()
が便利 - 正規表現はGLibの機能(
GLib.Regex
クラス)として利用可 - 数学に関する関数や定数などは
GLib.Math
名前空間で利用可 - リストやキューなどのデータ型もクラスとして利用可
- 詳しくはGLibのリファレンスなども参照
- 文字列を組み立てるのには
- C言語の標準Cライブラリは “posix” の.vapiファイルを通して常に使用可能
- GTK+などのGObjectに基づいたライブラリは.vapiファイルを用いてオブジェクト指向のライブラリとして使用でき、クラスの継承もできる
- .vapiファイルを用いる場合、そのファイルで使用されている名前空間を用いることができ、
using
文を用いるとその名前空間内の関数やクラスなどを名前空間を指定せずに使用できるようになる- 名前空間 “
GLib
” は常に名前空間を指定せずに使用可 - C#にある、代入する形の
using
文(別名・エイリアスの指定)の書き方は使用できない - 同じ名前のクラスなどを含む複数の名前空間に
using
文を使用した場合、名前空間を指定せずにそれを記述するとどちらかが決まらないことが出るため、名前空間を指定した書き方が必要となる[5]
- 名前空間 “
- オブジェクト指向でファイル入出力を行うにはGIOライブラリ(GLibの一部)が使える
- vala-extra-vapisでは更に多数のライブラリの言語バインディングが提供されているが、後方互換性が保証されない形で提供されるため、この中の言語バインディングを用いたコードが将来のバージョンの言語バインディングでは動かなくなる可能性がある点には注意が必要(コピーして使用するプログラム側に同梱することが推奨されている)。
Vala 0.28時点の標準付属の.vapiファイルを一覧する作業例
[vala-0.28.0/vapi]$ ls *.vapi alsa.vapi gtksourceview-2.0.vapi atk.vapi gtksourceview-3.0.vapi atspi-2.vapi gudev-1.0.vapi avahi-client.vapi hal.vapi avahi-gobject.vapi hildon-1.vapi bzlib.vapi hildon-fm-2.vapi cairo-xcb.vapi json-glib-1.0.vapi cairo.vapi libarchive.vapi ccss-1.vapi libbonoboui-2.0.vapi clutter-1.0.vapi libdaemon.vapi clutter-gdk-1.0.vapi libepc-1.0.vapi clutter-gst-1.0.vapi libesmtp.vapi clutter-gst-2.0.vapi libftdi.vapi clutter-gst-3.0.vapi libgda-4.0.vapi clutter-gtk-0.10.vapi libgda-report-4.0.vapi clutter-gtk-1.0.vapi libgdata.vapi clutter-json-1.0.vapi libglade-2.0.vapi clutter-x11-1.0.vapi libgnome-2.0.vapi cogl-1.0.vapi libgnome-menu-3.0.vapi cogl-pango-1.0.vapi libgnome-menu.vapi config.vapi libgnomeui-2.0.vapi curses.vapi libgsf-1.vapi dbus-glib-1.vapi libgvc.vapi enchant.vapi libmagic.vapi fuse.vapi libnl-1.vapi gconf-2.0.vapi libnl-2.0.vapi gdk-2.0.vapi libnl-3.0.vapi gdk-3.0.vapi libnotify.vapi gdk-pixbuf-2.0.vapi liboobs-1.vapi gdk-x11-2.0.vapi libosso.vapi gdk-x11-3.0.vapi libpanelapplet-2.0.vapi gdl-1.0.vapi libpeas-1.0.vapi gdl-3.0.vapi libpeas-gtk-1.0.vapi gdu-gtk.vapi libpq.vapi gdu.vapi librsvg-2.0.vapi gedit-2.20.vapi libsexy.vapi gedit.vapi libsoup-2.2.vapi geocode-glib-1.0.vapi libsoup-2.4.vapi gio-2.0.vapi libusb-1.0.vapi gio-unix-2.0.vapi libusb.vapi glib-2.0.vapi libvala-0.28.vapi gmodule-2.0.vapi libwnck-1.0.vapi gnet-2.0.vapi libwnck-3.0.vapi gnome-desktop-2.0.vapi libxml-2.0.vapi gnome-keyring-1.vapi linux.vapi gnome-vfs-2.0.vapi loudmouth-1.0.vapi gnutls.vapi lua.vapi gobject-2.0.vapi mx-1.0.vapi gobject-introspection-1.0.vapi mysql.vapi goocanvas.vapi orc-0.4.vapi gsl.vapi packagekit-glib2.vapi gstreamer-0.10.vapi pango.vapi gstreamer-1.0.vapi pangocairo.vapi gstreamer-allocators-1.0.vapi pixman-1.vapi gstreamer-app-0.10.vapi poppler-glib.vapi gstreamer-app-1.0.vapi posix.vapi gstreamer-audio-0.10.vapi purple.vapi gstreamer-audio-1.0.vapi raptor.vapi gstreamer-base-0.10.vapi rasqal.vapi gstreamer-base-1.0.vapi readline.vapi gstreamer-cdda-0.10.vapi rest-0.6.vapi gstreamer-check-0.10.vapi rest-0.7.vapi gstreamer-check-1.0.vapi rest-extras-0.6.vapi gstreamer-controller-0.10.vapi rest-extras-0.7.vapi gstreamer-controller-1.0.vapi sdl-gfx.vapi gstreamer-dataprotocol-0.10.vapi sdl-image.vapi gstreamer-fft-0.10.vapi sdl-mixer.vapi gstreamer-fft-1.0.vapi sdl-net.vapi gstreamer-interfaces-0.10.vapi sdl-ttf.vapi gstreamer-net-0.10.vapi sdl.vapi gstreamer-net-1.0.vapi sqlite3.vapi gstreamer-netbuffer-0.10.vapi taglib_c.vapi gstreamer-pbutils-0.10.vapi tiff.vapi gstreamer-pbutils-1.0.vapi tokyocabinet.vapi gstreamer-riff-0.10.vapi tracker-indexer-module-1.0.vapi gstreamer-riff-1.0.vapi twitter-glib-1.0.vapi gstreamer-rtp-0.10.vapi udisks2.vapi gstreamer-rtp-1.0.vapi unique-1.0.vapi gstreamer-rtsp-0.10.vapi v4l2.vapi gstreamer-rtsp-1.0.vapi vte-2.90.vapi gstreamer-rtsp-server-1.0.vapi vte.vapi gstreamer-sdp-0.10.vapi webkit-1.0.vapi gstreamer-sdp-1.0.vapi webkit2gtk-4.0.vapi gstreamer-tag-0.10.vapi webkit2gtk-web-extension-4.0.vapi gstreamer-tag-1.0.vapi x11.vapi gstreamer-video-0.10.vapi xcb-icccm.vapi gstreamer-video-1.0.vapi xcb.vapi gtk+-2.0.vapi xtst.vapi gtk+-3.0.vapi zlib.vapi gtkmozembed.vapi
コードのコンパイル
- コンパイラのコマンド名は
valac(-[バージョン])
- 出力ファイル名は
-o
オプションで指定可 - C言語ソースを出力するには
-C
オプションを付ける - スレッドを用いたプログラムのコンパイル時には
--thread
オプションを付ける - 外部のパッケージを用いる場合は
--pkg [パッケージ名]
[6]オプションを付ける
(Vala/Genieのプログラムから実行ファイルを生成) $ valac (オプション...) -o [出力ファイル] [ソースファイル...] (C言語のソースファイルを生成) $ valac -C (オプション...) [ソースファイル...]
簡単なコード例
[任意]ファイル名:
hello.vala
ライセンス:パブリックドメイン/*
* Valaを用いた簡単なコード例
* valac --pkg posix -o hello hello.vala
*/
using Posix; // Posix名前空間内の関数やクラスを名前空間指定なしで用いる
namespace TestNS // 名前空間を定義(複数段階可)
{
class Foo // クラスを定義
{
}
}
namespace Hello
{
class TestClass
{
static int cnt = 0; // 静的メンバ
public
TestClass (string str)
{
cnt++;
GLib.stdout.printf ("TestClass(): cnt=%d str=%s\n", cnt, str);
}
public int
get_count ()
{
return cnt;
}
public static int
max (int a, int b)
{
return (a < b) ? b : a;
}
}
class MainClass
{
public static int
main (string[] args)
{
/* 標準出力に文字列を出力 */
GLib.stdout.printf ("Hello, Work!!\n");
/* 引数の数によって分岐 */
if (args.length > 3) // 「.length」で長さが得られる
{
GLib.stderr.printf ("Too many arguments\n");
return EXIT_FAILURE;
}
/* 個別の要素を処理する */
foreach (string arg in args)
{
GLib.stdout.printf ("arg:" + arg + "\n");
}
/* new演算子を用いてオブジェクトを生成する */
TestNS.Foo foo = new TestNS.Foo (); // 後で使用していないので警告が出る
TestClass tst1 = new TestClass ("aaa");
TestClass tst2 = new TestClass ("bbb");
var tst3 = new TestClass ("ccc"); // 「var」キーワード
/* メンバ関数を呼び出す */
GLib.stdout.printf ("tst1.get_count(): %d\n", tst1.get_count ());
GLib.stdout.printf ("tst2.get_count(): %d\n", tst2.get_count ());
GLib.stdout.printf ("tst3.get_count(): %d\n", tst3.get_count ());
GLib.stdout.printf ("TestClass.max(3, 5): %d\n", TestClass.max (3, 5));
GLib.stdout.printf ("TestClass.max(6, 6): %d\n" , TestClass.max (6, 6));
GLib.stdout.printf ("TestClass.max(15, 7): %d\n", TestClass.max (15, 7));
return EXIT_SUCCESS;
}
}
}
実行例(引数を付けずに実行) $ ./hello Hello, Work!! arg:./hello TestClass(): cnt=1 str=aaa TestClass(): cnt=2 str=bbb TestClass(): cnt=3 str=ccc tst1.get_count(): 3 tst2.get_count(): 3 tst3.get_count(): 3 TestClass.max(3, 5): 5 TestClass.max(6, 6): 6 TestClass.max(15, 7): 15 (引数を2つ付けて実行) $ ./hello abc xyz Hello, Work!! arg:./hello arg:abc arg:xyz TestClass(): cnt=1 str=aaa ... (引数を3つ付けて実行) $ ./hello abc xyz 123 Hello, Work!! Too many arguments
使用したバージョン:
- Vala 0.26.2, 0.28.0