C++ ときどき ごはん、わりとてぃーぶれいく☆

USAGI.NETWORKのなかのひとのブログ。主にC++。

Real Unreal Engine C++ 2017-12 (part-4/5)

はてなブログの記事あたりの容量制限のため前の部分 §1.9.4 までは前の記事でどうぞ→http://usagi.hatenablog.jp/entry/2017/12/01/ac_ue4_2_p3

1.9.5 FMath に含まれる型

FMath には3次元の可視空間を扱う上で必要な形状や数値を扱うための型も定義されている。

数が多いので表に概要を整理する:

概要
std::numeric_limits<T>のとてもしょぼい版。Min, Max, Lowest くらいしか実装されていないので std::numeric_limits<T> を使っておけばよい。 TNumericLimits
任意精度整数 TBigInt
浮動小数点数のバイナリー表現を一般化する template 型 TFloatPacker
IEEE754/Binary16 相当の浮動小数点数 FFloat16
ソボル準乱数 FSobal
EUnit で定義される単位間の変換 FNumericUnit
単位変換 FUnitConversion
FUnitConversion が単位の表示名などに使うための補助的な型 FUnitSettings
D3D DirectX::XMVECTORエイリアス VectorRegister
SSEレジスタ__m128dエイリアス VectorRegisterDouble
SSEレジスタ__m128iエイリアス VectorRegisterInt
随意型(オプショナル型)
std::optiona<T>(C++17) や boost::optional<T> 相当だが operator bool, operator* の実装都合、 TOptional<T> は地味に扱いが面倒。 UE4C++17 標準対応完了すれば要らなくなる子。( TArray のようにBlueprintでも使用可能になるなどすればまた別だが・・・)
TOptional
成功値の型と失敗値の型を指定可能な TOptional のごつくなったようなやつ TValueOrError
補間 FInterpCurve
FInterpCurveFloat
FInterpCurveLinearColor
FInterpCurvePoint
FInterpCurveQuat
FInterpCurveTwoVectors
FInterpCurveVector
FInterpCurveVector2D注:FInterpCurveF 始まりの型だが template
球面座標 TSHVector
RGB色用の球面座標 TSHVectorRGB
3次元ベクター FVector
FIntVector
2次元ベクター FVector2D
FIntPoint
半精度浮動小数点数型の2次元ベクター FVector2DHalf
4次元ベクター FVector4
FIntVector4
FUintVector4
線形色
RGBAを値域 [0..1] に正規化した表現
FLinearColor
2 点を保持(するだけ) TInterval
FFloatInterval
FInt32Interval
2 点を保持(多機能) TRange
FAnimatedRange
FDateRange
FDoubleRange
FFloatRange
FInt8Range
FInt16Range
FInt32Range
FInt64Range
Rangeの内外の制御を行う型 TRangeBound
FDateRangeBound
FDoubleRangeBound
FFloatRangeBound
FInt8RangeBound
FInt16RangeBound
FInt32RangeBound
FInt64RangeBound
Range 集合用の template 型 AddContains など集合に対する操作を行える。 TRangeSet
2つの3次元ベクターの対 FTwoVectors
2つのベクターで表される角(かど) FEdge
立方体 FBox
FSphere
同一原点の軸平行直方体(AABB)と球の組み合わせ形状 FBoxSphereBounds
平面 FPlane
カプセル形状 FCapsuleShape
中心と軸方位と軸方位への広がり量を基に任意回転した立方体(の頂点位置)を表現する型 FOrientedBox
矩形 FBox2D
FIntRect
uin32 範囲で行数と列数をそれぞれ任意に template で与えられる行列型 TMatrix
4x4行列 FMatrix
2x2行列 FMatrix2x2
回転変換行列 FRotationMatrix
回転と平行移動の変換行列 FRotationTranslationMatrix
四元数を基にした回転の変換行列 FQuatRotationMatrix
四元数を基にした回転と平行移動の変換行列 FQuatRotationTranslationMatrix
逆回転変換行列 FInverseRotationMatrix
点を中心に回転する変換行列 FRotationAboutPointMatrix
拡大縮小変換行列 FScaleMatrix
拡大縮小と回転と平行移動の変換行列 FScaleRotationTranslationMatrix
変形(拡大縮小、回転、平行移動)変換行列 FTranslationMatrix
視線変換行列 FLookAtMatrix
正射影変換行列 FOrthoMatrix
遠近法変換行列 FPerspectiveMatrix
逆Z軸正射影変換行列 FReversedZOrthoMatrix
逆Z軸遠近法変換行列 FReversedZParspectiveMatrix
鏡像行列
ある平面で鏡像化するための変換行列
FMirrorMatrix
行列を他の表現に変換するための変換器 TransformConverter
オイラー角に基づく回転型 FRotator
四元数による3次元の回転型 FQuat
四元数風の2次元の回転型 FQuat2D
3次元の拡大縮小を表す型 FScale
2次元の拡大縮小を表す型 FScale2D
3次元変形(拡大縮小、回転、平行移動) FTransform
2次元変形(拡大縮小、回転、平行移動) FTransform2D
2次元の剪断 FShear2D
自称「スレッドセーフで低ビットの品質の悪い擬似乱数生成器」
実装を見るにスレッドセーフではないし、線形合同法ベースだし、 C++er は <random> を使っておけばよいので存在を忘れても構わない何か。
FRandomStream
暗号鍵用に整数の指数と剰余を保持する template 型 TEncryptionKey

