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
*/
Theolizer
source
document
ja
7g.modify_class.h
© 2016
Theoride Technology
All Rights Reserved. "Theolizer" is a registered trademark of Theoride Technology.
構築:
1.8.12