Theolizer  Version.1.2.0
serializer for C++ / Do you want to update your classes easily ?
7c.object_tracking.h
[詳解]
1 //############################################################################
2 /*!
3  @brief ドキュメント・ファイル-使用方法(個別)
4  @ingroup Documents
5  @file 7c.object_tracking.h
6  @author Yoshinori Tahara
7  @date 2017/01/06 Created
8 */
9 /*
10  © 2016 Theoride Technology (http://theolizer.com/) All Rights Reserved.
11  "Theolizer" is a registered trademark of Theoride Technology.
12 
13  "Theolizer" License
14  In the case where you are in possession of a valid “Theolizer” License,
15  you may use this file in accordance with the terms and conditions of
16  the use license determined by Theoride Technology.
17 
18  General Public License Version 3 ("GPLv3")
19  You may use this file in accordance with the terms and conditions of
20  GPLv3 published by Free Software Foundation.
21  Please confirm the contents of GPLv3 at https://www.gnu.org/licenses/gpl.txt .
22  A copy of GPLv3 is also saved in a LICENSE.TXT file.
23 
24  商用ライセンス
25  あなたが有効なTheolizer商用ライセンスを保持している場合、
26  セオライド テクノロジーの定める使用許諾書の条件に従って、
27  このファイルを取り扱うことができます。
28 
29  General Public License Version 3(以下GPLv3)
30  Free Software Foundationが公表するGPLv3の使用条件に従って、
31  あなたはこのファイルを取り扱うことができます。
32  GPLv3の内容を https://www.gnu.org/licenses/gpl.txt にて確認して下さい。
33  またGPLv3のコピーをLICENSE.TXTファイルにおいてます。
34 */
35 //############################################################################
36 
37 /*!
38  @page ObjectTracking オブジェクト追跡について
39 ここでは、オブジェクト追跡について説明します。
40 
41 <br>
42 //############################################################################
43 @section HowToObjectTracking 1.オブジェクト追跡の使い方
44 //############################################################################
45 
46 <br>
47 @subsection HowToObjectTracking11 1-1.指定方法
48 <b>・ポインタ型の指定(通常通りのシリアライズ指定です)</b>
49 
50 1. メンバ変数の場合は、通常通り保存指定<br>
51 2. THEOLIZER_PROCESS()マクロ<br>
52 
53 <b>・オーナー・ポインタ型の指定(オーナー指定)</b>
54 
55 1. メンバ変数の場合は、THEOLIZER_ANNOTATE(FS:...<...>Owner)<br>
56 このアノテーションはポインタに対してのみ指定できます。<br>
57 2. THEOLIZER_PROCESS_OWNER()マクロ<br>
58 このマクロはボインタのみ指定できます。<br>
59 
60 また、回復時は初期値が必要です。nullptr、もしくは、適正なインスタンスへのポインタを設定して下さい。ポイント先インスタンスのクラスとシリアライズ・データ内のクラスが同じならば設定されていたインスタンスを維持したまま回復します。(これは保存先指定機能により複数のファイルに別々に保存されたメンバを統合するための機能です。) もし、クラスが異なる場合は解放し、シリアライズ・データと同じクラスをnewして回復します。nullptrの時も同様です。
61 
62 <b>・オブジェクト追跡指定</b>
63 
64 1. メンバ変数の場合は、THEOLIZER_ANNOTATE(FS:...<...>Pointee)
65 2. THEOLIZER_PROCESS_POINTEE()マクロ
66 
67 @subsection HowToObjectTracking12 1-2.サンプル・ソース
68 
69 <b>サンプル用のクラス定義:(source/reference_and_test/basic2/test_object_tracking.h)</b><br>
70 (静的定義領域、動的生成領域については@ref HowToObjectTracking33 を参照下さい。)
71 
72 @dontinclude basic2/test_object_tracking.h
73 @skip ObjectTrackingClass
74 @until };
75 
76 ObjectTrackingClass全体はオブジェクト追跡するクラスのサンプルです。また、メンバ変数はTHEOLIZER_ANNOTATE()マクロによるオブジェクト追跡関連指定のサンプルです。<br>
77 - mIntメンバ変数はオブジェクト追跡するよう指定されたメンバ変数です。
78 - mShortメンバ変数はオーナー指定されたメンバ変数です。
79 
80 <b>保存処理:(source/reference_and_test/basic2/test_object_tracking.cpp)</b>
81 
82 @dontinclude basic2/test_object_tracking.cpp
83 @skip "tutoriseObjectTracking() start"
84 @skip {
85 @until aSerializer.clearTracking();
86 @until }
87 
88 <div style="padding: 10px; margin-bottom: 10px; border: 1px solid #333333; border-radius: 10px; background-color: #d0d0d0;">
89 <b>makeAutoRelease()</b><br>
90 これはnewで獲得した領域を自動的にdeleteするためのヘルパー関数です。戻り値のインスタンスが解放されるタイミングでdeleteします。source/reference_and_test/basic/common.hで定義しています。たいへん小さいですので、興味のある方は覗いてみて下さい。
91 </div>
92 
93 これにより、下図のようなデータ構造が生成され、図の順序でデータが保存されます。
94 
95 @image html object_tracking1.png
96 
97 <b>tutorise_object_tracking.json</b>ファイルは下記となります。
98 (//以下は説明のために書き込みました。)
99 
100  {
101  "SerialzierName":"JsonTheolizer",
102  "GlobalVersionNo":1,
103  "TypeInfoList":[1]
104  }
105  1 // オブジェクトID=1へのポインタ(aLongPtr)
106  2 // オブジェクトID=2へのポインタ(aObjectTrackingClassPtr)
107  [1,100] // オブジェクトID=1のインスタンス(aLong)
108  [2,{ // オブジェクトID=2のインスタンス(aObjectTrackingClass)
109  "mInt":[3,200], // オブジェクトID=3のインスタンス(mInt)
110  "mShort":[4,300] // オブジェクトID=4のインスタンス(mShort)
111  }]
112  [5,101] // オブジェクトID=5のインスタンス(aLongOwner)
113  [6,"ObjectTrackingClass",{ // オブジェクトID=6のインスタンス(aObjectTrackingOwner)
114  "mInt":[7,201], // オブジェクトID=7のインスタンス(mInt)
115  "mShort":[8,301] // オブジェクトID=8のインスタンス(mShort)
116  }]
117  5 // オブジェクトID=5へのポインタ(aLongPtr2)
118  6 // オブジェクトID=6へのポインタ(aObjectTrackingClassPtr2)
119  7 // オブジェクトID=7へのポインタ(aIntPtr)
120  8 // オブジェクトID=8へのポインタ(aShortPtr)
121 
122 <b>回復処理:(source/reference_and_test/basic2/test_object_tracking.cpp)</b><br>
123 元のデータ構造を回復できていることをチェックしています。
124 
125 @skip 回復
126 @skip {
127 @until aSerializer.clearTracking();
128 @until }
129 
130 
131 <br>
132 //############################################################################
133 @section Polymorphism 2.ポリモーフィズムの使い方
134 //############################################################################
135 
136 @subsection Polymorphism31 3-1.使い方
137 
138 ポリモーフィックな基底クラスへのポインタをオーナ・ポインタとしてシリアライズすることで、ポリモーフィックに回復されます。つまり、派生クラスのインスタンスを基底クラスへのポインタでポイントして保存して回復した場合、派生クラスのインスタンスとして回復されます。
139 
140 そのように動作させるために、2つの注意点があります。
141 
142 1. 基底クラスは仮想関数を最低1つ持つ必要が有ります。<br>
143 これがないと、C++の仕様上、基底クラスへのポインタが指す派生クラスのインスタンスの型を動的に判定できないためです。<br>
144 <br>
145 
146 2. 派生クラスはTHEOLIZER_REGISTER_CLASS()マクロで派生クラスであることを指定して下さい。<br>
147 基底クラスへのポインタ経由でシリアライズしたい派生クラスは必ず指定して下さい。<br>
148 指定漏れすると、シリアライズ処理する時に"Can not find the derived class for <基底クラス>."エラーになります。<br>
149 
150 また、@ref Basic164 も参照下さい。
151 
152 <b>サンプル用のクラス定義:(source/reference_and_test/basic2/test_polymorphism.h)</b>
153 
154 @dontinclude basic2/test_polymorphism.h
155 @skip //-
156 @until THEOLIZER_REGISTER_CLASS((PolyDerived1));
157 
158 <b>THEOLIZER_REGISTER_CLASS()</b>による派生クラスの指定を忘れないようにお願いします。<br>
159 また、クラス名を()で囲って指定する必要が有りますのでご注意下さい。
160 
161 <b>保存処理:(source/reference_and_test/basic2/test_polymorphism.cpp)</b>
162 
163 @dontinclude basic2/test_polymorphism.cpp
164 @skip "tutorisePolymorphism() start"
165 @skip {
166 @until aSerializer.clearTracking();
167 @until }
168 
169 <b>回復処理:(source/reference_and_test/basic2/test_polymorphism.cpp)</b><br>
170 元のデータ構造を回復できていることをチェックしています。
171 
172 @skip 回復
173 @skip {
174 @until aSerializer.clearTracking();
175 @until }
176 
177 <b>tutorise_object_tracking.json</b>ファイルは下記となります。
178 
179  {
180  "SerialzierName":"JsonTheolizer",
181  "GlobalVersionNo":1,
182  "TypeInfoList":[1]
183  }
184  [1,"PolyDerived0",{
185  "(PolyBase)":{
186  "mInt":100
187  },
188  "mShort":200
189  }]
190  [2,"PolyDerived1",{
191  "(PolyBase)":{
192  "mInt":1000
193  },
194  "mString":"string"
195  }]
196 
197 メンバ変数名は"メンバ変数名"として記録されます。基底クラスにはメンバ変数名がないため、"{基底クラス名}"として記録しています。これにより、基底クラスの定義順序変更にも対応できます。
198 
199 @subsection Polymorphism32 3-2.参照を経由する場合
200 
201 ポリモーフィックな基底クラスへの参照をシリアライズすることで、ポリモーフィックに回復されます。つまり、派生クラスのインスタンスを基底クラス型の参照でポイントして保存し回復した場合、派生クラスのインスタンスとして回復されます。
202 
203 <br>
204 //############################################################################
205 @section ClearTracking 3.オブジェクト追跡の仕組み
206 //############################################################################
207 
208 @subsection HowToObjectTracking31 3-1.ポインタをシリアライズする仕組み
209 Theolizerはboost::serializationと同様にポインタをファイルへ保存し、そのファイルを回復した時、元のポイント構造を回復できる仕組みを実装しています。
210 
211 下記のようなオブジェクト追跡処理により実現しています。
212 
213 <b>・ポインタの保存</b><br>
214 1. オブジェクトIDテーブル<br>
215 インスタンスを保存する時に、インスタンスの先頭アドレスに対してオブジェクトIDを割り当て、インスタンスの先頭アドレスとオブジェクトIDの対応表を生成します。<br>
216 
217 2. ポインタを保存<br>
218 ポインタの指すアドレスに対応するオブジェクトIDをオブジェクトIDテーブルで求め、オブジェクトIDを保存します。
219 
220 <b>・ポインタの回復</b><br>
221 1. オブジェクトIDテーブル<br>
222 インスタンスを回復する時に、オブジェクトIDに対応するインスタンスの先頭アドレスを記録します。
223 
224 2. ポインタを回復<br>
225 オブジェクトIDを読み出し、それに対応するインスタンスの先頭アドレスをオブジェクトIDテーブルで求め、インスタンスの先頭アドレスをポインタに設定します。
226 
227 インスタンスは任意の「型」のインスタンスですが、これが構造体の場合、構造体全体の先頭アドレスと構造体の先頭メンバの先頭アドレスは一致します。(クラスの場合も同様です。)これらを纏めて1つのオブジェクトIDとすると適切に回復できないため、インスタンスの先頭アドレスだけでなく「型」も一緒に記録し、アドレスと型に対してオブジェクトIDを割り当てています。
228 
229 <br>
230 @subsection HowToObjectTracking32 3-2.オブジェクト追跡の課題
231 ポインタを保存する前に必ずそのポインタが指すインスタンスを保存できれば特に問題はないのですが、そうとは限りません。<br>
232 
233 ポインタの指すインスタンスが例えばローカル変数で、それがポインタより後でシリアライズ指示される場合もあります。<br>
234 そのようなデータ構造を回復するためには、そのローカル変数の保存は最初にポインタをシリアライズ指示された時ではなく、ローカル変数のシリアライズ指示まで遅らせるべきです。そうしないと順序よく回復できないため回復手順が複雑で負荷が高いものになります。<br>
235 
236 しかし、ポインタが指している領域の所有権を当該ポインタが持っている場合は、最初にポインタをシリアライズ指示された時にインスタンスを保存すればOKです。最初にポインタを回復する際にインスタンス領域を確保して回復すれば適切にポイント構造を回復できますから。(*1)<br>
237 
238 従って、ポインタがポイント先の所有権を持っているかどうかを判定できれば適切に保存/回復できるのですが、残念ながらシリアライズしようとしているポインタが所有権のある領域を指しているのか、そうでない領域を指しているのか判定する仕組みはC++言語仕様にはありません。
239 
240 そこで、下記2つの選択肢を検討しました。
241 
242 1. boost::serializationのようにこのようなケースをエラーとする(*2)<br>
243 この場合ローカル変数のようなインスタンスはそれをポイントするポインタより先に保存する必要が有ります。できれば避けたい制約と思います。
244 
245 2. ポインタの属性として所有権の有無をプログラマが指定する
246 保存順序の制約はなく、処理負荷も特に高くはなりません。その代わりプログラマに少し負担がかかります。<br>
247 
248 さて、インスタンスの所有権を持つか持たないかをポインタの属性とすることはリソース・リークし難いプログラムになりますのでたいへん好ましいと考えます。実際、C++11のスマート・ポインタは正にそのような概念に基づいています。スマート・ポインタが管理する領域はスマート・ポインタが所有権を持ち、その他のポインタは所有権を持ちません。所有権が明確ですのでメモリ・リークのリスクを大きく低減できます。<br>
249 
250 そこで、インスタンスの所有権の有無をポインタの属性とする仕組みを実装することで、この問題を回避することにしました。<br>
251 
252 <div style="padding: 10px; margin-bottom: 10px; border: 1px solid #333333; border-radius: 10px; background-color: #d0d0d0;">
253 (*1)ポインタが指すオブジェクトをオブジェクト追跡単位の最後で保存する案も検討しました<br>
254 途中でボイントされているローカル変数がシリアライズされたら、その時点で保存すれば良いです。制約も少なくプログラマへの負荷も小さいように見えます。<br>
255 しかし、最後までポイント先のインスタンスがシリアライズされなかった時、ユーザ・プログラムにおけるシリアライズ漏れ不具合なのか、所有権のある領域なのかの判断がつきません。前者の不具合は発見が遅れるとかなり痛いですので、この案は棄却しました。
256 </div>
257 
258 <div style="padding: 10px; margin-bottom: 10px; border: 1px solid #333333; border-radius: 10px; background-color: #d0d0d0;">
259 (*2)boostの場合<br>
260 boost::serializationはこのような使い方を許可していません。上記ケースではローカル変数を保存する時にpointer_conflict例外が投げられます。<br>
261 ["Pointer Conflict" Errors in the Boost Serialization Library](http://www.bnikolic.co.uk/blog/cpp-boost-ser-conflict.html)<br>
262 [Boost serialization with pointers](http://stackoverflow.com/questions/37747596/boost-serialization-with-pointers)
263 </div>
264 
265 <br>
266 @subsection HowToObjectTracking33 3-3.オブジェクト追跡で用いる用語
267 このために、少し用語を定義しました。
268 
269 1. 静的定義領域<br>
270 ポインタが所有権(獲得/解放する権限)を持つことができない領域です。下記があります。
271  - グローバル変数
272  - ローカル変数
273  - 他のクラスのメンバ変数(非ボインタ)
274  これは、例えばstruct Foo { int mInt; };のmIntです。ポインタ側からmIntを獲得/解放することができません。
275 
276 2. 動的生成領域(*3)<br>
277 newやnew[]で獲得するインスタンスです。
278 
279 3. ポインタ型<br>
280 通常のポインタです。これはポイント先のインスタンスの所有権を持ちません。<br>
281 静的定義領域、動的生成領域の両方をボイントすることができませ。<br>
282 
283 4. オーナー・ポインタ型<br>
284 ポイント先のインスタンスの所有権を持っているポインタです。<br>
285 当然ですが動的生成領域のみポイント可能です。<br>
286 
287 <div style="padding: 10px; margin-bottom: 10px; border: 1px solid #333333; border-radius: 10px; background-color: #d0d0d0;">
288 (*3)「静的定義領域」と言う名称<br>
289 以前、teratailで「[インスタンス生成方法の呼び方について](https://teratail.com/questions/20206)」質問し、catsforepawさんの回答を元に決定しました。<br>
290 遠いところからですが、catsforepawさん、その節はありがとうございました。
291 </div>
292 
293 <br>
294 @subsection HowToObjectTracking34 3-4.オブジェクト追跡対象について
295 静的定義領域は、全てのシリアライズ対象の変数がオブジェクト追跡候補になります。しかし、実際にポインタでポイントされる変数はその内の一部だけですので、全てをオブジェクト追跡するのは無駄が多いです。そこで、@ref Basic21 で示した方法でオブジェクト追跡対象を絞り込んでいます。
296 
297 <br>
298 @subsection HowToObjectTracking35 3-5.オブジェクト追跡単位について
299 @ref Basic22 に示したオブジェクト追跡単位について少し詳しく説明します。
300 
301 これはオブジェクトIDテーブルの有効期間です。clearTracking()することで
302 
303 1. オブジェクトIDテーブルにシリアライズされていないインスタンスが登録されていないか確認します
304 2. オブジェクトIDテーブルをクリアします
305 
306 前者により、ユーザ・プログラムのバグ検出をサポートします。<br>
307 後者により、不要なオブジェクト追跡を解除できるのでバフォーマンスを改善できます。<br>
308 
309 <div style="padding: 10px; margin-bottom: 10px; border: 1px solid #333333; border-radius: 10px; background-color: #d0d0d0;">
310 <b>注意事項1:</b><br>
311 解放したインスタンスを追跡したままにしていると未定義メモリ・アクセスが発生する場合があります。ですので、clearTracking()するまで、もしくは、シリアライザ自身を破棄するまで下記インスタンスを破棄しないで下さい。<br>
312 ・シリアライズしたインスタンス<br>
313 ・シリアライズしたポインタが指すインスタンス<br>
314 なお、clearTracking()することで「シリアライズした」と言う記録が全て破棄されますので、必要であれば、cleartTracking()後、最初のシリアライズまでの間ならばインスタンスを安全に破棄できます。
315 </div>
316 
317 <div style="padding: 10px; margin-bottom: 10px; border: 1px solid #333333; border-radius: 10px; background-color: #d0d0d0;">
318 <b>注意事項2:</b><br>
319 clearTracking()により、未解決なオブジェクト追跡(ポインタが登録されたがポインタの指すインスタンスが保存/回復されていない)の有無を確認します。もし、未解決なオブジェクト追跡がある時、WrongUsingエラーを通知します。(「@ref ErrorReport 」参照)<br>
320 なお、オブジェクト追跡を行い、かつ、clearTracking()を呼び出していない場合、シリアライザ・インスタンスのデストラクタでプログラムを異常終了させています。オブジェクト追跡する場合は必ずデストラクトする前にclearTracking()を呼び出すようにして下さい。</div>
321 
322 <br>
323 //############################################################################
324 @section TestObjectTracking 4.オブジェクト追跡の網羅的な使用例(自動テスト)の説明
325 //############################################################################
326 
327 @subsection TestObjectTracking41 4-1.各種メモリへのポインタのテスト
328 @subsubsection TestObjectTracking411 4-1-1.概要
329 
330 ここでは、C++で用いられる少し異なるメモリ上のインスタンスへのポインタが適切に回復されることをテストしています。具体的には下記の通りです。
331 
332 1. グローバル変数<br>
333 ここには静的定義領域のみ存在できますので、ここへのポインタをオーナー指定できません。<br>
334 
335 2. ローカル変数(スタック変数)<br>
336 ここも静的定義領域のみ存在できますので、ここへのポインタをオーナー指定できません。<br>
337 
338 3. ヒープ変数<br>
339 ここには動的生成領域と静的定義領域の両方が存在できます。newで獲得したクラスのインスタンス全体は動的生成領域です。そのクラスの各メンバ変数は静的定義領域となります。基底クラスは少し複雑です。<br>
340 
341  - ポリモーフィックな(仮想関数がある)クラスの場合<br>
342  基底クラスへのポインタ経由でdeleteできますので、基底クラスへのポインタをオーナー指定できます。<br>
343 <br>
344 
345  - 非ポリモーフィックな(仮想関数がない)クラスの場合<br>
346  C++仕様上、基底クラスへのポインタ経由でdeleteすると派生クラスのデストラクタは呼ばれず基底クラスとしてデストラクトされます。従って、非ポリモーフィックなクラスの場合、その基底クラスへのポインタをオーナー指定してはいけません。<br>
347 
348 下記組み合わせのインスタンスとポインタ(非オーナー)について保存/回復テストを行います。<br>
349 
350  静的定義領域(グローバル変数, ローカル変数, メンバ変数) + 動的生成領域(プリミティブ, クラス)
351  ×
352  Pointee指定プリミティブ型(long) + Pointee指定なしクラス型(ObjectTracking0) + Pointee指定クラス型(ObjectTracking1)
353 
354 また、2番目以降の基底クラス・ポインタは一般にインスタンス先頭ではないアドレスを指しますが、このケースにも対応できていることを確認します。
355 そのために、派生クラスのインスタンス、および、その2番目の基底クラスへのポインタをシリアライズし、ポインタが回復したインスンタスをポイントすることを確認しています。
356 
357 更に、動的生成領域(クラス)へのポインタ(非オーナー)のテストを実施する際に、std::shared_ptr<>を非侵入型手動対応しました。その時、[std::shared_ptr<>対応は意外に難しい](https://github.com/yossi-tahara/Theolizer/issues/18)ことが分かり、いくつか複雑な対応を行いましたので、ここでその一部のテストも行っています。(後日、標準コンテナ対応する予定です。その時、網羅的な自動テストを実装します。)
358 
359 @subsubsection TestObjectTracking412 4-1-2.ソース・コード
360 
361 <b>source/reference_and_test/basic2/test_object_tracking.h</b>でテスト用のクラスを定義してます。<br>
362 
363 1. 2番目以降の基底クラスへのポインタの回復テスト用
364  - ObjectTrackingBase0 1番目の基底クラス
365  - ObjectTrackingBase1 2番目の基底クラス
366  - ObjectTrackingDerived 上記2つを継承したクラス<br>
367 <br>
368 
369 2. 各種メモリへのポインタのテスト用
370  - ObjectTracking0 被ポイント指定(Pointee)なし
371  - ObjectTracking1 被ポイント指定(Pointee)あり
372  - StaticDefinition メンバ変数静的定義領域用
373  - Pointers 自動シリアライズするポインタの定義<br>
374 <br>
375 
376 3. 同じインスタンスを複数回シリアライズした時のテスト用
377 上記で定義したObjectTrackingDerivedを用います。
378 
379 <b>source/reference_and_test/basic2/test_object_tracking2.cpp</b>でテスト関数を定義してます。<br>
380 
381 1. 保存処理<br>
382 template<class tSerializer><br>
383 void saveObjectTracking(tSerializer& iSerializer)の前半<br>
384 <br>
385 
386 2. 回復処理<br>
387 template<class tSerializer><br>
388 void loadObjectTracking(tSerializer& iSerializer)の前半<br>
389 
390 
391 @subsection TestObjectTracking42 4-2.ポインタ(非オーナー)のテスト
392 @subsubsection TestObjectTracking421 4-2-1.概要
393 ポインタのシリアライズ、および、ポイント先のインスタンスのシリアライズについて、指定方法が手動(トップ・レベル)、自動シリアライズ(自動型のメンバ変数)、手動(非トップ・レベル)の3種類あります。それぞれについて各種の型、および、ポインタ→インスタンスとインスタンス→ポインタの順序おのおのの組み合わせのテストを行います。
394 
395  ポインタ:
396   手動(トップ・レベル) + 自動シリアライズ(自動型のメンバ変数) + 手動(非トップ・レベル)
397  ×
398  インスタンス:
399   手動(トップ・レベル) + 自動シリアライズ(自動型のメンバ変数) + 手動(非トップ・レベル)
400  ×
401  全てのプリミティブ + enum型 + scoped num型 + クラス + 各配列型
402  ×
403  ポインタを先に保存 + インスタンスを先に保存
404 
405 @subsubsection TestObjectTracking422 4-2-2.ソース・コード
406 
407 <b>source/reference_and_test/basic2/test_object_tracking.h</b>でテスト用のクラスとマクロを定義してます。<br>
408 
409 1. DEFINE_MEMBERS()マクロ<br>
410 各種の型に対応する、変数宣言や初期化を定義するためのマクロです。@ref SingleTest と同じ名前ですが、少し異なるマクロです。定義している変数の型は同じですが、初期化値が異なります。<br>
411 <br>
412 
413 2. PointeeListクラス<br>
414 静的定義領域の定義です。各型のインスタンスについて、自動シリアライズと手動(トップ・レベル)を担います。前者は非侵入型完全自動として処理し、後者はsavePointee()とloadPointee()関数で行います。<br>
415 <br>
416 
417 3. PointeeListManualクラス<br>
418 静的定義領域の定義です。各型のインスタンスについて、手動(非トップ・レベル)を担います。<br>
419 <br>
420 
421 4. PointerListクラス<br>
422 ポインタ(非オーナー)の定義です。各型のポインタについて、自動シリアライズと手動(トップ・レベル)を担います。前者は非侵入型完全自動として処理し、後者はsavePointer()とloadPointer()関数で行います。トップ・レベル、および、自動型については、例えばデバッグやログ用にconst領域へのポインタの保存を想定して、constポインタも定義しています。<br>
423 <br>
424 
425 5. PointerListManual<br>
426 ポインタ(非オーナー)の定義です。各型のポインタについて、手動(非トップ・レベル)を担います。回復処理を行わない手動型の実装は想定不要と考えますので、constポインタを定義していません。<br>
427 <br>
428 
429 <b>source/reference_and_test/basic2/test_object_tracking2.cpp</b>でテスト関数を定義してます。<br>
430 
431 1. 保存処理<br>
432 template<class tSerializer><br>
433 void saveObjectTracking(tSerializer& iSerializer)の後半<br>
434 <br>
435 
436 2. 回復処理<br>
437 template<class tSerializer><br>
438 void loadObjectTracking(tSerializer& iSerializer)の後半<br>
439 
440 
441 @subsection TestObjectTracking43 4-3.オーナー・ポインタのテスト
442 @subsubsection TestObjectTracking431 4-3-1.概要
443 オーナー・ポインタのシリアライズについて、指定方法が手動(トップ・レベル)、自動シリアライズ(自動型のメンバ変数)、手動(非トップ・レベル)の3種類あります。それぞれについて各種の型の組み合わせのテストを行います。また、nullptrの回復もテストします。<br>
444 (ポインタ(非オーナー)と異なり、インスタンス側はポインタが管理するため組み合わせが発生しません。また、インスタンスの保存は先に出現した方が先ですので、保存順序の組み合わせも発生しません。)
445 
446  オーナー・ポインタ:
447   手動(トップ・レベル) + 自動シリアライズ(自動型のメンバ変数) + 手動(非トップ・レベル)
448  ×
449  全てのプリミティブ + enum型 + scoped num型 + クラス + 各配列型
450 
451 @subsubsection TestObjectTracking432 4-3-2.ソース・コード
452 
453 <b>source/reference_and_test/basic2/test_object_tracking.h</b>でテスト用のクラスとマクロを定義してます。<br>
454 
455 1. DEFINE_MEMBERS()マクロ<br>
456 ポインタ(非オーナー)と共通です。<br>
457 <br>
458 
459 2. NEW_ARRAY()マクロ<br>
460 C++は、配列型(Type[N])をnewした結果は残念なことに配列型へのポインタ(Type(*)[N])にならず要素へのポインタ(Type*)になります。そのため、配列型へのポインタ(Type(*)[N])変数へそのままでは代入できません。これをreinterpret_cast<Type(*)[N]>()して(*4)代入できるようにするマクロです。<br>
461 <br>
462 
463 3. OwnerListクラス<br>
464 オーナー・ポインタの定義です。各型のポインタについて、自動シリアライズと手動(トップ・レベル)を担います。前者は非侵入型完全自動として処理し、後者はsavePointer()とloadPointer()関数で行います。インスタンスを回復しないオーナー指定ポインタは意味がないので、constポインタは定義していません。<br>
465 <br>
466 
467 4. OwnerListManualクラス<br>
468 オーナー・ポインタの定義です。各型のポインタについて、手動(非トップ・レベル)を担います。OwnerListクラスと同様constポインタを定義していません。<br>
469 <br>
470 
471 <div style="padding: 10px; margin-bottom: 10px; border: 1px solid #333333; border-radius: 10px; background-color: #d0d0d0;">
472 (*4)「配列型へのポインタ」のサポート<br>
473 以前、teratailで「[配列型(サイズ含む)へのポインタをnewで生成したい](https://teratail.com/questions/34266)」旨を質問してraccyさんの回答をヒントに対応できました。<br>
474 遠いところからですが、raccyさん、ありがとうございました。
475 </div>
476 
477 <b>source/reference_and_test/basic2/test_object_tracking3.cpp</b>でテスト関数を定義してます。<br>
478 
479 1. 保存処理<br>
480 template<class tSerializer><br>
481 void saveObjectTracking3(tSerializer& iSerializer)の前半<br>
482 <br>
483 
484 2. 回復処理<br>
485 template<class tSerializer><br>
486 void loadObjectTracking3(tSerializer& iSerializer)の前半<br>
487 
488 @subsection TestObjectTracking44 4-4.同じインスタンスを複数回シリアライズするテスト
489 
490 ObjectTrackingDerivedのインスタンスに対して、被ポインタ指定したものとしていないものについて保存/回復テストを行います。
491 
492 <b>source/reference_and_test/basic2/test_object_tracking3.cpp</b>でテスト関数を定義してます。<br>
493 
494 1. 保存処理<br>
495 template<class tSerializer><br>
496 void saveObjectTracking3(tSerializer& iSerializer)の後半<br>
497 <br>
498 
499 2. 回復処理<br>
500 template<class tSerializer><br>
501 void loadObjectTracking3(tSerializer& iSerializer)の後半<br>
502 
503 また、同じインスタンスを複数回保存し、それを異なるインスタンスへ回復しようとした時、WrongUsing例外が発生することのテストを行います。
504 
505 <b>source/reference_and_test/basic2/test_object_tracking.cpp</b>でテスト関数を定義してます。<br>
506 
507 tutoriseObjectTracking()関数の最後の方、「複数回シリアライズ・データの回復エラーテスト」で行っています。<br>
508 
509 <br>
510 //############################################################################
511 @section TestPolymorphism 5.ポリモーフィズムの網羅的な使用例(自動テスト)の説明
512 //############################################################################
513 
514 ここでは、基底クラスへのポインタでポイントされる異なる派生クラスを適切に回復できることを確認します。<br>
515 非侵入型完全自動、侵入型半自動、非侵入型手動の3種類の基底クラスと派生クラスを用意し、派生クラスは3種類の基底クラスを全て継承しました。
516 
517 <b>source/reference_and_test/basic2/test_polymorphism.h</b>でテスト用のクラスを定義してます。<br>
518 
519 1. 基底クラス
520  - PolyBaseFullAuto 非侵入型完全自動
521  - PolyBaseHalfAuto 侵入型半自動
522  - PolyBaseManual 非侵入型手動<br>
523 <br>
524 
525 2. 派生クラス
526  - PolyDerivedFullAuto 非侵入型完全自動
527  - PolyDerivedHalfAuto 侵入型半自動
528  - PolyDerivedManual 非侵入型手動
529 
530 <b>source/reference_and_test/basic2/test_polymorphism.cpp</b>でテスト関数を定義してます。<br>
531 
532 1. 保存処理<br>
533 template<class tSerializer><br>
534 void savePolymorphism(tSerializer& iSerializer)<br>
535 <br>
536 
537 2. 回復処理<br>
538 template<class tSerializer><br>
539 void loadPolymorphism(tSerializer& iSerializer)<br>
540 
541 基底クラスのインスタンスをstd::unique_ptr<>で保持し、そのstd::unique_ptr<>のリストをstd::vector<>で保持しています。(std::vector<>とstd::unique_ptr<>を非侵入型手動クラスとして仮にシリアライズ対応しています。)<br>
542 3種類の基底クラス毎にstd::vector<>を用意しているので、std::vector<>も下記の3種類あります。
543 
544 - 非侵入型完全自動
545 - 侵入型半自動
546 - 非侵入型手動
547 
548 各std::vector<>には3種類の派生クラスのインスタンスを登録して、std::vector<>を保存します。そして、全てのポインタをnullptr設定して回復し、保存した時の派生クラスを回復できたことを確認しています。<br>
549 
550 */