UE4 の UCLASS の定義の基底クラスに名前空間を使っている子が居る場合の不幸と安直な回避策について
現状の UE4 で C++ の namespace
を使おうと思っても面倒事が増えるだけなので、現実問題としてはわざわざ UE4 の C++ コードを記述するに当たっては namespace
を用いないほうが賢い。少なくとも UE-4.16 現在は。
しかし、そうは言っても、世の中の素敵な C++ ライブラリーを UE4 プロジェクトへ取り込んで使いたい場合もある。単にどこかのメンバー関数から呼び出すだけだとか、そういう場合はたいてい問題は起こらない。しかし、深刻な問題となる場合に遭遇したので tips として紹介したい。
UCLASS
で定義する UHogeComponent
なり AHogeActor
なり、そういったクラスの定義で、素敵な C++er 仕様のきちんと namespace
を使ったライブラリーのクラスを継承して使いたい場合があったとしよう。継承じゃなくてコンポジションすればいいとかは本題ではないのでここでの議論は割愛する。
具体例は次の通り:
// MyComponent.h #pragma once #include "CoreMinimal.h" #include <memory> #include "MyComponent.generated.h" // UCLASS() 付きで定義するクラスの基底クラスに名前空間付きの // クラスを使用するコードを書いてしまうとビルド不能に陥る例 UCLASS() class MyComponent : public std::enable_shared_from_this< MyComponent > { GENERATED_BODY() };
なお、 UE4 では std::enable_shared_from_this
や std::shared_ptr
に対応する TSharedFromThis
や TSharedPtr
が用意されているのでこの例の場合はそれらを使うだけでも問題は回避できるが、この例で std::enable_shared_from_this
を使ったのは、誰でも C++ 標準ライブラリーのクラスとしてすぐに include してどのような問題が発生するのか試しやすいために選んだ。ついで、 enable_shared_from_this
はコンポジションではなく継承で使う理由のある例としても有意性が高い事も選んだ理由に含まれる。
このコードはビルドすると次のようなエラーに見舞われる。
MyComponent.h(11) : Error: Missing '{' in 'Class'
このエラーは C++ コンパイラーの前に UE4 が UCLASS()
などを解釈して独自の追加のヘッダーファイル “MyComponent.generated.h” 等を生成するために動かす UnrealHeaderTool が UCLASS で定義されるクラスについて基底クラス部分やその他あちらこちらでそもそも C++ の namespace 機能が使われる事をまったく想定していないためにパースに失敗して発生してしまう。
悲しい。
しかし、現実問題として我々プログラマーは UE4 の世界とは無関係に開発された有用な多くの C++ コード資源を活用して、さくっと目的のプロダクトを開発したい。このような開発に対しては本質的でないトラブルの対応のためにオレオレライブラリーやオレオレラッパーを書いていいのはそれ自体が趣味の場合だけだ。そこで、
// MyComponent.h #pragma once #include "CoreMinimal.h" #include <memory> #include "MyComponent.generated.h" // 名前空間を使わないエイリアスを定義 template < typename T > using std_enable_shared_from_this = std::enable_shared_from_this< T >; // 名前空間を使わないエイリアスを介して UCLASS() 付きのクラスの基底クラスとして使用 UCLASS() class MyComponent : public std_enable_shared_from_this< MyComponent > { GENERATED_BODY() };
これでいい。少なくとも UnrealHeaderTool が namespace
(というか名前空間解決演算子)を考慮していない UE-4.16 執筆現在は。悲しい事だがプログラマーは現実の製品を作ってなんぼ・w・ 未来の UE4 が namespace に美しく対応する日を夢見て現実を乗り越えよう。
UE4 における enum class の「フラグ」化
// ↓ブループリントで使わない場合はこの1行は不要 UENUM( BlueprintType, meta=( Bitflags ) ) enum class EHoge: uint8 { EV_Nothing = 0 , EV_Flag1 = 0x01 , EV_Flag2 = 0x02 , EV_Flag3 = 0x04 , EV_Flag4 = 0x08 , EV_FlagAll = EV_Flag1 | EV_Flag2 | EV_Flag3 | EV_Flag4 }; // ↑ こんな enum class に ↓ このマクロを通しておく(今回の本題) ENUM_CLASS_FLAGS( EHoge )
ENUM_CLASS_FLAGS
マクロに通しておくと、 EHoge
で次のようなフラグ計算でお馴染みの機能についてユーザー独自に定義したり、
underlying_type_t
に static_cast
して云々など必要なくなり、楽ができるようになる。
auto a = EHoge::Nothing; // フラグ追加 a |= EHoge::EV_Flag3; // フラグ判定 auto result_of_any = ( a & EHoge::EV_FlagAll ) != EHoge::EV_Nothing;
参考
UE4: C++er 視点での実行中の AActor への USceneComponent の動的なオブジェクトの実行中の生成、レジスター、アタッチ、デタッチ、アンレジスター、破棄の方法
状況 (½) おさらい編: AActor::AActor
(アクターのコンストラクター) で生成する場合
// .h
UHogeComponent* HogeComponent;
// .cpp AActor::AActor() { RootComponent = CreateDefaultSubobject< USceneComponent >( TEXT( "RootComponent" ) ); HogeComponent = CreateDefaultSubobject< UHogeComponent >( TEXT( "Hoge" ) ); HogeComponent->SetupAttachment( RootComponent ); }
以上のように ctor で"デフォルト"のサブオブジェクトとして生成する場合には CreateDefaultSubobject
を使う。
Note: コンポーネント間のアタッチメントは SetupAttachment
を使うのがナウい方式。やや古い参考文献を見ると [[deprecated]]
な API を使っている事があるのでコンパイラーの warning を見てモダン化する。
状況 (2/2) 本編: 実行中の任意のタイミングで生成する場合
// .h UHogeComponent* HogeComponent; void Fuga(); void Piyo();
// .cpp AActor::AActor() { // 比較用: ctor では CreateDefaultSubobject を使った。 RootComponent = CreateDefaultSubobject< USceneComponent >( TEXT( "RootComponent" ) ); } // 実行中の任意のタイミングでコンポーネントを生成 AActor::Fuga() { // 実行中の任意のタイミングで生成する場合は NewObject を使う。 // (ここでは AActor のデフォルトではないオブジェクト生成なので CreateDefaultSubobject は使えない事に注意) HogeComponent = NewObject< UHogeComponent >( this ); // NewObject で生成したオブジェクトは RegisterComponent が必要。 HogeComponent->RegisterComponent(); // ctor 以外でのコンポーネントのアタッチメント(取り付け)も SetupAttachemnt ではなく AttachToComponent を使う // ( SetupAttachment を使っても 4.16 時点では大量の警告とエラーを垂れ流しつつ一応は動作するのでうっかり使わないよう注意) HogeComponent->AttachToComponent( RootComponent, { EAttachmentRule::SnapToTarget, true } ); } // 実行中の任意のタイミングでコンポーネントを破棄 AActor::Piyo() { // RootComponent への Attachement (取り付け)を Detach (解除)する HogeComponent->DetachFromComponent( { EDetachmentRule::KeepRelative, true } ); // RegisterComponent によるコンポーネントの登録を UnregisterComponent で解除する HogeComponent->UnregisterComponent(); // コンポーネントオブジェクトの破棄をGCへ明示する // (ここで突然 DestroyComponent を呼び出すコードを書くとアクセス違反で死ぬことになるので注意) HogeComponent->ConditionalBeginDestroy(); // "立つ鳥跡を濁さず" HogeComponent = nullptr; }
実行中の AActor へ任意のタイミングで USceneComponent (というか実際にはその派生コンポーネントなど)を生成したり、付け足したり、消したり。公式の C++er 向けチュートリアルでも軽く触れるような ctor での生成とは違うポイントがいくつもあるので少し手間取った@w@
参考
- Unreal Engine | UObject インスタンスの作成
- What is the correct way to create and add components at runtime ?
- Explicitely Delete a UObject - UE4 AnswerHub
- その他としてコンパイラーの deprecated 警告、 Debug/Development ビルドの実行中の Warning, Error レベルのログなど。
事後修正
- 2017-06-19T22:20+09:00: 記事中のソース例で
AttachToComponent
をモダンなAPIとしてコードすべきところがAttachTo
になっていて[[deprecated]]
警告を受ける実装を示していたので修正しました。 - 2017-06-21T15:17+09:00: 記事中のソース例で
NewObject
の引数が誤ってTEXT( "Hoge" )
となっていたところをthis
に変更。
UE4/C++ FORCEINLINE にしたいけど Debug ビルドの時は inline 最適化されたくない関数を定義する方法、あるいは UE_BUILD_xxxx シリーズのマクロについて。
UE4/C++ FORCEINLINE
にしたいけど Debug ビルドの時は inline 最適化されたくない関数を定義する方法
#ifndef UE_BUILD_DEBUG FORCEINLINE #endif void hogehoge() { /* fugafuga */ }
と、いうわけで #ifdef
でOK。 Debug ビルドで定義される UE_BUILD_DEBUG
の他に Development ビルドで定義される UE_BUILD_DEVELOPMENT
もあるので用途に応じて使う。
あるいは UE_BUILD_xxxx
シリーズのマクロについて
これらのマクロのシリーズは公式ドキュメント的にはたぶんアンドキュメント。ソースを見るとこのプリフィックスのシリーズは
Engine/Source/Runtime/Core/Public/Misc/Build.h
に、
UE_BUILD_DEBUG
UE_BUILD_DEVELOPMENT
UE_BUILD_TEST
UE_BUILD_SHIPPING
UE_BUILD_SHIPPIING_WITH_EDITOR
UE_BUILD_DOCS
UE_BUILD_FINAL_RELEASE
UE_BUILD_MINIMAL
の存在を UE-4.16 現在、確認できる。
追記: FORCEINLINE_DEBUGGABLE
について
↑までの内容でブログを書いた後もしばらくソースを眺めていると、 FORCEINLINE_DEBUGGABLE
マクロの定義もあることに気付いた。これを使うと、
FORCEINLINE_DEBUGGABLE void hogehoge() { /* fugafuga */ }
こんな具合で簡単化できて嬉しいのかなー・・・と思ったものの、ちょっと違った。冒頭で紹介した #ifdef
の代わりに使ってしまうと FORCEINLINE
ほどではないもののデバッグ実行は実際問題としては難しくなってしまう。
UE_BUILD_xxxx
シリーズや FORCEINLINE_DEBUGGABLE
は Engine/Source/Runtime/Core/Public/Misc/Build.h
で定義されていて、 FORCEINLINE_DEBUGGABLE
は FORCEINLINE_DEBUGGABLE_ACTUAL
へ置換される。それから
Engine/Source/Runtime/Core/Public/HAL/Platform.h
で FORCEINLINE_DEBUGGABLE_ACTUAL
が"未定義"の場合には FORCEINLINE_DEBUGGABLE_ACTUAL
は inline
と定義される。
つまり、そんなこんなで少し入り組んでいるものの、ともかく FORCEINLINE_DEBUGGABLE
を使うと C++ キーワードの inline
と同様の挙動で inline 最適化が指示された関数を定義した事になる。
ちなみに、 FORCEINLINE
は C++ キーワードの inline
ではなくて、コンパイラーの独自拡張によるより強制力の高い inline 関数を指示するキーワードへ置換される UE4 定義のマクロで、例えば cl.exe
(MSVC++) なら __forceinline
に置換されたりする。UE4 が対応する FORCEINLINE
のプラットフォームごとの対応は Engine/Source/Runtime/Core/Public/<PLATFORM>/<PLATFORM>Platform.h
でそれぞれに定義されていて、
置換先 | |
---|---|
Android | __attribute__ ((always_inline)) |
iOS | __attribute__ ((always_inline)) |
Linux | __attribute__ ((always_inline)) |
Mac | __attribute__ ((always_inline)) |
Windows | __forceinline |
こんな感じに定義されている。
参考
- https://github.com/EpicGames/UnrealEngine/blob/4.16/Engine/Source/Runtime/Core/Public/Misc/Build.h
- How can I differentiate between Macros for DebugGame Editor vs Development Editor? - UE4 AnswerHub
追記参考
続・ VSCode で UE4 デバッグの launch.json: DebugGame と Development の切り替え追加と違いについて
概要
前回のポスト「VSCode で UE4/C++: デバッグ実行に必要な launch.json の設定の仕方 - C++ ときどき ごはん、わりとてぃーぶれいく☆」で VSCode の launch.json を設定して UE4 のデバッグ実行を VSCode から"えせ"スタンドアローン風に実行する方法を紹介した。
今回のポストでは UE4/C++ プロジェクトで標準的に用意される DebugGame と Development を切り替える方法と何が違うのかについて追加的に解説したい。
DebugGame と Development の切り替え
前回は launch.json の初手の作成方法の解説として Attach
と Launch
で2つのデバッグ実行モードを VSCode へ設定する方法を示した。今回はこのうちの Launch
を Debug
と Development
の2つに分けて、 Attach
Debug
Development
の3つに分けた launch.json を示す。
{ "version": "0.2.0" , "configurations": [ { "name": "Attach" , "type": "cppvsdbg" , "request": "attach" , "processId": "${command:pickProcess}" } , { "name":"Debug" , "type": "cppvsdbg" , "request": "launch" , "program": "C:/Program Files/Epic Games/UE_4.16/Engine/Binaries/Win64/UE4Editor.exe" , "args": [ "C:/<YOUR-UE4-PROJECT-PATH>/<YOUR-PROJECT-NAME>.uproject", "-debug", "-game" ] , "cwd": "${workspaceRoot}" } , { "name":"Development" , "type": "cppvsdbg" , "request": "launch" , "program": "C:/Program Files/Epic Games/UE_4.16/Engine/Binaries/Win64/UE4Editor.exe" , "args": [ "C:/<YOUR-UE4-PROJECT-PATH>/<YOUR-PROJECT-NAME>.uproject", "-game" ] , "cwd": "${workspaceRoot}" } ] }
Debug
は Development
に対して args
で指示する引数が1つ多く -debug
が追加されている。
DebugGame と Development の違い
-debug
を追加で渡して実行する Debug
モードでは、実行時に <YOUR-UE4-PROJECT-PATH>/Binaries/Win64/<YOUR-PROJECT-NAME>.exe
と一連の .dll の代わりに、 <YOUR-UE4-PROJECT-PATH>/Binaries/Win64/<YOUR-PROJECT-NAME>-Win64-DebugGame.exe
と一連の .dll を用いた実行を試みるようになる。
DebugGame と Development 、2つのコンフィグレーションで実際にどのようにデバッグ性が変化するのか図を交えて紹介する。
DebugGame:
Development:
適当なブレイクポイントで止めてみると、 Debug 版では全ての VARIABLES が丸見えでまさにデバッグ実行と言った風だが、 Development 版では VARIABLES の表示に幾つか Variable is optimized away and not available.
となり扱えない状態で実行されている事がわかる。 Development 版は Debug 版に比べ、デバッガーがまるで役に立たないほどではないものの、あここちでちらほらと最適化によってソースコードと対応の取れない変数や制御が発生している。
通常、 UE4Editor が起動時に読み出す C++ コードの産物は Development コンフィグレーション版だが、 C++er が特に VSCode 等の外部のエディターや開発環境を用いて UE4 開発を行う際には Debug と Development (必要に応じて他に Shipping も)を切り替えながら便利に開発を進捗させる事になる。
参考
VSCode で UE4/C++: デバッグ実行に必要な launch.json の設定の仕方
概要
VSCode で UE4/C++ を開発する際にデバッグ実行に必要な launch.json の設定方法を紹介。
↑こんな感じで VSCode しか起動していない状態から"えせ"スタンドアローン風の実行もできる。もちろん、起動中の UE4Editor へのアタッチもできる。
VSCode の設定の仕方
VSCode で .uproject のあるディレクトリーを開くなどしている状態で:
- 画面左
Debug
アイコンをぽち –> 左側に展開する DEBUG カラムの上部の歯車アイコンをぽち- or メニュー > Debug > Open Configurations
- VSCode
.vscode/launch.json
を次のように編集
{ "version": "0.2.0" , "configurations": [ { "name": "Attach" , "type": "cppvsdbg" , "request": "attach" , "processId": "${command:pickProcess}" } , { "name":"Launch" , "type": "cppvsdbg" , "request": "launch" , "program": "C:/Program Files/Epic Games/UE_4.16/Engine/Binaries/Win64/UE4Editor.exe" , "args": [ "C:/<YOUR UE4 PROJECT PATH>/<YOUR PROJECT NAME>.uproject", "-game" ] , "cwd": "${workspaceRoot}" } ] }
Note: <YOUR UE4 PROJECT PATH>/<YOUR PROJECT NAME>
は各自のUE4プロジェクトの配置パスとプロジェクト名に置き換える。
launch.json
を保存した直後から DEBUG の緑のプレイ的なボタンの右側のコンボボックスに Attach
と Launch
が出現。Launch
で緑のプレイ的なボタンをぽちると"えせ"スタンドアローン風の実行( UE4Editorから Launch
した場合と同様の独立したウィンドウでの起動)が可能。既に起動中の UE4Editor へアタッチしたい場合は VSCode の DEBUG で Attach
を選択した状態で緑のプレイ的なボタンをぽちり、プロセス選択で ue4 くらいまで入力するとインクリメンタルに UE4Editor を選択でき、 ENTER してアタッチできる。
デバッグ中は行番号の左側にブレイクポイントを仕込んで止めたり、ステップ実行したり、ローカル変数、ウォッチ、コールスタック、スレッドの監視、デバッグコンソールなどを活用してバグの原因を確認するなど、よくある便利なGUIデバッガーの必要十分な機能を使える。
VSCode の非常に軽快ながらパワフルな動作はプログラマーにとって憂鬱なムダに浪費されてしまう MOTTAINAI 的なコストを小さく抑えながら快適にソフトウェアの開発やドキュメントの記述が可能で、今回招待したように UE4/C++ 開発についても少しの設定で快適にGUIデバッガーとしても機能。嬉しいですねヽ(´ー`)ノ
参考
曰く「Project.uproject を右クリックして Generate Visual Studio project files」・・・そんなのダルい!今すぐ端末でコマンドを撃たせろ!!って思ったらどうすれば良いか・w・
問題
UE4 C++er は .cpp を追加したりリネームするたびに「Project.uproject を右クリックして Generate Visual Studio project files」しなければならない、事になっている(目につくドキュメントや AnswerHub 的なスタンダードな回答によれば)。
だるい・w・
どうしたいのか?
Windows の「右クリックメニュー」の中身の定義を見る
Win + R
–>regedit
–>Enter
CTRL + F
–>Generate Visual Studio project files
–>Enter
- しばらく検索を待つ –>
Computer\HKEY_CLASSES_ROOT\Unreal.ProjectFile\shell\rungenproj
がヒット - 付近の構造を確認 –>
rungenproj
の下にcommand
があるので値を選択してModify
Value data
がテキストボックスなので値をコピーして後はお好みで使う
手っ取り早い方法としては %1
を実際の Project.uproject のフルパスに置換して1行コマンドを書いただけの generate.bat
をプロジェクトルートにでも入れておき、お好みのシェルなりテキストエディターなりから必要な時にそれを叩けばOK。
その他、そもそもシェル関数にするとか alias 張るとか .lnk ショートカットを作ってタスクバーにピン留めしたりグローバルキーボードショートカットを設定したりなどお好みの応用でどうぞ。
何れにしても↓のGUIが動作して .uproject を右クリックして云々と同様の動作となります。
これでまたストレスが1つ減りました・w・