この節の表を作っただけで力尽きて来たので、面白い小話などはまた別の機会に書く事にする。さしあたり、 C++er は FMath に定義される機能で必要十分な限りにおいては FMath の部品を再実装せず少々の癖はあるが上手く付き合えるよう、どのような機能の関数と型があるのか脳内にインデクシングしておくと良い。

なお、 UE4 の線形幾何ライブラリーの実装レベルは速度最適化の面から言っても、それほど優れた実装にはなっていない。例えば、 Eigen のように template が充実していたり、 expression template により評価を遅延していたり、などという工夫はほとんど無く、わりと単純に float ベースで個々に実装されている。そういうわけで、 "必要十分な限りにおいては" と書いた。それについてもまた本記事の趣旨とは少々ずれるので別の機会があれば書く事にする。

1.10. UE4/C++ における入門的な「コンテナー」ライブラリーの std 互換性

一般的な std 慣れした C++er は UE4 コンテナーのメンバー変数の命名と機能とパラメーターに翻弄される。この節では std に慣れた C++er が UE4/C++ にうっかり翻弄され難くなるよう、予防接種的な視点にも注意しながら UE4 のコンテナー類を整理する。

1.10.1. TArray<T>std::deque<T> / std::vector<T> / std::queue / std::stack

所属 Header
std vector <vector>
std deque <deque>
std stack <stack>
std queue <queue>
UE4 TArray Runtime/Core/Public/Containers/Array.h
UE4 TQueue Runtime/Core/Public/Containers/Queue.h

C++er は std::vector / std::stack / std::queue がコンテナーアダプターであり、実装詳細は std::deque コンテナーである事をよく知っている。また、多くのコンテナー類に対して操作を共通化するため <algorithm> が用意され、ほとんどの操作はイテレーターを介したレンジで扱われる事もよく把握している。

UE4 にはそれらをひとまとめにしたような "便利" なコンテナーとして "マッチョ" な TArray が定義されている。

そこで、この項では std::vector / std::stack / std::queueTArray を対応させ、機能の有無や対応関係がわかりやすいよう整理する。また、 UE4 にはキュー専用に特化した TQueue も定義されているので "ついで" 程度に含める事にする。

なお、「機能を組み合わせれば実現できる」ものは除外し(それは C++er なら脳内でわかること)、直接的にそのように振る舞う機能がある場合にのみ表に関数名を記載する。

