見出し画像

MGL週報 #69 - MGLをgccでもコンパイルが通るようにしよう対策案

このエントリはゲーム開発用フレームワーク「MGL」の開発記録です。MGLはzlibライセンスの下に無償で提供されています。


MGLをgccでもコンパイルが通るようにしよう対策案

問題の経緯

MGLはどのようなプラットフォームでも動作できるよう、中核部分はC++の標準的な言語仕様とAPIのみで構成されています。中核部分には描画やユーザー入力の取得といった環境固有の機能は含まれていませんが、これらは後から任意のものを追加できる仕組みになっています。これにより、標準で対応していないプラットフォームに対しても、自前でそれらの機能を用意する(もしくは誰かに作ってもらう)事であらゆる環境で動かせるという仕組みになっています。

……という仕組みで作っておきながら、MGLは現在、移植性について大きな問題を抱えています。C++における主要コンパイラの3つのうちの1つ、gcc(g++)においてコンパイルが通らないのです。

その理由は、MGLの定数式の扱いに適切でない使用箇所があるためです。

C++における定数式(constexpr)は、その内容を実行時ではなくコンパイル時に処理するための機能です。これについては説明すると長くなるのでここでは割愛し、例によってcpprefjpさんへのリンクを貼っておきましょう。

MGLは標準構成としてWindowsとAppleプラットフォームに対応していますが、前者のコンパイラはMSVC、後者はClangが標準的に用いられています。両者は現状の用法でもそれを適格とみなすため、つい最近までこの問題に気付いていませんでした。

より具体的な原因は、スマートポインタを扱うstd::unique_ptrの内容の取得を定数式内で行っている事です。MGLはC++17準拠ですので、このバージョンの仕様に準ずるなら定数式内では非定数式の関数を呼び出すことはできないはずです。しかし、先述の通りMSVCやClangはこれを適格とみなし、gccではこれを不適格とします。

この差異の原因は実のところ調べきれていません。コンパイラ自身の差異ではなく、STLの実装に依存している可能性もあります。そちらの問題も気にはなりますが長い寄り道となりそうですので、今は目下の「標準的なビルド環境でエラーが出る」問題に専念しましょう。

……という点を踏まえて、じゃあどうしようかという案を3つほど考えてみました。

対策案1: エラーの出ているconstexprを取り払う

いわゆる正攻法。現状が標準的な使用方法でないのだから、それを標準的に直せば良いだろうという案です。

この方法は確実ではあるのですが、芋づる式に影響範囲が広くなる懸念があります。

定数式として定義した関数が定数式になれない事が問題ですので、そこからconstexprを外せばその関数は適格となります。しかし、それを呼び出している関数も定数式ではいられなくなるため、そちらに対してもconstexpr外しを繰り返していく必要があります。これはMGLの公開APIにも影響を及ぼし、ひいてはアプリケーション側にも影響が伝播するでしょう。

実際のところ、MGLはまだ安定リリースを迎えていないため、現状ではこの破壊的変更がそこまで問題にはならないかもしれません。とはいえ、後述の案まで考慮するとこれ一択とも言い難くなります。

対策案2: 廃止したMGL_MAYBE_CONSTEXPRを復活させる

今回問題になっているのはgccとMSVC/Clangとの間での差異による問題ですが、実はMSVCとClangの間にも同様の差異はありました。そして、MGLは少し前までこの差異を解決するためにMGL_MAYBE_CONSTEXPRというプリプロセッサマクロを使用していました。

このマクロは場合によって内容がconstexprだったり、もしくは空だったりするものでした。コンパイラの種類によってその内容を切り替え、定数式として通らない可能性のある関数はconstexprの代わりにこのマクロを使用していました。

このマクロは標準的でない用法を力技で無理やり押し通すための存在とも言えるものです。本来は頼るべき機能ではなかったため、MGL 1.1.5にて廃止してしまいました。その際、エラーとなっていたAPIに関しては対策案1のような正攻法による対応を行っています。

このマクロを復活させて、gccでのみ内容を空にするという手法も使えなくはありません。これはあまり大っぴらにしたくない類の手法ですが、後述の事を考えると一時しのぎにはなるかも……という感じです。

対策案3: C++23にアップデート

MGLは現在C++17に準拠したフレームワークですが、これは可能であればタイミングを見てアップデートしていきたいとは考えています。そして、実は今回の件はC++23だと問題にならなかったりします。

この問題の根源となっているstd::unique_ptr::get()はC++17の時点では定数式ではありませんが、C++23から定数式になっています。この変更により、今エラーとなっている関数は軒並み適格となるでしょう。

C++23へのアップデートは、やるのであればモジュール化なども検討したいのでちょっと時間がかかりそうです。しかし、将来予定しているのであれば、対策案2のような一時しのぎも策の1つかなとも考えています。

ついでに、本件とは無関係そうですが、C++23での定数式に関する仕様変更で気になるものを見つけました。

「標準ライブラリの定数式化が未来の話で困ってるよ」という点では共通していて、対策例のマクロを用いる手法は対策案2に近いと言えますね。

その他の策

「対策案4: std::unique_ptrに相当する機能を自作する」というのも書こうかと思いましたが、定数式とは無関係なうえにちょっと説明がややこしいので今回は割愛します。

具体的な理由な省略しますが、MGLのSTLエイリアスのうちMGL::STL::make_unique()だけが、実はエイリアスではなく独自実装になっていたりします。これが何だか気持ち悪いなと思ってはいて、もういっその事スマートポインタ自体を独自に実装してしまえばC++23を待つ必要も無くなるな、とチラリと思ったりもしました。

しかし、標準ライブラリにある機能を自前で用意するのは、特別な理由がない限りは避けたいところ。今回の件が「特別な理由」に相当するか否かは本件とはまた別問題になるため、今回は一旦忘れる事にしましょう。

まとめ

直近の対応としてはMGL_MAYBE_CONSTEXPRを復活させるか否かの判断となりますが、これは影響範囲を鑑みたうえで決めようかと思います。

そして、C++23へのアップデートの理由もまた1つ増えてしまいました。これは頃合いを見計らって積極的に対応したいですね。

どちらにせよ、次のバージョンではgccでもビルドが通せるようにする予定です。


その他

この週報は読み手に専門知識を極力求めないよう心掛けていたつもりなのですが、ここ最近は作業内容の関係で真面目に書くとC++のマニアックな話題になりがちです。

この記事が気に入ったらサポートをしてみませんか?