Theolizer  Version.1.2.0
serializer for C++ / Do you want to update your classes easily ?
7g.modify_class.h
[詳解]
1 //############################################################################
2 /*!
3  @brief ドキュメント・ファイル-使用方法(個別)
4  @ingroup Documents
5  @file 7g.modify_class.h
6  @author Yoshinori Tahara
7  @date 2017/03/19 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 ChangingClass クラスのアップデート/バージョン・アップ方法
39 ここでは、クラス(classとstruct)を修正した時、古いプログラムが保存したデータを回復するための各種指定方法を説明します。<br>
40 
41 
42 <br>
43 //############################################################################
44 @section HowToModifyClass1 1.バージョン番号を変えないでクラスを修正
45 //############################################################################
46 
47 クラスのバージョン番号の更新無しで行う修正は、enum型と異なりTHEOLIZER_ANNOTATE()を用いて指定することは特にありません。通常通りクラス定義を変更するだけです。
48 
49 その際、@ref Basic121 で説明した「名前対応」と「順序対応」でできることが異なります。<br>
50 また、Theolizerは考え方としては配列も一種のクラス(複数の変数の集まり)として取り扱います。<br>
51 例えば2次元配列は1次元配列の配列とします。これはC++の考え方に準じます。<br>
52 
53 <br>
54 @subsection HowToModifyClass11 1-1.名前対応の場合
55 メンバ変数のメモリ上のデータとシリアライズ・データ間を変数名で対応しますので、柔軟な対応が可能です。<br>
56 - メンバ変数の追加<br>
57 メンバ変数を任意の位置へ追加できます。古いシリアライズ・データを回復する時、新規追加したメンバ変数の値は変更せず、そのまま維持します。ただし、オーナー指定ポインタへ回復する際に領域を獲得した時はコンストラクタにて初期化された値となります。<br>
58 <br>
59 
60 - メンバ変数の削除<br>
61 任意の位置のメンバ変数を削除できます。古いシリアライズ・データには削除したメンバ変数に対応するデータが含まれていますが、そのデータは破棄されます。<br>
62 <br>
63 
64 - メンバ変数の順序変更<br>
65 メンバ変数の定義順序の変更が可能です。メンバ変数名で対応付けて正しい変数へ回復します。<br>
66 <br>
67 
68 - 基底クラスについて<br>
69 基底クラスの追加/削除/順序変更はメンバ変数の変更に準じます。<br>
70 なお、基底クラスは"{基底クラス名}"と言う名前でシリアライス・データへ記録します。<br>
71 <br>
72 
73 <div style="padding: 10px; margin-bottom: 10px; border: 1px solid #333333; border-radius: 10px; background-color: #d0d0d0;">
74 <b>注意事項:</b><br>
75 enum型はバージョン・アップ無しでのシンボル名/値変更に対応していましたが、メンバ変数名を変更する時はバージョン・アップが必要です。原理的には対応可能ですので要望があれば対応します。
76 </div>
77 
78 <br>
79 @subsection HowToModifyClass12 1-2.順序対応の場合
80 メンバ変数のメモリ上のデータとシリアライズ・データ間を双方の順序で対応します。例えば、先頭のメンバ変数はシリアライズ・データ先頭から回復されます。そのため、変更対応はあまりできません。<br>
81 - メンバ変数の追加<br>
82 メンバ変数の並びの最後へのみ追加できます。古いシリアライズ・データを回復する時、新規追加したメンバ変数の値は変更せず、そのまま維持します。ただし、オーナー指定ポインタへ回復する際に領域を獲得した時はコンストラクタにて初期化された値となります。<br>
83 メンバ変数の削除と併用するのは危険です。順序だけで対応するため、シリアライズ・データに記録された旧メンバ変数を新メンバ変数へ回復しようとします。どうしても必要な場合はバージョン番号変更により対応して下さい。<br>
84 <br>
85 
86 - メンバ変数の削除<br>
87 メンバ変数の並びの最後からのみ削除できます。(途中を削除することはできません。)古いシリアライズ・データには削除したメンバ変数に対応するデータが含まれていますが、そのデータは破棄されます。<br>
88 メンバ変数の追加と同様、メンバ変数の削除を追加と併用するのは危険です。<br>
89 <br>
90 
91 - メンバ変数の順序変更<br>
92 頭から順に回復しますので、定義順序の変更には対応できません。<br>
93 <br>
94 
95 - 基底クラスについて<br>
96 基底クラスの追加/削除/順序変更はメンバ変数の変更に準じます。<br>
97 <br>
98 
99 <br>
100 @subsection HowToModifyClass13 1-3.配列の場合
101 - 考え方<br>
102 配列は、順序対応に準じた考え方です。<br>
103 <br>
104 
105 - 要素数の増加<br>
106 要素を増やすと、従来の要素列の最後に追加されます。古いシリアライズ・データを回復する時、新規追加された要素の値は変更せず、そのまま維持します。<br>
107 <br>
108 
109 - 要素数の減少<br>
110 要素を減らすと、従来の要素列の際がから削除されます。古いシリアライズ・データには削除された要素に対応するデータが含まれていますが、そのデータは破棄されます。<br>
111 <br>
112 
113 - 配列の要素数の上限<br>
114 バージョン番号変更時に要素毎の引き継ぎが必要です。それをコンパイル時に再帰関数を用いて処理していますので、あまり大きな配列に対応できません。<br>
115 現在、プリビルド版を提供している環境では要素数はsource/reference_and_test/all_common.hのkArrayMaxでテストしています。(v0.5.0では160)<br>
116 <br>
117 
118 <br>
119 @subsection HowToModifyClass14 1-4.違反した場
120 例えば、順序対応において変数の並び順を変更した場合、シリアライズ・データ内の変数と異なる変数へデータを回復しようとします。もしも、同じ型の変数であった場合はそのまま回復してしまいます。もしも、異なる型の変数だった場合、2つの事態があります。
121 - シリアライザのコンストラクト時にTypeCheckかTypeCheckByIndexを指定した場合<br>
122 この場合は型チェックを行います。互換性のない型から回復しようとした場合、エラーになります。
123 - それ以外の場合<br>
124 異常動作します。多くの場合はフォーマット・エラーが発生しますが、エラーになることを保証できません。<br>
125 <br>
126 <br>
127 @subsection HowToModifyClass15 1-5.サンプル・ソース
128 
129 @subsubsection HowToModifyClass111 1-1-1.名前対応クラス
130 
131 <br>
132 <b>変更前のソース例:(source/reference_and_test/ver1a/test_modify_class.h)</b><br>
133 
134 @dontinclude ver1a/test_modify_class.h
135 @skip struct ModifyClassName :
136 @until 以下略
137 @skip };
138 @until };
139 
140 <br>
141 <b>変更後のソース例:(source/reference_and_test/ver1b/test_modify_class.h)</b><br>
142 
143 @dontinclude ver1b/test_modify_class.h
144 @skip struct ModifyClassName :
145 @until 以下略
146 @skip };
147 @until };
148 
149 <br>
150 <b>変更前(ver1a)にシリアライズされたModifyClassNameのデータ(該当データのみ抽出):</b><br>
151 
152 @code
153 
154 {
155  "{ModifyFullAuto}":{
156  "mFullAuto":100
157  },
158  "{ModifyHalfAuto}":{
159  "mHalfAuto":101
160  },
161  "{ModifyManual}":[
162  102
163  ],
164  "mFullAutoMember":{
165  "mFullAuto":110
166  },
167  "mHalfAutoMember":{
168  "mHalfAuto":111
169  },
170  "mManualMember":[
171  112
172  ],
173  "mShort":120,
174  "mInt":121,
175  "mUnsigned":122
176 }
177 
178 @endcode
179 
180 <br>
181 <b>それを変更後のプログラムで回復してJsonで出力:</b><br>
182 
183 @code
184 
185 {
186  "SerialzierName":"JsonTheolizer",
187  "GlobalVersionNo":1,
188  "TypeInfoList":[1]
189 }
190 {
191  "{ModifyManual}":[
192  102
193  ],
194  "{ModifyFullAuto}":{
195  "mFullAuto":100
196  },
197  "{ModifyHalfAutoX}":{
198  "mHalfAutoX":""
199  },
200  "mManualMember":[
201  112
202  ],
203  "mFullAutoMember":{
204  "mFullAuto":110
205  },
206  "mHalfAutoXMember":{
207  "mHalfAutoX":""
208  },
209  "mUnsigned":122,
210  "mShort":120,
211  "mLong":0
212 }
213 
214 @endcode
215 
216 下記のように回復されます。
217 
218 - ver1aとver1bの両方に存在するデータ<br>
219 これらはそのまま回復されます。基底クラスはクラス名、メンバ変数は変数名で対応しますので、定義順序を変更しても回復できます。<br>
220 <br>
221 - 新規追加されたModifyHalfAutoX基底クラスとmHalfAutoXMemberとmLongメンバ変数<br>
222 ver1aのシリアライズ・データには存在しませんので、シリアライズ前の値が維持されます。<br>
223 サンプル・ソースでは0もしくは""(長さ0の文字列)でクリア後にデシリアライズしていますので0、""になっています。<br>
224 また、オーナー指定ポインタ経由で保存され、回復時にコンストラクトされた場合は、コンストラクタで設定した値が維持されます。<br>
225 <br>
226 - ver1bで削除された基底クラスModifyHalfAutoとメンバ変数mHalfAutoMember<br>
227 これらはシリアライズ・データ内に記録されていますが、回復先がありませんので破棄されます。
228 <br>
229 
230 ちなみに下記コードでJsonフォーマットで標準出力へ出力したものです。
231 @code
232 
233 {
234  theolizer::JsonOSerializer<> jos(std::cout);
235  THEOLIZER_PROCESS(jos, aModifyClassName);
236 }
237 
238 @endcode
239 
240 <br>
241 @subsubsection HowToModifyClass152 1-5-2.順序対応クラス
242 
243 <br>
244 <b>変更前のソース例:(source/reference_and_test/ver1a/test_modify_class.h)</b><br>
245 
246 @dontinclude ver1a/test_modify_class.h
247 @skip struct ModifyClassOrder :
248 @until 以下略
249 @skip };
250 @until };
251 
252 <br>
253 <b>変更後のソース例:(source/reference_and_test/ver1b/test_modify_class.h)</b><br>
254 
255 @dontinclude ver1b/test_modify_class.h
256 @skip struct ModifyClassOrder :
257 @until 以下略
258 @skip };
259 @until };
260 
261 <br>
262 <b>変更前(ver1a)のプログラムで保存されたModifyClassOrderのデータ(該当データのみ抽出):</b><br>
263 
264 @code
265 
266 [
267  {
268  "mFullAuto":200
269  },
270  {
271  "mHalfAuto":201
272  },
273  [
274  202
275  ],
276  {
277  "mFullAuto":210
278  },
279  {
280  "mHalfAuto":211
281  },
282  [
283  212
284  ],
285  220,
286  221,
287  222
288 ]
289 
290 @endcode
291 
292 <br>
293 <b>上記データを変更後のプログラムで回復してJson形式で出力:</b><br>
294 
295 @code
296 
297 {
298  "SerialzierName":"JsonTheolizer",
299  "GlobalVersionNo":1,
300  "TypeInfoList":[1]
301 }
302 [
303  {
304  "mFullAuto":200
305  },
306  {
307  "mHalfAuto":201
308  },
309  [
310  202
311  ],
312  {
313  "mFullAuto":210
314  },
315  {
316  "mHalfAuto":211
317  },
318  [
319  212
320  ],
321  220,
322  221,
323  222,
324  {
325  "mHalfAutoX":""
326  },
327  0
328 ]
329 
330 @endcode
331 
332 新規追加されたmHalfAutoXMemberとmLongメンバ変数はシリアライズ前のデータが維持されます。<br>
333 同じバージョン内で可能な変更は、最後への追加、もしくは最後からの削除のどちらか一方のみですので、今回は追加してます。削除した場合は当該シリアライズ・データは単純に破棄されます。<br>
334 基底クラス→メンバ変数の順序でシリアライズするため、メンバ変数が無い場合は、最後の基底クラスの後への基底クラスの追加、もしくは、最後の方からの基底クラスの削除のどちらかが可能です。<br>
335 <br>
336 
337 ちなみに下記コードでJsonフォーマットで標準出力へ出力しています。
338 @code
339 
340 theolizer::JsonOSerializer<> jos(std::cout);
341 THEOLIZER_PROCESS(jos, aModifyClassOrder);
342 
343 @endcode
344 
345 <br>
346 @subsubsection HowToModifyClass153 1-5-3.配列の要素数の変更サンプル
347 
348 <br>
349 <b>変更前のソース例:(source/reference_and_test/ver1a/test_modify_class.h)</b><br>
350 
351 @dontinclude ver1a/test_modify_class.h
352 @skip struct ArrayTest
353 @until {
354 @skip kSizeA=
355 @until 以下略
356 @skip };
357 @until };
358 
359 <br>
360 <b>変更後のソース例:(source/reference_and_test/ver1b/test_modify_class.h)</b><br>
361 
362 @dontinclude ver1b/test_modify_class.h
363 @skip struct ArrayTest
364 @until {
365 @skip kSizeA=
366 @until 以下略
367 @skip };
368 @until };
369 
370 <br>
371 <b>変更前(ver1a)のプログラムで保存されたModifyClassOrderのデータ(該当データのみ抽出):</b><br>
372 (長いのでmArray3Dを省略)
373 
374 @code
375 
376 [
377  [1,"ArrayTest",{
378  "mArray1D":[
379  0,
380  1
381  ],
382  "mArray2D":[
383  [
384  0,
385  1,
386  2 //★ver1bでは破棄
387  ],
388  [
389  1000,
390  1001,
391  1002 //★ver1bでは破棄
392  ]
393  ],
394  "mArray3D":[
395  省略
396  ]
397  }]
398 ]
399 
400 @endcode
401 
402 <br>
403 <b>上記データを変更後のプログラムで回復してJson形式で出力:</b><br>
404 (長いのでmArray3Dを省略)
405 
406 @code
407 
408 {
409  "SerialzierName":"JsonTheolizer",
410  "GlobalVersionNo":1,
411  "TypeInfoList":[1]
412 }
413 [
414  [1,"ArrayTest",{
415  "mArray1D":[
416  0,
417  1,
418  0 //★未変更
419  ],
420  "mArray2D":[
421  [
422  0,
423  1
424  ],
425  [
426  1000,
427  1001
428  ],
429  [
430  0, //★未変更
431  0 //★未変更
432  ]
433  ],
434  "mArray3D":[
435  省略
436  ]
437  }]
438 ]
439 
440 @endcode
441 
442 ver1bにあってver1aにない要素の内容は、ver1bで生成された時の0のまま変更されていません。<br>
443 また、ver1aになってver1bにない要素は破棄されています。
444 
445 <br>
446 //############################################################################
447 @section HowToMakeGlobalVersionNoTable2 2.グローバル・バージョン番号テーブル生成
448 //############################################################################
449 
450 クラスについてもバージョン・アップする際にはグローバル・バージョン番号テーブルが必要です。<br>
451 enum型の場合と同じですので、詳しくは@ref HowToMakeGlobalVersionNoTable を参照下さい。
452 
453 
454 <br>
455 //############################################################################
456 @section HowToModifyClass3 3.バージョン番号を変えることで可能な修正
457 //############################################################################
458 
459 enum型と同様に各クラスへバージョン番号を割り当てることで大きな修正を行うことができます。<br>
460 
461 @subsection HowToModifyClass31 3-1.名前対応の場合
462 名前対応クラスは下記の変更に対応できます。
463 
464 - メンバ変数、および、基底クラスの追加/削除/順序変更<br>
465 これらに付いてはバージョン番号を変えない場合と同じです。<br>
466 <br>
467 - メンバ変数名の変更<br>
468 型を変更せず名前だけの変更であれば参照できますので、変更可能です。THEOLIZER_ANNOTATE()で指定します。<br>
469 <br>
470 - クラス名の変更<br>
471 次の場合、シリアライズ・データ中に型名が記録されていますので、クラス名を変更すると通常は型チェックに通りません。<br>
472 しかし、適切にリファクタリングすればクラス名の変更も可能です。<br>
473  - 型チェック有り(TypeCheckByかTypeCheckByIndexを指定して派生シリアライザを生成した時)<br>
474  - クラスへのポインタをオーナー指定している時(ポリモーフィズム対応のため)<br>
475 <br>
476 - 順序対応への変更<br>
477 クラス定義が枯れてきたら効率の良い順序対応へ変更できます。<br>
478 <br>
479 
480 @subsection HowToModifyClass32 3-2.順序対応の場合
481 順序対応クラスは下記の変更に対応できます。
482 
483 - メンバ変数、および、基底クラスの追加/削除/順序変更<br>
484 これらに付いてはバージョン番号を変えない場合と同じです。<br>
485 <br>
486 - メンバ変数名の変更とクラス名の変更<br>
487 名前対応の場合と同じです。<br>
488 <br>
489 - 名前対応への変更<br>
490 クラスの大幅変更が必要になった時に一度名前対応へ変更すると効率良く開発できます。<br>
491 <br>
492 
493 @subsection HowToModifyClass33 3-3.配列の場合
494 配列は下記の変更に対応できます。なお、基底の型はクラスに限りません。プリミティブやenum型を含む任意の型に対応しています。
495 
496 - 考え方<br>
497 配列は、順序対応に準じた考え方です。
498 配列の要素数はバージョン番号変更無しで変更できますが、次元数はバージョン変更時にのみ可能です。ただし、配列単独ではバージョン番号を管理できる仕組みに対応していませんので、クラス・メンバとしての配列のみ次元数変更が可能です。<br>
499 <br>
500 
501 - 要素数の増加/減少<br>
502 これらに付いてはバージョン番号を変えない場合と同じです。<br>
503 <br>
504 
505 - 次元数の増加<br>
506 最上位の次元へ追加されます。対応先がないものは無視されます。<br>
507 例えば、int array[2][3];をint array[2][3][2];と増加した場合、次のように対応します。<br>
508 |変更前|変更後|
509 |:----:|:---:|
510 |array[0][0]|array[0][0][0]|
511 |array[0][1]|array[0][0][1]|
512 |array[0][2]|---|
513 |array[1][0]|array[0][1][0]|
514 |array[1][1]|array[0][1][1]|
515 |array[1][2]|---|
516  |---|array[0][2][0]|
517  |---|array[0][2][1]|
518  |---|array[1][0][0]|
519  |---|array[1][0][1]|
520  |---|array[1][1][0]|
521  |---|array[1][1][1]|
522  |---|array[1][2][0]|
523  |---|array[1][2][1]|
524 <br>
525 
526 - 次元数の減少<br>
527 最上位の次元から削除されます。対応先がないものは無視されます。<br>
528 例えば、int array[2][2][3];をint array[3][2];と増加した場合、次のように対応します。<br>
529 |変更前|変更後|
530 |:----:|:---:|
531 |array[0][0][0]|array[0][0]|
532 |array[0][0][1]|array[0][1]|
533  |---|array[0][2]|
534 |array[0][1][0]|array[1][0]|
535 |array[0][1][1]|array[1][1]|
536  |---|array[1][2]|
537 |array[0][0][2]|---|
538 |array[0][1][2]|---|
539 |array[1][0][0]|---|
540 |array[1][0][1]|---|
541 |array[1][0][2]|---|
542 |array[1][1][0]|---|
543 |array[1][1][1]|---|
544 |array[1][1][2]|---|
545 <br>
546 
547 <br>
548 //############################################################################
549 @section HowToModifyClass4 4.バージョン番号を変えてクラス修正する仕組み概要
550 //############################################################################
551 
552 @subsection HowToModifyClass41 4-1.コア・データ構造
553 バージョン毎にシリアライズ対象のメンバ変数だけを集めたクラス(TheolizerVersion)を定義しています。これは、当該バージョンで持っていたメンバ変数を管理する内部クラスです。<br>
554 
555 各バージョンのTheolizerVersionは、次バージョンのTheolizerバージョンとの関係を管理します。バージョンnのTheolzierVersionの各メンバ変数には、バージョンn+1のTheolizerバージョンのどのメンバ変数と対応するのか記録され、自動的に引継ぎ処理が実行されます。<br>
556 
557 また、この自動引継ぎ処理は、メンバ変数のアドレスも引継ぎますので、旧バージョンでオブジェクト追跡されていた情報も回復できます。メンバ変数名を変更してもアドレス引継ぎは機能します。ただし、メンバ変数の型を変更した場合は、アドレスを引き継げません。<br>
558 
559 <div style="padding: 10px; margin-bottom: 10px; border: 1px solid #333333; border-radius: 10px; background-color: #d0d0d0;">
560 <b>重要事項:</b><br>
561 Theolizerドライバの主な役割はTheolizerVersionを自動生成することであり、*.theolzier.hppファイルには当該コンパイル単位でシリアライズ指定されているクラスとenum型のTheolizerVersionの定義が生成されます。旧バージョンのTheolizerVersion定義もここに入っていますので、*.theolzier.hppは自動生成ファイルですがバージョン管理システムの管理対象ソースとして登録して下さい。
562 </div>
563 
564 @subsection HowToModifyClass42 4-2.補助データ構造
565 @ref Basic33 で説明したように、足並みを揃えて(Keep-step)バージョン・アップ/ダウンする変数としない変数があります。<br>
566 
567 Keep-step処理する基底クラスやメンバ変数は、4-1.で説明したTheolizerVersionクラスにて管理されます。<br>
568 Keep-step処理しない(Non-keep-step)基底クラスやメンバ変数用には異なるクラスで管理しています。
569 
570 - Non-keep-step基底クラス(非侵入型手動クラス)<br>
571 `TheolizerBase<基底クラス>`クラスを用いて、シリアライズ対象クラス・インスタンスへの参照を管理しています。<br>
572 <br>
573 
574 - Non-keep-stepメンバ変数<br>
575 `TheolizerNonKeepStep<型>`クラスを用いています。
576 
577 更に`TheolizerNonKeepStep<型>`クラスは下記のような2種類を定義しています。
578 
579 - 部分特殊化 : プリミティブ型(int型やstd::string型)
580 - プライマリ : その他のNon-keep-step全て(ポインタ、参照、非侵入型手動クラス)
581 
582 @subsection HowToModifyClass43 4-3.侵入型半自動におけるバージョン・アップ/ダウン処理
583 - 旧バージョンのデータを保存する時の内部動作<br>
584 ターゲットのユーザ定義クラスから最新版のTheolizerVersionを生成後、次々とカスケードに1つ前のバージョンのTheolizerVersionをコンストラクトする際にメンバ変数を引き継ぎつつ、目的のバージョンのTheolizerVersionまで生成します。そして、そのバージョンのTheolizerVersionを使って保存処理を行います。<br>
585 この時、<b>1つ前のバージョンのTheolizerVersionを生成後、ユーザ定義のバージョン・ダウン処理(downVersion関数)が定義されていたら、それを呼び出します。</b><br>
586 <br>
587 
588 - 旧バージョンのデータから回復する時の内部動作<br>
589 保存時と同様の流れで、目的のハージョンのTheolizerVersionまで生成します。目的のTheolizerVersionを使って回復処理を行い、生成時と逆の順序でTheolizerVersionをデストラクトしつつ、ターゲットのユーザ定義クラスへ値を戻します。<br>
590 この時、<b>1つ前バージョンのTheolizerVersionをデストラクトする時に、ユーザ定義のバージョン・アップ処理(upVersion関数)が定義されていたら、それを呼び出します。</b><br>
591 <br>
592 
593 @subsection HowToModifyClass44 4-4.非侵入型手動におけるバージョン・アップ/ダウン処理
594 こちらは半自動型と異なり、カスケードにバージョン・アップ/ダウンを行うことはたいへん難しく、却ってあなたのプログラム開発の手間を増大させるため、カスケード処理は行いません。従って、各バージョン毎に保存処理と回復処理を記述する必要があります。<br>
595 しかし、対象クラスを変更する度にバージョン・アップが必要なわけではありません。対象クラスを回復する際に従来の保存データでは不足する場合にのみバージョン・アップが必要になります。<br>
596 
597 また、バージョン変更の際にシリアライズ対象クラスと保存/回復処理間のI/Fに変化がなければ、旧バージョンの保存/回復処理の変更は不要です。しかし、もし、このI/Fを変更された場合は、それに合わせて旧バージョンの保存/回復処理の修正も必要となります。ですので、他のクラスとのI/Fは自由ですが、保存/回復処理とのI/Fはできるだけ変更しないことをお勧めします。<br>
598 <br>
599 
600 @subsection HowToModifyClass45 4-5.バージョン・アップ時の注意事項
601 @ref Modifying にて記述した以下の点にご注意下さい。<br>
602 
603 1. down/upVersion関数で変更可能な変数<br>
604 プリミティブ型とenum型変数のみです。<br>
605 クラス型メンバ変数にに含まれるプリミティブ型とenum型変数も変更可能です。更に、クラス型メンバ変数にに含まれるクラス型メンバ変数に含まれるプリミティブ型とenum型変数も変更可能です。(以下同様)<br>
606 <br>
607 2. それ以外のものは変更してはいけません。<br>
608 @ref Basic343 参照<br>
609  - ポインタ、および、参照が指す先のインスタンス
610  - 非侵入型手動クラスの基底クラス、および、メンバ変数
611  - ポインタそれ自身<br>
612 <br>
613 3. upVersionで参照する変数は先に回復すること<br>
614 クラス分割により、異なるシリアライズ・データから回復される変数が存在します。<br>
615 それらをアクセスしてupVersion処理を行う場合、依存先のメンバ変数を先に回復する必要があります。<br>
616 見落とし易いのでご注意下さい。<br>
617 
618 <br>
619 //############################################################################
620 @section HowToModifyClass5 5.バージョン番号を変えてクラスを修正する方法
621 //############################################################################
622 
623 <br>
624 @subsection HowToModifyClass51 5-1.メンバ変数名の変更
625 リファクタリングの際にメンバ変数名を変更したい場合があると思います。<br>
626 単に変更するだけの場合、名前を変更した変数は削除して、別名の変数を追加された扱いとなり、変更前のプログラムで保存したシリアライズ・データから回復する際に、値を引き継ぐことができません。
627 
628 メンバ変数名を変更するにはTHEOLIZER_ANNOTATE(FS:)の第一パラメータを用いて対応つけます。<br>
629 変更後のクラス定義で、変更前の変数名を指定します。
630 
631 @code
632 型 メンバ変数名 THEOLIZER_ANNOTATE(FS:旧メンバ変数名);
633 @endcode
634 
635 以下、名前対応クラス(ModifyClassName)と順序対応クラス(ModifyClassOrder)のmHalfAutoXMemberメンバ変数の変数名をmHalfAutoYMemberへ変更する例です。どちらのクラスの場合も同じです。<br>
636 なお、型変更には対応していません。変数名を変更して対応付ける各変数の型は同じにして下さい。<br>
637 
638 なお、下記の例ではクラス名を変更していますが、クラス自体は同じものです。<br>
639 クラス名をModifyHalfAutoXからModifyHalfAutoYへ変更しています。クラス名の変更については次節で説明します。<br>
640 
641 <br>
642 <b>変更前のソース例:(source/reference_and_test/ver1c/test_modify_class.h)</b><br>
643 
644 @dontinclude ver1c/test_modify_class.h
645 @skip struct ModifyClassName :
646 @until 以下略
647 @skip THEOLIZER_INTRUSIVE
648 @until };
649 
650 @dontinclude ver1c/test_modify_class.h
651 @skip struct ModifyClassOrder :
652 @until 以下略
653 @skip THEOLIZER_INTRUSIVE
654 @until };
655 
656 <br>
657 <b>変更後のソース例:(source/reference_and_test/ver2a/test_modify_class.h)</b><br>
658 
659 @dontinclude ver2a/test_modify_class.h
660 @skip struct ModifyClassName :
661 @until 以下略
662 @skip THEOLIZER_INTRUSIVE
663 @until };
664 
665 @dontinclude ver2a/test_modify_class.h
666 @skip struct ModifyClassOrder :
667 @until 以下略
668 @skip THEOLIZER_INTRUSIVE
669 @until };
670 
671 <br>
672 @subsection HowToModifyClass52 5-2.クラス名の変更
673 リファクタリングを行う時、クラス名を変更したい場合もあると思います。<br>
674 基本的には通常通り全てのソース・ファイルで使っているクラス名を変更します。<br>
675 その際、*.theolizer.hppで指定されているクラス名も変更します。ただし、旧バージョンのシリアライズ・データと対応付けるためのクラス名は変更してはいけません。<br>
676 前者(リファクタリング対象)は`(クラス名)`のように()で括られています。後者(変更しないクラス名)は`u8"クラス名"`のように文字列として定義されていますので、`(クラス名)`を一括修正することで*.theolizer.hppを変更できます。<br>
677 
678 例えば、下記は「5-1.メンバ変数名の変更」で、クラス名をModifyHalfAutoXからModifyHalfAutoYへ変更する前の*.theolizer.hppファイル(複数あるので注意)の該当部分の一部です。<br>
679 
680 <b>ModifyHalfAutoX</b>自動生成ソース部
681 
682 <div style="padding: 5px; margin-bottom: 10px; border: 1px solid #c4cfe5; border-radius: 1px; background-color: #fbfcfd;">
683 #`ifdef THEOLIZER_WRITE_CODE // ###### ModifyHalfAutoX ######`<br>
684 <br>
685 (中略)<br>
686 <br>
687 #`define THEOLIZER_GENERATED_LAST_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kLastVersionNo,1)`<br>
688 #`define THEOLIZER_GENERATED_CLASS_TYPE THEOLIZER_INTERNAL_UNPAREN`<b style="color: #ff0000;">(ModifyHalfAutoX)</b>``<br>
689 <br>
690 `// ---<<< Version.1 >>>---`<br>
691 <br>
692 #`define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,1)`<br>
693 #`define THEOLIZER_GENERATED_CLASS_NAME()\`<br>
694 `  THEOLIZER_INTERNAL_CLASS_NAME((u8"ModifyHalfAutoX"))`<br>
695 #`define THEOLIZER_GENERATED_ELEMENT_MAP emName`<br>
696 #`define THEOLIZER_GENERATED_ELEMENT_LIST()\`<br>
697 `  THEOLIZER_INTERNAL_ELEMENT_N((mHalfAutoX),mHalfAutoX,etmDefault,\`<br>
698 `  (theolizerD::All),\`<br>
699 `  (std::string))<br>`
700 #`include <theolizer/internal/version_auto.inc><br>`
701 #`undef THEOLIZER_GENERATED_VERSION_NO<br>`
702 <br>
703 #`endif //THEOLIZER_WRITE_CODE // ###### ModifyHalfAutoX ######<br>`
704 </div>
705 
706 <b>ModifyClassName</b>自動生成ソース部
707 
708 <div style="padding: 5px; margin-bottom: 10px; border: 1px solid #c4cfe5; border-radius: 1px; background-color: #fbfcfd;">
709 #`ifdef THEOLIZER_WRITE_CODE // ###### ModifyClassName ######`<br>
710 <br>
711 #`define THEOLIZER_GENERATED_LAST_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kLastVersionNo,1)`<br>
712 #`define THEOLIZER_GENERATED_FULL_AUTO ModifyClassName`<br>
713 <br>
714 `// ---<<< Version.1 >>>---`<br>
715 <br>
716 #`define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,1)`<br>
717 #`define THEOLIZER_GENERATED_CLASS_NAME()\`<br>
718    `THEOLIZER_INTERNAL_CLASS_NAME((u8"ModifyClassName"))`<br>
719 #`define THEOLIZER_GENERATED_ELEMENT_MAP emName`<br>
720 #`define THEOLIZER_GENERATED_BASE_LIST()\`<br>
721 `  THEOLIZER_INTERNAL_BASE_N(public,etmDefault,0,(ModifyManual),u8"ModifyManual")\`<br>
722 `  THEOLIZER_GENERATED_SEP\`<br>
723 `  THEOLIZER_INTERNAL_BASE_KN(public,etmDefault,1,(ModifyFullAuto),1,u8"ModifyFullAuto")\`<br>
724    THEOLIZER_GENERATED_SEP\`<br>
725 `  THEOLIZER_INTERNAL_BASE_KI(public,etmDefault,2,`<b style="color: #ff0000;">(ModifyHalfAutoX)</b>`,1,u8"ModifyHalfAutoX")`<br>
726 #`define THEOLIZER_GENERATED_ELEMENT_LIST()\`<br>
727 `  THEOLIZER_INTERNAL_ELEMENT_N((mManualMember),mManualMember,etmDefault,\`<br>
728 `  (theolizerD::All),\`<br>
729 `  (ModifyManual))\`<br>
730 `  THEOLIZER_INTERNAL_ELEMENT_KN((mFullAutoMember),mFullAutoMember,etmDefault,\`<br>
731 `  (theolizerD::All),\`<br>
732 `  (ModifyFullAuto),1)\`<br>
733 `  THEOLIZER_INTERNAL_ELEMENT_KI((mHalfAutoXMember),mHalfAutoXMember,etmDefault,\`<br>
734 `  (theolizerD::All),\`<br>
735 `  `<b style="color: #ff0000;">(ModifyHalfAutoX)</b>`,1)`<br>
736 `  THEOLIZER_INTERNAL_ELEMENT_N((mUnsigned),mUnsigned,etmDefault,\`<br>
737 `  (theolizerD::All),\`<br>
738 `  (unsigned int))\`<br>
739 `  THEOLIZER_INTERNAL_ELEMENT_N((mShort),mShort,etmDefault,\`<br>
740 `  (theolizerD::All),\`<br>
741 `  (short))\`<br>
742 `  THEOLIZER_INTERNAL_ELEMENT_N((mLong),mLong,etmDefault,\`<br>
743 `  (theolizerD::All),\`<br>
744 `  (long))`<br>
745 #`include <theolizer/internal/version_auto.inc>`<br>
746 #`undef THEOLIZER_GENERATED_VERSION_NO`<br>
747 <br>
748 #`endif//THEOLIZER_WRITE_CODE // ###### ModifyClassName ######`<br>
749 </div>
750 
751 ModifyHalfAutoXクラスの名前をModifyHalfAutoYへリファクタリングする際に*.theolizer.hppファイル内のu8"ModifyHalfAutoX"を除く(ModifyHalfAutoX)を(ModifyHalfAutoY)へ変更します。<br>
752 
753 上記の場合、赤く記した3箇所の(ModifyHalfAutoX)を(ModifyHalfAutoY)へ変更することになります。<br>
754 なお、コメントに含まれるModifyHalfAutoXは変更してもしなくても影響はありません。
755 
756 <br>
757 @subsection HowToModifyClass53 5-3.配列の次元数の変更
758 バージョン変更する際に配列の次元を増やしたり減らしたりすることができます。<br>
759 その際、特別な記述は不要です。<br>
760 
761 <br>
762 <b>変更前のソース例:(source/reference_and_test/ver1c/test_modify_class.h)</b><br>
763 
764 @dontinclude ver1c/test_modify_class.h
765 @skip struct ArrayTest
766 @until {
767 @skip kSize1=
768 @until 以下略
769 @skip };
770 @until };
771 
772 <br>
773 <b>変更後のソース例:(source/reference_and_test/ver2a/test_modify_class.h)</b><br>
774 
775 @dontinclude ver2a/test_modify_class.h
776 @skip struct ArrayTest
777 @until {
778 @skip kSize2=
779 @until 以下略
780 @skip };
781 @until };
782 
783 <br>
784 @subsection HowToModifyClass54 5-4.バージョン・アップ/ダウン処理の記述方法
785 
786 手順としては以下の通りです。
787 
788 1. 通常通り、グローバル・バージョン番号テーブルを定義<br>
789 既に定義していたら、再定義は不要です。<br>
790 THEOLIZER_DEFINE_GLOBAL_VERSION_TABLE()マクロで定義します。<br>
791 詳細は@ref HowToMakeGlobalVersionNoTable を参照下さい。<br>
792 <br>
793 2. 対象のクラスが非侵入型完全自動クラスなら、侵入型半自動へ変更して、<b>一度ビルド</b><br>
794 通常はTHEOLIZER_INTRUSIVE()マクロを対象クラス内に記述することで「侵入型半自動」であることを指定します。<br>
795 詳細は@ref HalfAutoClass を参照下さい。<br>
796 ここでビルドしてver.1の自動生成ソースを生成します。<br>
797 ビルドしない場合、突然ver.2へ上げられたことになり、コンパイルできない場合があります。<br>
798 <br>
799 3. バージョン番号をインクリメント<br>
800  - グローバル・バージョン番号<br>
801 @ref HowToMakeGlobalVersionNoTable を参照下さい。<br>
802  - 対象クラスのローカル・バージョン番号<br>
803 @ref Basic321 、および、@ref HalfAutoClass を参照下さい。
804 <br>
805 <br>
806 4. down/upVersion関数雛形をコピー&ペースト<br>
807 後述します。<br>
808 <br>
809 5. 必要に応じてdown/upVersion関数の内容を記述<br>
810 後述します。<br>
811 
812 @subsubsection HowToModifyClass541 5-4-1.down/upVersion関数雛形のコピー&ペースト
813 シリアライズ対象クラスのシリアライズ用のコードは*.theolizer.hppに自動生成されています。それと一緒にdown/upVersion関数の雛形も定義していますので、それをコピー&ペーストして下さい。
814 
815 例えば、以下の通りです。
816 
817 <br>
818 <b>対象クラスKeepStepTest:(source/reference_and_test/ver1c/test_modify_complex.h)</b><br>
819 
820 @dontinclude ver1c/test_modify_complex.h
821 @skip struct KeepStepTest :
822 @until };
823 
824 <br>
825 <b>KeepStepTestの自動生成ソース:(ビルド・フォルダ/reference_and_test/ver1c/test_modify_class.cpp.theolizer.hpp)</b><br>
826 
827 @code
828 
829 #ifdef THEOLIZER_WRITE_CODE // ###### KeepStepTest ######
830 
831 #if false // Sample of up/downVersion function.
832 template<class tTheolizerVersion, class tNextVersion>
833 struct KeepStepTest::TheolizerUserDefine<tTheolizerVersion, tNextVersion, 1>
834 {
835  // Members version down.
836  static void downVersion(tNextVersion const& iNextVersion, tTheolizerVersion& oNowVersion)
837  {
838  }
839 
840  // Members version up.
841  static void upVersion(tTheolizerVersion const& iNowVersion, tNextVersion& oNextVersion)
842  {
843  }
844 };
845 #endif // Sample of up/downVersion function.
846 
847 #define THEOLIZER_GENERATED_LAST_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kLastVersionNo,1)
848 #define THEOLIZER_GENERATED_CLASS_TYPE THEOLIZER_INTERNAL_UNPAREN(KeepStepTest)
849 
850 // ---<<< Version.1 >>>---
851 
852  中略
853 
854 #endif//THEOLIZER_WRITE_CODE // ###### KeepStepTest ######
855 
856 @endcode
857 
858 この #`if` false ~ #`endif` までの間をコピーしてください。コピー先はKeepStepTestクラスの定義の後、自動生成ソースの前ならばどこでも良いです。KeepStepTestの定義直後をお勧めします。<br>
859 
860 <b>コピー先ソース例:(source/reference_and_test/ver2a/test_modify_complex.h)</b><br>
861 
862 @dontinclude ver2a/test_modify_complex.h
863 @skip struct KeepStepTest :
864 @until static void upVersion
865 @until {
866 @skip oNextVersion.mInt16=
867 @skip }
868 @until };
869 
870 以上のTheolizerUserDefineクラス・テンプレートの最後のテンプレート引数の1がローカル・バージョン番号です。1の時はver.1とver.2の間、2の時はver.2とver.3の間のdown/upVersion処理を定義します。
871 
872 バージョン・アップ/ダウン処理を記述する必要があるバージョンについてTheolizerUserDefineクラスを定義して下さい。
873 
874 @subsubsection HowToModifyClass542 5-4-2.down/upVersion処理を記述する
875 
876 <b>static void downVersion(tNextVersion const& iNextVersion, tTheolizerVersion& oNowVersion)関数</b><br>
877 iNextVersion引数は次バージョンです。<br>
878 oNowVersionは現バージョン(テンプレート引数で指定したバージョン)です。<br>
879 それぞれ@link HowToModifyClass41 TheolizerVersionクラス@endlink のインスタンスが渡ってきます。<br>
880 iNextVersionを参照しつつ、oNowVersionの@link Basic343 設定可能なメンバ@endlink を設定して下さい。<br>
881 <br>
882 <b>設定例:</b>
883 @code
884 oNowVersion.mData0=iNextVersion.mData0-100;
885 @endcode
886 
887 なお、基底クラスは実装上の都合により、メンバ変数としてアクセスする必要が有ります。<br>
888 クラスAの基底クラスへアクセスする時は、クラス名にTheolizerBaseを連結したメンバ変数を使ってアクセスして下さい。<br>
889 例えば、次バージョンの基底クラス<b>FooClass</b>のmBarメンバへアクセスする場合、iNextVersion.<b>FooClass</b>TheolizerBase.mBar でアクセスして下さい。<br>
890 <br>
891 
892 <b>static void upVersion(tTheolizerVersion const& iNowVersion, tNextVersion& oNextVersion)関数</b><br>
893 iNowVersionは現バージョン(テンプレート引数で指定したバージョン)です。<br>
894 tNextVersion引数は次バージョンです。<br>
895 それぞれ@link HowToModifyClass41 TheolizerVersionクラス@endlink のインスタンスが渡ってきます。<br>
896 iNextVersionを参照しつつ、oNowVersionの@link Basic343 設定可能なメンバ@endlink を設定して下さい。<br>
897 <br>
898 <b>設定例:</b>
899 @code
900 oNextVersion.mData0=iNowVersion.mData0+100;
901 @endcode
902 
903 基底クラスのアクセス方法はdownVersion関数と同じです。<br>
904 
905 
906 upVersion()関数の設定可能なメンバ変数は、set()関数を持っています。これは例えば、変数の型を変更したような場合に用います。(型変更された変数の自動引継ぎをTheolizerはサポートしていませんので、手動で引継ぎが必要だからです。)<br>
907 例えば、ver.1ではint型であったmFooメンバ変数を、ver.2でlong型へ変更した場合、下記のような処理を記述して引継ぎして下さい。<br>
908 この時、set関数の第一パラメータの型は引継ぎ先の型となりますので、この例ではlong型です。<br>
909 
910 @code
911 oNextVersion.mFoo.set(iNextVersion.mFoo, iNextVersion.mFoo.getDoSucceed());
912 @endcode
913 
914 getDoSucceed()関数は、そのメンバ変数の引継ぎが必要かどうかを示します。通常はデシリアライズ時に回復された場合trueとなります。また、set(xxx, true)で設定された場合もtrueになります。最終的に最新版のTheolizerVersionでtrueだった場合、その変数の値がシリアライズ対象クラスの該当メンバへ設定されます。<br>
915 従って、上記の文により、mFooがデシリアライズされていた場合に限り、その値が次バージョンのmFoo変数へ引き継がれます。<br>
916 
917 <br>
918 //############################################################################
919 @section HowToModifyClass6 6.網羅的な使用例(自動テスト)の説明
920 //############################################################################
921 
922 <br>
923 @subsection HowToModifyClass61 6-1.クラス変更のテスト
924 クラス変更の基本的な網羅的なテストは @ref TestProgram で説明した各フォルダの下記2つのファイルにて実装しています。
925 
926 - test_modify_class.h
927 - test_modify_class.cpp
928 
929 名前対応クラス(ModifyClassName)、順序対応クラス(ModifyClassOrder)、配列メンバを持つクラス(ArrayTest)について下記の修正を行い、@ref TestProgram412 の各バージョン間で正しく回復できることを確認しています。<br>
930 それぞれのメンバ変数の型は、非侵入型完全自動クラス、侵入型半自動クラス、非侵入型手動クラス、および、プリミティブ型をテストしています。<br>
931 
932 |バージョン|ModifyClassName|ModifyClassOrder|ArrayTest|
933 |----------|---------------|----------------|---------|
934 |ver1a→ver1b|基底クラスとメンバ変数について<br> 順序変更、追加、削除|メンバ変数の追加|要素数の増加と減少|
935 |ver1b→ver1c|基底クラス、メンバ変数(クラス、enum)について<br> 旧バージョンのみのシリアライズ|---|---|
936 |ver1c→ver2a|クラス名変更<br>クラス型メンバ変数名変更|クラス名変更<br>クラス型メンバ変数名変更|次元数増加|
937 |ver2a→ver3a|プリミティブ型メンバ変数名変更<br>クラス配列型メンバ変数追加|プリミティブ型メンバ変数名変更|次元数減少|
938 |ver3a→ver3b|基底クラスとメンバ変数について<br> 順序変更、追加、削除<br>クラス配列型メンバ変数削除|クラス型とプリミティブ型について<br> メンバ変数削除|要素数上限|
939 
940 
941 <br>
942 @subsection HowToModifyClass62 6-2.クラス変更の複合テスト
943 クラス変更の複合的で網羅的なテストは @ref TestProgram で説明した各フォルダの下記2つのファイルにて実装しています。
944 
945 - test_modify_complex.h
946 - test_modify_complex.cpp
947 
948 ここでは、大きく下記の2種類についてテストしています。
949 - オブジェクト追跡している時のバージョン変更<br>
950 用いる主なクラスは、ポイントされる側のメンバ変数を持つクラス(PointeeInclude)、ポインタ型メンバ変数を持つクラス(PointerInclude)。<br>
951 ポイントされる変数について値だけでなく、アドレスも引き継がれることを確認しています。<br>
952 変数名を変更した時に値だけでなく、アドレスも引き継がれることを確認しています。<br>
953 <br>
954 - バージョン・アップ/ダウン処理<br>
955 用いる主なクラスは、主なdown/upVersionテスト用のVersionUpDownTestクラス、Keep-step変数のテストとNon-keep-step変数用の外側のupVersionテスト用のKeepStepTestクラスです。<br>
956 KeepStepTestはVersionUpDownTestを基底クラス、および、メンバ変数として含みます。<br>
957 「upVersionの全組合せテスト」では、下記の全組合せをテストします。<br>
958  - データ回復した場合、しなかった場合
959  - set()しなかった場合、DoSucceed=falseでset()した場合、DoSucceed=trueでset()した場合
960  - 代入しなかった場合、代入した場合
961 
962 |バージョン|オブジェクト追跡|バージョン・アップ/ダウン処理|
963 |----------|----------------|------------------------------|
964 |ver1a→ver1b|---|---|
965 |ver1b→ver1c|---|テスト準備|
966 |ver1c→ver2a|被ポインタとポインタ追加|Non-keep-step(enum型とプリミティブ型)について<br> upVersionの全組合せテスト|
967 |ver2a→ver3a|変数名変更|型変更対応<br>外側のupVersion()優先|
968 |ver3a→ver3b|---|---|
969 
970 */