BLOGブログ

2019.05.29UE4UE/ C++

[UE4]UE4のアサーションについて考える

執筆バージョン: Unreal Engine 4.20

今回はアサーションについてです。

アサーションとは、不正な値・処理ではないかチェックし、不正であった場合プログラムを終了させます。
リリース時には処理速度を向上させるためにチェックの処理が行われないようになっています。

基本的には想定外の使い方をされたことを使う側に通知するためにプログラムに埋め込みます。
想定外の値・処理になった場合でもそのまま動作させてしまい、原因がわからなくなる前に
問題を検知し早めに原因追求を行うことができます。

UE4でC++で処理をよく書く人方は、とりあえず取得したオブジェクトがnullptrになってないか確認するためにcheckマクロを使うかと思います。

UE4で提供されているマクロをおさらいします。
check(expression);
checkf(expression, …);
expressionの条件が偽(false)の場合プログラムを終了させます。
もし、デバッガをつなげている場合はプログラムを止めて問題のソースコードの行に飛びます。
製品リリースの設定(Shipping)でパッケージを作成するとこの処理は無効になり実行されません。
「f」がつくと追加情報をログに追加することができます。

verify(expression);
verifyf(expression, …);
checkマクロと同様にexpressionの条件が偽(false)の場合プログラムを終了し、デバッガをつなげている場合は問題の行に飛びます。
checkマクロと違う点としては、製品リリースの設定でも無効にはなりません。処理は実行されます。
checkf同様「f」がつくと追加情報をログに追加することができます。

ensure(expression);
ensureMsg(expression, message);
expressionの条件が偽(false)の場合、ログにアサーションメッセージとコールスタックを出力しますが、
checkマクロと異なり処理を止めません。デバッガーが起動していた場合は問題のソースコードの行で止まります。
メッセージをログに出力したい場合はensureMsgを使用します。

以下の4つはcheckと同様にプログラムを止めるマクロですが、
用途によって定形のログが出力されるようになっています。

checkNoEntry(); 処理が呼ばれることを想定していない場合
checkNoReentry(); 一度しか呼ばれない処理が呼ばれた場合
checkNoRecursion(); 再起では呼ばない処理が呼ばれた場合
unimplemented(); 関数をオーバーライドせず処理が未実装で呼ばれた場合

本来、不正チェックのためにこれらのマクロを使用するべきなのですが、
なんでもかんでも使えばいいとはいきません。
使い所を間違えるとこんな問題が発生します…
check verifyの場合
・エディタでの開発中呼ばれるとエディタごと終了してしまう。
ensureの場合
・ログを見れない状況の人には問題が認識されにくい。

パッケージ・スタンドアローン動作では、間違いがあった場合は終了するのが良いのですが、
エディタで開発中の場合は調整中にちょっとした入力ミスでエディタが終了してしまうのは不便かと思います。

そこで他にエディタ上で開発する人に問題を通知する方法が無いか?と考えてみました。

案1 メッセージボックスの表示
メッセージボックスを表示することにプログラムは一時的に止まります。
表示方法

デバッガが動作していることを考慮して、ブレイクポイントをC++上に埋め込みたい場合はUE_BREAK_POINTマクロを埋め込んても良いでしょう。
問題点としてOKボタンを押してしまえば、そのあとはプログラムが動作してしまいますので、
別なアサーションに引っかかり、終了してしまう可能性はあります。

案2 エディタのボーズをC++から呼び出す
エディタ起動時に一時停止ボタンを押すことによりエディタが終了せずにプログラムを止めることができますが、
C++から押す仕組みが作れれば、エディタが終了せずにプログラムを止めることが可能です。

実装方法
①ModuleRulesのPublicDependencyModuleNamesに「UnrealEd」を追加

②必要なヘッダーファイルをインクルードする

③GUnrealEdの一時停止処理を呼ぶ

この処理を呼ぶとエディタ上のPauseボタンが押されたのと同じ動作をします。

用途に合わせてこれらのデバッグ機能を使いこなし、バグの無いコードを書きましょう。(自戒を込めて…)