効果 std::deque std::vector std::stack std::queue TArray TQueue
[begin..end)の値群に置き換える assign assign
別のコンテナーの値群を追加する Append
operator+=
最前に値を直接生成する emplace_front
最後に値を直接生成する emplace_back emplace_back emplace emplace Emplace
最前に値を入れる push_front
最後に値を入れる push_back push_back push push Add
AddDefaulted
AddUninitialized
AddUnique
AddZeroed
<Push>
Enqueue
最前の値を削除する pop_front pop Pop Pop
最後の値を削除する pop_back pop_back pop
任意の位置へ値を直接生成する EmplaceAt
任意の位置へ値を内挿する insert insert Insert
InsertDefaulted
InsertUninitialized
InsertZeroed
値を全て削除する clear clear Empty
Reset
保持する値の数を変更する resize resize SetNum
SetNumUninitialized
SetNumZeroed
Init
保持する値の数を予約する reserve Reserve
値を範囲で削除する erase erase
値を値またはインデックスで削除する Remove
RemoveAll
RemoveAllSwap
RemoveAt
RemoveAtSwap
RemoveSingle
RemoveSingleSwap
RemoveSwap
保持可能な値の数を実際の保持数に最適化する shrink_to_fit shrink_to_fit Shrink
最前の値を取得する front front front Peek
Dequeue
最後の値を取得する back back top back Last
Top
任意の位置の値を範囲チェック付きで取得する at at
任意の位置の値を取得する operator[] operator[] operator[]
先頭の値のメモリーアドレスを取得 data GetData
値を保持していない事を判定する empty empty empty empty IsEmpty
値の保持数が正常かアサーション( checkSlow )する CheckInvariants
保持する値の数を取得する size size size size Num
保持する値の総容量を取得する CountBytes
内部バッファーの数を取得する capacity
内部バッファーの空き数を取得する GetSlack
内部バッファーのメモリー容量を取得する GetAllocatedSize
値を保持可能な最大数を取得する max_size max_size Max
最前のイテレーターを取得する begin begin CreateIterator
最後のイテレーターを取得する end end
最前のconstイテレーターを取得する cbegin cbegin CreateConstIterator
最後のconstイテレーターを取得する cend cend
最後からの始端のリバースイテレーターを取得する rbegin rbegin
最後からの終端のリバースイテレーターを取得する rend rend
最後からの始端のconstリバースイテレーターを取得する crbegin crbegin
最後からの終端のconstリバースイテレーターを取得する crend crend
別のコンテナーと値群を挿げ替える swap swap swap swap Swap
別のコンテナーと値群のメモリーを挿げ替える SwapMemory
アロケーターを取得する get_allocator get_allocator
有効なインデックスか判定する IsValidIndex
有効なレンジか判定する RangeCheck
アドレスがコンテナーの値か判定する CheckAddress
値のインデックスを取得する IndexOfByKey
IndexOfByPredicate
要素型のメモリー容量を取得する GetTypeSize
値が含まれるか判定する Contains
ContainsByPredicate
最前から値を検索する std::find Find
FindByKey
FindByPredicate
FindItemByClass
最後から値を検索する FindLast
FindLastByPredicate
フィルターしたコンテナーのコピーを取得する std::copy_if FilterByPredicate
非安定ソートする std::sort Sort
安定ソートする std::stable_sort StableSort
シリアライズ結果を取得する BulkSerialize
保持する値群をヒープ構造化する std::make_heap Heapify
保持する値群がヒープ構造化されているか判定する std::is_heap VerifyHeap
ヒープ構造を前提に最前の値を削除する std::pop_heap HeapPop
HeapPopDiscard
ヒープ構造を前提に値を追加する std::push_heap HeapPush
ヒープ構造を前提に任意の位置の値を削除する HeapRemoveAt
ヒープ構造を前提にソートする std::sort_heap HeapSort

この他に大きな相違として、 UE4 では TArray と次節で紹介する TMap 、それと TSet については template であっても例外的に UE4Editor / Blueprint でも直接的な使用がサポートされている点、 UPROPERTY() マクロの指定が可能な点がある。これは一般に C++ コードだけでは完結しない UE4 のプロジェクトの "開発" について、また UObject 派生型を要素型とする必要性において、しばしばコンテナーの実装に求められる重要な要件となる。

std 慣れした C++er が注意を要する TArray を扱う上でのポイントは以下の通り:

  1. deque | vector | stack | queue | SORT | HEAP | FINDTArray
  2. sizeNum
  3. resizeSetNum
  4. reserveReserve
  5. empty()Num() > 0clear()Empty() の混乱回避。
  6. std::remove + erase ( erase-remove idiom ) ≃ Remove
  7. dataGetData
  8. TArrayUPROPERTY() 可能。
  9. std アルゴリズムのレンジは生ポインターでも動作するので TArray にも適用はできる。
  10. std::vector における reservecapacity で扱う内部バッファーを TArray では "Slack" (スラック)と用語を充てている。

1.10.2. TMap<K,V>std::unordered_map<K,V>

所属 Header
std unordered_map <unordered_map>
UE4 TMap Runtime/Core/Public/Containers/Array.h

std の解説一般でもしばしば同じ節で扱われるように unordered_map にはよく似た unordered_set があり、 UE4 にもそれらと同じ関係の TMapTSet がある。この記事は網羅的なクラスや API の紹介が趣旨ではないため(&そろそろアドベンカレンダーの期日も近づきすぎている都合もあり…)、冗長を避ける意味でも unordered_map vs. TMap のみを取り上げる。

