めのん@ひとりプログラミング同好会

このブログはすべてフィクションであり、実在の人物、団体とは一切関係ありません

OpenSiv3Dで使われている難解な言語仕様

こんにちは、めのんです!

例によって今回もOpenSiv3Dに関する話題です。

いつもならお昼休みに書いて13時前には投稿するんですけど、今日は日曜日ですし、ちょっと遅くなってしまいました。

遅くなった理由はもうひとつあって、OpenSiv3Dのプログラムを読み解くのがだんだん難しくなってきて、ブログに書けるほど考えがまとまらなかったからです。

ここは開き直って、読み解くのが難しいなら難しいで、どこが難しいのかを紹介してしまおうと思いました。 難しいところはたくさんあるのですが、まずはC++の難しい部分について書いてみることにします。

私はCはそれなりに経験があるのですが、その感覚ではとっつきが悪いところがどうしてもあります。 具体的には次のようなものです。

このあたりは省略を最小限にして、処理内容を可能な限りソースコード上に表現しようとしているZigとは対照的だなあと感じています。

C++では演算子オーバーロードができます。 私はC#も以前から使っていますので、演算子オーバーロードがどんなものかはわかっているつもりでしたが、C#演算子オーバーロードってそんなに積極的には使わえないように思います。 それに比べてC++は、というかOpenSiv3Dはかなり積極的に使っているようで、ソースコードをパッと見ただけでは何がどうなっているのかわかりづらいんです。

たとえば、先日から取り上げているHello, World!のような簡単なプログラムでも演算子オーバーロードが使われています。

# include <Siv3D.hpp> // OpenSiv3D v0.4.3

void Main()
{
    // 大きさ 60 のフォントを用意
    const Font font(60);

    while (System::Update())
    {
        // テキストを画面の中心に描く
        font(U"Hello, World!").drawAt(Scene::Center(), Palette::White);
    }
}

一見してわかりにくいですが、fontというのはFontクラスのオブジェクトですよね。 while文の中では、このfontというオブジェクトに対してオーバーロードされた関数呼び出し演算子を使っています。

イメージとしてはこんな感じです。

font.operator()(U"Hello, World!")

C++の入門書に登場するcoutを使ったHello, World!でも演算子オーバーロードを使っているので、同じと言えば同じなんでしょうけどねえ。

で、fontオブジェクトの関数呼び出し演算子の評価結果はDrawableTextというクラスを値返ししています。 その返されたDrawableTextクラスのオブジェクトは当然右辺値になると思うのですが、それに対してメンバー関数drawAtを呼び出しているようです。

Cだと、関数が返した値を別の関数に渡すぐらいのことはありますが、関数が返した値を使って関数呼び出しすることなんかまずありませんから。 まっ、関数へのポインタを返す場合はそういうこともできないわけじゃないでしょうけど、たいていは失敗するとNULLを返すことになるのでNULLチェックぐらいはしますからねえ。

こういう感じでなかなか苦戦しています。

まだそこまでたどり着いていませんが、この先デストラクタや例外処理が絡んでくるともっと複雑になる予感がしています。 C#でも結構苦戦しましたから。 そういえば、PHPにもデストラクタと例外処理があるので同じ問題が出てくるんでしょうね。

というわけで、なかなか難しくて読み解くのが難航していますが、どうにかこうにか進めています。

あっ、そういえばユーザー定義リテラルのことを書くのを忘れていましたね。 それは今夜のブログネタにしたいと思います。

それでは!!