差分

このページの2つのバージョン間の差分を表示します。

この比較画面にリンクする

次のリビジョン
前のリビジョン
blog:2017:2017-01-20 [2017-01-20 20:53]
Decomo 作成
blog:2017:2017-01-20 [2017-01-24 11:13] (現在)
Decomo
行 1: 行 1:
 ====== FName::GetPlainANSIString()で正しい文字列が取れない事がある ====== ====== FName::GetPlainANSIString()で正しい文字列が取れない事がある ======
  
-Unreal Engine 4軽量文字列クラス''FName''([[https://docs.unrealengine.com/latest/INT/API/Runtime/Core/UObject/FName/index.html|公式リファレンス]])の''GetPlainANSIString''関数/''GetPlainWIDEString''関数は、そのFNameインスタンスが持つ文字列の一部し返さなことある。多分仕様+お急ぎあなたのために、まずは結論。<fc #ff0000>FNameが保持する文字列が必要な時はToString()で生成したFStringを使うべし。</fc>''GetPlainANSIString()''''GetPlainWIDEString()''を使うとハマるら止めとた方いい
  
-実証コード。+Unreal Engine 4の軽量文字列クラス''FName''([[https://docs.unrealengine.com/latest/INT/API/Runtime/Core/UObject/FName/index.html|公式リファレンス]])の''GetPlainANSIString''関数/''GetPlainWIDEString''関数で取得できる文字列ポインタには、そのFNameインスタンスが本来持っている文字列の一部しか入っていない事がある。恐らく仕様。以下が実証コード。
  
 <code c++> <code c++>
行 131: 行 131:
 LogWindows:         isWide=0 LogWindows:         isWide=0
 LogWindows:     Number=0 LogWindows:     Number=0
-LogWindows: abc == ABC 
 </code> </code>
  
-FNameはハッシュ付き文字列として実装されている。文字列はFNameの共用領域に格納され、各FNameインスタンスはその文字列格納領域へのインデックス=ハッシュを保持してる。FNameの比較は互いのハッシュの比較、つまり整数の比較に還元されるため、通常の文字列比較よりいっていう組みなんですな。+FNameはハッシュ付き文字列として実装されている。文字列はFNameの共用領域に格納され、各FNameインスタンスはその文字列格納領域へのインデックス=ハッシュを保持してる。FNameの同士の比較は互いのハッシュの比較、つまり整数の比較に還元されるため、通常の文字列比較よりいって仕掛けなんですな。
  
-このハッシュ生成方法がちょっと曲者で、文字列が「**アンダースコア+ゼロ詰めされていない数値で終わって**いたら、その部分を除いた文字列を共用領域に格納し、数値は各FNameインスタンスで保持するという方法なのだ。割と最近のバージョンアンプで変わったらしい(知人曰く4.12あたりで変わった気がすると)。なかなか破壊的な変更をしてくださりやがるな!+ただ、このハッシュ生成方法がちょっと曲者で、**文字列がアンダースコア+ゼロ詰めされていない数値で終わっていたら、その部分を除いた文字列を共用領域に格納し、数値は各FNameインスタンスで保持する**という方法なのだ。割と最近のバージョンアンプで変わったらしい(知人曰く4.12あたりで変わった気がすると)。なかなか破壊的な変更をしてくださりやがるな!言葉だと分かりにくいので図を作ってみた。
  
-ここでもう一度実行結果を見てみると、条件に合致するHoge_10, Hoge_1200, Hoge_1300, Hoge_999の中身が、生成方法通りになっている事がお分かりいただけよう。''Number''が実際の数値+1された値なのは、これまた仕様で、数値分割条件を満たさない''FName''インスタンス(Number == 0)との区別の為っぽい。+{{ :blog:2017:fname_string_store_mechanism.png |}}
  
 +図を踏まえつつ改めて実行結果を見てみると、条件に合致するHoge_10, Hoge_1200, Hoge_1300, Hoge_999の中身が、その通りになっている事がお分かりいただけよう。''Number''が実際の数値+1されているのは、これまた仕様で、数値分割条件を満たさない''FName''インスタンス(Number == 0)との区別の為っぽい。
 +
 +UE4では生成されたオブジェクトのインスタンスに対し、オブジェクト名+連番の名前を自動付与するため、大規模な開発になるとFName文字列ストアの肥大化が無視できなくなり、保持方法を変更したのだと思われる。
 +
 +こんな格納の仕方で大丈夫なの!?と思うが、ふつーに''FName''を使う分には何の問題もない。や、正確には大丈夫じゃなかったからこの記事書いてる訳だけど、文字列の生ポインタ取ってこねくり回すような事をしなければ大丈夫。FName文字列に対して低レベルな操作を行いたい時は、''ToString()''関数で生成した''FString''に対して行う事をオススメする。エンジンのコードを見てもらうと分かるが、''ComparisonIndex''の文字列にアンダースコアと数値をくっつけて元の文字列を復元してやがるので(笑)
 +
 +ちなみに、実行結果内の''DisplayIndex''というのは、名前の通り表示用の文字列へのインデックス。簡単に言えば、FName生成時に与えられた文字列のインデックスである。FNameは基本的にcase-preserving(内部的には大文字小文字を区別するが対外的には区別せずに扱う)なので、比較用と表示用で別々の文字列を持つ必要がある訳だ。知らずに使ってると、これも地味にハマりポイントかも。
 +
 +<code c++>
 +FName abc(TEXT("abc"));
 +FName ABC(TEXT("ABC"));
 +UE_LOG(LogWindows, Log, TEXT("%s %s %s"), *abc.ToString(), (abc == ABC ? TEXT("==") : TEXT("!=")), *ABC.ToString());
 +</code>
 +
 +念のため上記コードで確認したら、''LogWindows: abc == ABC''となった。
 +
 +掘ってみるとFNameには結構罠があるので注意が必要だ。
  • blog/2017/2017-01-20.1484913225.txt.gz
  • 最終更新: 2017-01-20 20:53
  • by Decomo