効果 std::unordered_map TMap TMap のメンバーの継承元
別のコンテナーの値群を追加する Append
operator+=
TMap
キーと値の組みを直接生成する emplace Emplace TMapBase
キーと値の組みを挿入位置のヒント付きで直接生成する emplace_hint
キーと値の組みを入れる insert Add TMapBase
キーの値を取得または生成して取得する operator[] FindRef TMapBase
キーの値を取得する at FindChecked
operator[]
TMapBase
TMap
キーの値を取得または生成してポインターを取得する FindOrAdd TMapBase
キーの値のポインターを取得する Find TMapBase
値のキーのポインターを取得する FindKey TMapBase
キーの組みのイテレーターを取得する find
equal_range
キーの組みを削除しつつ値を取得する FindAndRemoveChecked TMap
キーの組みの有無を確認して存在すれば削除しつつ値を取得する RemoveAndCopyValue TMap
キーの組みを削除する erase Remove TMapBase
組みを全て削除する clear Empty
Reset
TMapBase
内部バッファーの数を予約する reserve Reserve TMapBase
内部バッファーのバケットの最小数を指定してバケット数を調整する rehash
内部バッファーのバケットの負荷率の最大値を設定または取得する max_load_factor
内部バッファーのバゲットの負荷率を取得 load_factor
内部バッファーの末尾の未使用領域を削除する Shrink TMapBase
内部バッファーの未使用領域を削除する Compact TMapBase
内部バッファーの未使用領域を使用領域の順序を保持して削除する CompactStable TMapBase
組みの順序をキーでソートする KeySort TSortableMapBase
組みの順序を値でソートする ValueSort TSortableMapBase
組みへのconstイテレーターを取得する cbegin CreateConstIterator TMapBase
組みの終端へのconstイテレーターを取得する cend
キーへのconstイテレーターを取得する CreateConstKeyIterator TMapBase
組みへのイテレーターを取得する begin CreateIterator TMapBase
組みの終端へのイテレーターを取得する end
キーへのイテレーターを取得する CreateKeyIterator TMapBase
キーの配列と数を取得する GetKeys TMapBase
組みの数を取得する size Num TMapBase
内部バッファーの数を取得する bucket_count GetAllocatedSize TMapBase
内部バッファーの最大数を取得する max_size
bucket_size
キーが存在するか判定する count Contains TMapBase
保持する組みが空か判定する empty
キー群の配列を取得する GenerateKeyArray TMapBase
値群の配列を取得する GenerateValueArray TMapBase
内部バッファーを出力デバイス(ログ的なやつ)へ書き出す Dump TMapBase
シリアライズに要するメモリー容量を取得する CountBytes TMapBase
アロケーターを取得する get_allocator
要素を挿げ替える swap
ハッシュ関数オブジェクトを取得する hash_function
キー比較用オブジェクトを取得する key_eq

std::unordered_map に比べ TMap ではキー、値それぞれに着目した操作や列挙の実装が充実している。 std 慣れした C++er が留意すべき TMap のポイントは次の通り:

  1. unordered_mapoperator[] は存在しない要素は生成してくれるが、 TMapoperator[] は存在しない要素は生成してくれない(実行時エラーとなる)。その用途には FindRef を使う。
  2. TMap は KEY, VALUE によるソートが可能。
  3. メンバーの命名 → TArray と同様の注意が必要。
  4. UPROPERTY() 可能。
  5. インデックスアクセスも可能な連想配列として振る舞う(もちろん表面上は似てはいるが std::unordered_map の一般的な実装と TMap の実装詳細はまったく異なる)

1.10.3. その他の UE4 コンテナーライブラリー

他の std に対応する UE4 コンテナーは以下の通り:

概要 std UE4
ビット列コンテナー std::vector<bool> TBitArray
単方向リスト std::forward_list<T> TList<T>
ソート済み連想コンテナー std::map<K,V> TSortedMap<K,V>
ハッシュ集合 std::unordered_set<T> TSet
優先度付きキュー std::priority_queue<T> FBinaryHeap<K,I>

また、 他にも UE4 には std には無いコンテナー類も存在します。そのうちの一部は boost には同種のコンテナーが存在する。

概要 boost UE4
既存の生配列を参照してサイズ変更を除く TArray<T> と同等の操作を可能にするビュー TArrayView
循環バッファー boost::circular_buffer<T> TCircularBuffer<T>
ロックフリー循環キュー boost::lockfree:queue TCircularQueue<T>
ロックフリーポインターリスト FLockFreePointerListFIFO
FLockFreePointerListLIFORoot
疎配列 Boost.SparseVector SparseArray<T>
バリアント集合 TUnion<A,B,C,D,E,F>

何れも前項までに紹介したコンテナー類よろしく std 慣れした C++er の脳を引っ掻き回すような命名規則や引数はおおよそ共通。また、一部には EPIC GAMES 以外が主体的に開発したソースも含まれさらなる混沌感も少々ある。

何れも使い所次第では単に TArray<T>TMap<T> あるいは TSet<T>、それに何らかの工夫を加えて使うよりも高効率であったり便利であったりはするものの、 UE4Editor / Blueprint からは使用不能。必要が生じたら使える程度に軽く脳にインデクシングしておけば善いでしょう。

はてなブログの記事あたりの容量制限のため続き §1.11. 以降は次の記事でどうぞ→http://usagi.hatenablog.jp/entry/2017/12/01/ac_ue4_2_p5