Theolizer  Version.1.1.3
serializer for C++ / Do you want to update your classes easily ?
クラスのバリエーション

ここでは、クラス(classとstruct)をシリアライズする時の様々なバリエーションについて説明します。


1.非侵入型完全自動クラス

これはシリアライズするためにクラス定義への前準備が不要な使い方です。
使用している他のライブラリの「構造体」をシリアライズしたい時、および、プロトタイピングやデバッグ中に一時的にシリアライズしたい時に用います。

使い方は簡単で、シリアライズしたい構造体のインスタンスをいきなりTHEOLIZER_PROCESS()マクロに与えるだけです。

定義例 (source/reference_and_test/basic/test_class_variation.h)
非侵入型完全自動クラスの定義例です。

class FullAutoTutorial
{
int mFullAutoPrivate;
protected:
int mFullAutoProtected;
public:
int mFullAutoPublic;
FullAutoTutorial() :
mFullAutoPrivate(0),
mFullAutoProtected(0),
mFullAutoPublic(0)
{ }
void setNonPublic(int iFullAutoPrivate, int iFullAutoProtected);
void checkNonPublic(int iFullAutoPrivate, int iFullAutoProtected);
};

保存処理例 (source/reference_and_test/basic/test_class_variation.cpp の tutoriseClassVariation() 関数)
上記のFullAutoTutorialを保存するコード例です。

{
std::ofstream aStream("tutorise_class_variation.json");
theolizer::JsonOSerializer<> aSerializer(aStream);
// 非侵入型完全自動
FullAutoTutorial aFullAutoTutorial;
aFullAutoTutorial.setNonPublic(123, 456);
aFullAutoTutorial.mFullAutoPublic=789;
THEOLIZER_PROCESS(aSerializer, aFullAutoTutorial);
}

保存結果 (<ビルド・フォルダ>/Testing/tutorise_class_variation.json)
上記で保存されたシリアライズ・データの該当部分です。
このファイルは、zy2_long_test.shzz2_long_test.bat にて自動生成されます。(「1-3.ビルド用スクリプトの設定とビルド 」参照。)本節のビルド・フォルダ配下のファイルは全て同じです。
privateメンバ以外のメンバが保存されています。

{
"mFullAutoProtected":456,
"mFullAutoPublic":789
}

回復処理例 (source/reference_and_test/basic/test_class_variation.cpp の tutoriseClassVariation() 関数)
上記のFullAutoTutorialを回復して値をチェックするコード例です。

{
std::ifstream aStream("tutorise_class_variation.json");
theolizer::JsonISerializer<> aSerializer(aStream);
// 非侵入型完全自動
FullAutoTutorial aFullAutoTutorial;
THEOLIZER_PROCESS(aSerializer, aFullAutoTutorial);
aFullAutoTutorial.checkNonPublic(0, 456); // 完全自動のprivateはシリアライズされない
THEOLIZER_EQUAL(aFullAutoTutorial.mFullAutoPublic, 789);
}

Theolizerドライバがシリアライズされていることを検出し、自動的に必要なメンバ・リストを生成して、シリアライズ可能な状態にします。
privateメンバをシリアライズするためには、対象構造体の内部にシリアライズ関数を設ける必要が有ります。 それを自動的に行うと問題が多発してしまいます。(例えば「他のライブラリ」のソースを変更してしまう。)
それは行うべきではありませんので完全自動クラスを「非侵入型」として実装しました。従って、privateメンバをシリアライズすることはできません。
なお、対象クラスを派生して派生先クラスでシリアライズ関数を実装しているため、protectedメンバはシリアライズします。

各メンバに対して保存の有無、および、保存先の指定が可能です。「保存先指定について 」を参照下さい。

2.侵入型半自動クラス

これはシリアライズするためにクラス定義への前準備が必要ですが、クラス定義のバージョン管理が可能になる使い方です。(バージョン管理については「クラスのアップデート/バージョン・アップ方法 」を参照下さい。)

クラス定義のバージョン管理が不要な間は原則として使う必要はありません。
例外は下記です。

  • privateメンバもシリアライズしたい時
  • メンバ変数を「順序対応」にしたい時

1-2-1.クラスについて 」で説明したように、シリアライズ・データとプログラム間でメンバ変数を対応する方法として2種類サポートしています。

名前対応の定義例 (source/reference_and_test/basic/test_class_variation.h)
侵入型半自動クラスを「名前対応」で定義する時はTHEOLIZER_INTRUSIVE()マクロを使います。

class HalfAutoNameTutorial
{
int mHalfAutoNamePrivate;
protected:
int mHalfAutoNameProtected;
public:
int mHalfAutoNamePublic;
HalfAutoNameTutorial() :
mHalfAutoNamePrivate(0),
mHalfAutoNameProtected(0),
mHalfAutoNamePublic(0)
{ }
void setNonPublic(int iHalfAutoNamePrivate, int iHalfAutoNameProtected);
void checkNonPublic(int iHalfAutoNamePrivate, int iHalfAutoNameProtected);
// 侵入型半自動 指定
THEOLIZER_INTRUSIVE(CS, (HalfAutoNameTutorial), 1);
};

順序対応の定義例 (source/reference_and_test/basic/test_class_variation.h)
侵入型半自動クラスを「名前対応」で定義する時はTHEOLIZER_INTRUSIVE_ORDER()マクロを使います。

class HalfAutoOrderTutorial
{
int mHalfAutoOrderPrivate;
protected:
int mHalfAutoOrderProtected;
public:
int mHalfAutoOrderPublic;
HalfAutoOrderTutorial() :
mHalfAutoOrderPrivate(0),
mHalfAutoOrderProtected(0),
mHalfAutoOrderPublic(0)
{ }
void setNonPublic(int iHalfAutoOrderPrivate, int iHalfAutoOrderProtected);
void checkNonPublic(int iHalfAutoOrderPrivate, int iHalfAutoOrderProtected);
// 侵入型半自動 指定
THEOLIZER_INTRUSIVE_ORDER(CS, (HalfAutoOrderTutorial), 1);
};

THEOLIZER_INTRUSIVE()とTHEOLIZER_INTRUSIVE_ORDER()は、最後にprivate: で終わっています。ですので、クラス定義の最後に置くことをお勧めします。

これらのマクロは次のように使います。

それぞれのパラメータは以下の通りです。

  • dAnno
    メンバ保存の指定方法のオプションです。詳しくは「保存先指定について 」を参照下さい。
    • CS : デフォルトで保存します。(通常はこちらを指定して下さい。)
    • CN : デフォルトは保存しません。(保存するメンバを指定します)
  • dClass
    自クラス名を指定します。C++はメンバ関数の外クラス定義内で自クラスにアクセスできないため、指定が必要なのです。クラス名の外側に必ず()を付けて下さい。
  • dLastVersionNo
    最新版のローカル・バージョン番号を指定します。最初は1を指定して下さい。
    クラスのアップデート/バージョン・アップ方法 」で使い方を説明します。

保存処理/回復処理は非侵入型完全自動と同じですので、省略します。

保存結果 (<ビルド・フォルダ>/Testing/tutorise_class_variation.json)
保存されたシリアライズ・データの該当部分です。
privateメンバも含めて保存されています。

{
"mHalfAutoNamePrivate":-123,
"mHalfAutoNameProtected":-456,
"mHalfAutoNamePublic":-789
}


3.非侵入型手動クラス

これは自動でシリアライズすることが困難なクラスに対応するために設けました。
例えば、std::vector<>を自動的に適切なシリアライズをすることは現実的ではありません。そのような場合に保存/回復処理を手動で記述するための仕組みです。
使い方が難しいため、どうしても必要な時だけお使い下さい。(クラス・テンプレートにも対応しているのですが、使い方の難易度が高いこと、および、十分なテストを行うには時間が掛かりそうですので、現時点ではクラス・テンプレート対応は非公開と致します。)

下記の手順で保存/回復処理を記述することを推奨します。

  1. プロジェクトをビルド・エラーがでない状態にして下さい。
    シリアライズ対象クラスの定義を行って下さい。そして、ビルド・エラーをなくすことをお勧めします。

  2. 対象とするクラスをTHEOLIZER_NON_INTRUSIVE_ORDER()マクロで指定します。

  3. 一度ビルドします。この時、未定義エラーが出ます。
    TheolizerNonIntrusive<対象クラス名>::TheolizerUserDefine を含むエラーになります。
    この部分はテンプレートを使っていることもあり、あまり適切なエラー・メッセージになりません。たいへん申し訳ないですが、注意深くご使用下さい。

  4. 自動生成されたソース内にある未定義エラーのクラス定義雛形をコピーします。
    これは保存/回復処理の記述先関数の枠組み定義です。
    これをTHEOLIZER_NON_INTRUSIVE_ORDER()マクロの直後へコピーして下さい。

  5. 上記のクラス内に保存/回復処理を記述して下さい。

  6. 後は通常通りTHEOLIZER_PROCESS()シリーズ・マクロでシリアライズ処理します。

3-1.非侵入型手動クラスの定義例

サンプル・ソースは source/reference_and_test/basic/test_class_variation.h です。
非侵入型手動クラスの定義例です。

class ManualTutorial
{
public:
int mManualPublic;
ManualTutorial() :
mManualPublic(0)
{ }
};

3-2.非侵入型手動クラスのシリアライズ指定

サンプルですのでクラス定義に続けてシリアライズ指定していますが、実際にはシリアライズ指定はクラス定義より後であれば離れていてもよいです。

THEOLIZER_NON_INTRUSIVE_ORDER((ManualTutorial), 1);

THEOLIZER_NON_INTRUSIVE_ORDER()マクロは次のように使います。

パラメータは以下の通りです。

3-3.自動生成される保存/回復処理用関数の雛形例

サンプルは <ビルド・フォルダ>/reference_and_test/basic/test_class_variation.cpp.theolizer.hpp に生成されます。
ManualTutorialクラスに対する部分は下記です。

#ifdef THEOLIZER_WRITE_CODE // ###### ManualTutorial ######
#if false // Sample of save/load function.
template<class tBaseSerializer, class tTheolizerVersion>
struct TheolizerNonIntrusive<ManualTutorial>::TheolizerUserDefine<tBaseSerializer, tTheolizerVersion, 1>
{
// Save members.
static void saveClassManual
(
tBaseSerializer& iSerializer,
typename tTheolizerVersion::TheolizerTarget const*const& iInstance
)
{
}
// Load members.
static void loadClassManual
(
tBaseSerializer& iSerializer,
typename tTheolizerVersion::TheolizerTarget*& oInstance
)
{
if (!oInstance) oInstance=new typename tTheolizerVersion::TheolizerTarget();
}
};
#endif // Sample of save/load function.
#define THEOLIZER_GENERATED_LAST_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kLastVersionNo,1)
#define THEOLIZER_GENERATED_CLASS_TYPE ManualTutorial
// ---<<< Version.1 >>>---
#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,1)
#define THEOLIZER_GENERATED_CLASS_NAME()\
THEOLIZER_INTERNAL_CLASS_NAME((u8"ManualTutorial"))
#include <theolizer/internal/version_manual.inc>
#undef THEOLIZER_GENERATED_VERSION_NO
#endif//THEOLIZER_WRITE_CODE // ###### ManualTutorial ######

3-4.クラス定義雛形のコピー

上記雛形の #if false // Sample of save/load function. の次の行から、 #endif // Sample of save/load function. の前の行までを、THEOLIZER_NON_INTRUSIVE_ORDER((ManualTutorial), 1);の直後へコピーします。

3-5.保存/回復処理関数の記述

雛形をコピーしたsave/load関数にトップ・レベルと同じく下記マクロ群を用いて、必要な情報を保存/回復します。

  • THEOLIZER_PROCESS
  • THEOLIZER_PROCESS_POINTEE
  • THEOLIZER_PROCESS_OWNER

また、非侵入型手動専用のシリアライズ・マクロがあります。

  • THEOLIZER_PROCESS_BASE

THEOLIZER_PROCESS_BASE(dSerializer, dBase, dInstance)

  • dSerializer : シリアライザのインスタンスを指定します。
  • dBase : 保存/回復する基底クラスをクラス名で指定します。
  • dInstance : 保存/回復するインスタンスを指定します。

保存/回復処理のサンプルです。

// ---<<< シリアライズ用の手動記述 >>>---
// 非侵入型手動クラスの指定
THEOLIZER_NON_INTRUSIVE_ORDER((ManualTutorial), 1);
// 保存処理/回復処理関数
template<class tBaseSerializer, class tTheolizerVersion>
struct TheolizerNonIntrusive<ManualTutorial>::
TheolizerUserDefine<tBaseSerializer, tTheolizerVersion, 1>
{
// Save members.
static void saveClassManual
(
tBaseSerializer& iSerializer,
typename tTheolizerVersion::TheolizerTarget const*const& iInstance
)
{
THEOLIZER_PROCESS(iSerializer, iInstance->mManualPublic);
}
// Load members.
static void loadClassManual
(
tBaseSerializer& iSerializer,
typename tTheolizerVersion::TheolizerTarget*& oInstance
)
{
if (!oInstance) oInstance=new typename tTheolizerVersion::TheolizerTarget();
THEOLIZER_PROCESS(iSerializer, oInstance->mManualPublic);
}
};

tTheolizerVersion::TheolizerTarget は、指定した対象クラス型へ展開されます。
この例ではManualTutorialです。constや参照がついていて分かりにくいですが、意味的には下記となります。

  • saveClassManual() 関数のiInstance : ManualTutorialへのポインタ
  • loadClassManual() 関数のoInstance : ManualTutorialへのポインタへの参照

oInstanceにnullptrが設定されていた場合は、領域を獲得して下さい。
その際、一部の回復処理を先に行い、その値を使ってコンストラクタしてポインタを返却するため、ポインタへの参照にしています。

3-6.保存/回復処理例

保存処理/回復処理は非侵入型完全自動と同じですので、省略します。

保存結果 (<ビルド・フォルダ>/Testing/tutorise_class_variation.json)
保存されたシリアライズ・データの該当部分です。
順序対応なのでメンバ変数名は出力されません。mManualPublicに100を設定して保存していますので、100のみが出力されます。

[
100
]


4.それらを派生したり包含したりしたクラス

各種クラスの派生、および、包含(メンバ変数として他のクラス型を用いる)は、通常通りです。

各種クラスを派生/包含した例 (source/reference_and_test/basic/test_class_variation.h)
FullAutoTutorialをpublic継承、HalfAutoNameTutorialをprivate継承、HalfAutoOrderTutorialをprivateメンバ、ManualTutorialをpublicメンバとした非侵入型完全自動クラスです。
protectedを定義していませんが、protected継承、protectedメンバの両方とも使えます。

class DerivedClass : public FullAutoTutorial, private HalfAutoNameTutorial
{
HalfAutoOrderTutorial mHalfAutoOrderTutorial;
public:
ManualTutorial mManualTutorial;
DerivedClass() :
FullAutoTutorial(),
HalfAutoNameTutorial(),
mHalfAutoOrderTutorial(),
mManualTutorial()
{ }
void setNonPublic
(
int iHalfAutoNamePrivate,
int iHalfAutoNameProtected,
int iHalfAutoOrderPrivate,
int iHalfAutoOrderProtected
);
void checkNonPublic
(
int iHalfAutoNamePrivate,
int iHalfAutoNameProtected,
int iHalfAutoOrderPrivate,
int iHalfAutoOrderProtected
);
};

保存処理例 (source/reference_and_test/basic/test_class_variation.cpp の tutoriseClassVariation() 関数)
上記のDerivedClassを保存するコード例です。

{
std::ofstream aStream("tutorise_class_variation.json");
theolizer::JsonOSerializer<> aSerializer(aStream);
// 派生/包含クラス
DerivedClass aDerivedClass;
aDerivedClass.FullAutoTutorial::setNonPublic(200, 201);
aDerivedClass.mFullAutoPublic=202;
aDerivedClass.setNonPublic(203, 204, 205, 206);
aDerivedClass.mManualTutorial.mManualPublic=207;
THEOLIZER_PROCESS(aSerializer, aDerivedClass);
}

保存結果 (<ビルド・フォルダ>/Testing/tutorise_class_variation.json)
上記で保存されたシリアライズ・データの該当部分です。
名前対応ですので、基底クラスの定義順序変更に備え基底クラスは()付きでクラス名で保存しています。
非侵入型ですのでprivate継承したクラス、およひ、privateメンバは保存されません。

{
"(FullAutoTutorial)":{
"mFullAutoProtected":201,
"mFullAutoPublic":202
},
"mManualTutorial":[
207
]
}

回復処理例 (source/reference_and_test/basic/test_class_variation.cpp の tutoriseClassVariation() 関数)
上記のFullAutoTutorialを回復して値をチェックするコード例です。

{
std::ifstream aStream("tutorise_class_variation.json");
theolizer::JsonISerializer<> aSerializer(aStream);
// 派生/包含クラス
DerivedClass aDerivedClass;
THEOLIZER_PROCESS(aSerializer, aDerivedClass);
// 基底クラスFullAutoTutorialのcheckNonPublic()関数呼び出し
aDerivedClass.FullAutoTutorial::checkNonPublic(0, 201);
THEOLIZER_EQUAL(aDerivedClass.mFullAutoPublic, 202);
aDerivedClass.checkNonPublic(0, 0, 0, 0);
THEOLIZER_EQUAL(aDerivedClass.mManualTutorial.mManualPublic, 207);
}

private継承したクラス、およひ、privateメンバは保存されないため、デフォルト・コンストラクタで生成された値(0)のまま変化しませんので、0と一致することを確認しています。


5.網羅的な使用例(自動テスト)の説明

網羅的な自動テスト用のクラスはsource/reference_and_test/basic/test_class_variation.h ファイルの「単独テスト」以降で定義しています。
また、テスト処理はsource/reference_and_test/basic/test_class_variation.cpp ファイルで定義しており、saveClassVariation()関数で保存処理、loadClassVariation()関数で回復処理と値の検証を行っています。

5-1.単独テスト

単独テストの先頭でDEFINE_MEMBERS()マクロを定義しています。

これは、全てのプリミティブ型、幾つかの基本的なenum型とクラスについて、単独、および、配列を定義するためのマクロです。このマクロを展開する直前でDEFINE()とARRAY()マクロを定義することで各々について、メンバ変数の定義、初期化、保存、回復、値チェックするコードを生成しています。

#define DEFINE_MEMBERS() \
/* 文字型 */ \
DEFINE(char, mChar, 0, 110) \
DEFINE(signed char, mSChar, 0, -111) \
DEFINE(unsigned char, mUChar, 0U, 112U) \
DEFINE(wchar_t, mWchar, 0, 1100) \
DEFINE(char16_t, mChar16, 0U, 1101U) \
DEFINE(char32_t, mChar32, 0U, 1102U) \
/* 整数型 */ \
DEFINE(bool, mBool, false, true) \
DEFINE(short, mShort, 0, -2100) \
DEFINE(unsigned short, mUShort, 0U, 2100U) \
DEFINE(int, mInt, 0, -3100) \
DEFINE(unsigned int, mUInt, 0U, 3100U) \
DEFINE(long, mLong, 0L, -4100L) \
DEFINE(unsigned long, mULong, 0UL, 4100UL) \
DEFINE(long long, mLongLong, 0LL, -5100LL) \
DEFINE(unsigned long long, mULongLong, 0ULL, 5100ULL) \
/* 浮動小数点型 */ \
DEFINE(float, mFloat, 0.0F, 2.23456F) \
DEFINE(double, mDouble, 0.0, 2.23456789012345) \
DEFINE(long double, mLongDouble, 0.0L, 2.23456789012345678L) \
/* 文字列型 */ \
DEFINE(std::string, mString, u8"", u8"UTF-8a") \
DEFINE(std::wstring, mWstring, L"", L"UTF-16/32a") \
DEFINE(std::u16string, mU16string, u"", u"UTF-16a") \
DEFINE(std::u32string, mU32string, U"", U"UTF-32a") \
/* enum型 */ \
DEFINE(NormalEnum, mNormalEnum, eneZero, eneOne) \
DEFINE(ScopedEnum, mScopedEnum, ScopedEnum::ZERO, ScopedEnum::TWO) \
/* クラス */ \
DEFINE(ClassBasicTest, mClassBasicTest, \
ClassBasicTest(), ClassBasicTest(1, "1", eneOne)) \
/* --- 配列 --- */ \
/* 文字型 */ \
ARRAY(char, mChar, 5, 0, 120) \
ARRAY(signed char, mSChar, 6, 0, -121) \
ARRAY(unsigned char, mUChar, 7, 0U, 112U) \
ARRAY(wchar_t, mWchar, 8, 0, 1100) \
ARRAY(char16_t, mChar16, 9, 0U, 1101U) \
ARRAY(char32_t, mChar32, 10, 0U, 1102U) \
/* 整数型 */ \
ARRAY(bool, mBool, 4, false, true) \
ARRAY(short, mShort, 5, 0, -2200) \
ARRAY(unsigned short, mUShort, 6, 0U, 2200U) \
ARRAY(int, mInt, 7, 0, -3200) \
ARRAY(unsigned int, mUInt, 8, 0U, 3200U) \
ARRAY(long, mLong, 9, 0L, -4200L) \
ARRAY(unsigned long, mULong, 10, 0UL, 4200UL) \
ARRAY(long long, mLongLong, 11, 0LL, -5200LL) \
ARRAY(unsigned long long, mULongLong, 12, 0ULL, 5200ULL) \
/* 浮動小数点型 */ \
ARRAY(float, mFloat, 5, 0.0F, 3.23456F) \
ARRAY(double, mDouble, 6, 0.0, 3.23456789012345) \
ARRAY(long double, mLongDouble,7, 0.0L, 3.23456789012345678L) \
/* 文字列型 */ \
ARRAY(std::string, mString, 5, u8"", u8"UTF-8a") \
ARRAY(std::wstring, mWstring, 6, L"", L"UTF-16/32a") \
ARRAY(std::u16string, mU16string, 7, u"", u"UTF-16a") \
ARRAY(std::u32string, mU32string, 8, U"", U"UTF-32a") \
/* enum型 */ \
ARRAY(NormalEnum, mNormalEnum, 5, eneZero, eneOne) \
ARRAY(ScopedEnum, mScopedEnum, 6, ScopedEnum::ZERO, ScopedEnum::TWO) \
/* クラス */ \
ARRAY(ClassBasicTest, mClassBasicTest, 5, \
ClassBasicTest(), ClassBasicTest(1, "1", eneOne))

単独テストの内容は、以下の通りです。

  1. THEOLIZER_PROCESS()の使用方法 」の網羅テストと同じプリミティブとenum型、クラス、配列のセットの定義
    多数用いるのでDEFINE_MEMBERS()マクロで定義しています。

  2. 非侵入型完全自動クラス(FullAuto)
    DEFINE_MEMBERS()マクロのメンバについて、private, protected, publicのそれぞれに定義しています。
    ArrayOnly0(非侵入型完全自動クラス)クラスの配列メンバをpublicとして定義しています。
    privateメンバ以外は保存/回復でき、privateメンバは初期値が維持されることを検証しています。

  3. 侵入型半自動-名前対応クラス(HalfAutoName)
    DEFINE_MEMBERS()マクロのメンバについて、private, protected, publicのそれぞれに定義しています。
    ArrayOnly1(非侵入型完全自動クラス)クラスの配列メンバをpublicとして定義しています。
    全てのメンバが保存/回復できることを検証しています。

  4. 侵入型半自動-順序対応クラス(HalfAutoorder)
    DEFINE_MEMBERS()マクロのメンバについて、private, protected, publicのそれぞれに定義しています。
    ArrayOnly2(非侵入型完全自動クラス)クラスの配列メンバをpublicとして定義しています。
    全てのメンバが保存/回復できることを検証しています。

  5. 非侵入型手動クラス(Manual)
    DEFINE_MEMBERS()マクロのメンバについて、public定義しています。
    ArrayOnly3(非侵入型完全自動クラス)クラスの配列メンバをpublicとして定義しています。
    全てのメンバの保存/回復処理を実装しています。
    全てのメンバが保存/回復できることを検証しています。
    (非侵入型手動クラスで、private/protectedメンバをシリアライズするコードを記述するとコンパイルできないため、これらのメンバの実装とシリアライズ・テストは行なっていません。)


ArrayOnlyについて
他のクラスメンバに配列としてのみ使用される非侵入型完全自動クラスのソース自動生成に不具合が発生したため、その再発防止用として実装しています。

5-2.二重組み合わせテスト

5-2-1.二重組み合わせテスト用のヘルパー・クラス

非侵入型完全自動(BaseFullAuto)、侵入型半自動(BaseHalfAuto)、非侵入型手動(BaseManual)のクラス・テンプレートを定義しています。
これらは全てテンプレート・パラメータとしてint型の非型パラメータを1つ持ち、メンバ変数の初期値を決めてます。

これらのBaseXxxクラス群を派生したクラスは更に別のクラスへ派生されます。その際、同じ基底クラスを複数の派生クラスを経由して1つの派生クラスに継承されることは許されないため、この非型パラメータを用いて別クラスにしています。
例えば、下記のように定義すると、BaseFullAutoがDrivedFullAutoとDrivedHalfAutoを経由してTripledFullAutoへ継承されるためエラーになります。それを防ぐためにBaseFullAutoをクラス・テンプレートとし、DrivedFullAutoとDrivedHalfAutoへの継承は別実体としています。

BaseFullAuto { };
DrivedFullAuto : public BaseFullAuto { };
DrivedHalfAuto : public BaseFullAuto { };
TripledFullAuto : public DrivedFullAuto, public DrivedHalfAuto { };

BaseFullAutoは侵入型半自動クラスPrivateInheritanceをprivate継承しています。
BaseManualは侵入型半自動クラスProtectedInheritanceをprotected継承しています。
侵入型を非侵入型へprivate/protected継承するとMinGWでビルド・エラーになる不具合が発生したため、その再発防止用です。

侵入型内部で定義しているstatic変数(kIsTheolizer)の有無で侵入型であることを判定していたのですが、private継承しているため非侵入型クラスにkIsTheolizerがprivateで存在していることになります。
kIsTheolizerはアクセスできないのでSFINAEにより合致しないことを期待したのですが、「privateなのでアクセスできないエラー」となってしまいました。他の手段で判定し回避しました。

5-2-2.二重組み合わせテスト本体

内容は、以下の通りです。

  1. 非侵入型完全自動クラス(DerivedFullAuto)
    BaseFullAuto, BaseHalfAuto, BaseManualのそれぞれを、private, protected, public継承しています。合計9個のクラスを継承しています。
    BaseFullAuto, BaseHalfAuto, BaseManualのそれぞれを、private, protected, publicメンバ変数として定義しています。
    private継承したクラス、および、DerivedFullAutoのprivateメンバ変数は全て初期値が維持されること、それら以外のクラスについてはBaseFullAutoのprivateメンバ以外は全て保存/回復されること、BaseFullAutoのprivateメンバは初期値が維持されることを検証しています。

  2. 侵入型半自動クラス(DerivedHalfAuto)
    BaseFullAuto, BaseHalfAuto, BaseManualのそれぞれを、private, protected, public継承しています。合計9個のクラスを継承しています。
    BaseFullAuto, BaseHalfAuto, BaseManualのそれぞれを、private, protected, publicメンバ変数として定義しています。
    全ての継承したクラス、および、DerivedHalfAutoのメンバ変数において、BaseFullAuto型のprivateメンバは全て初期値が維持されること、それら以外については全て保存/回復されることを検証しています。

  3. 非侵入型手動クラス(DerivedManual)
    BaseFullAuto, BaseHalfAuto, BaseManualのそれぞれを、public継承しています。合計3個のクラスを継承しています。
    BaseFullAuto, BaseHalfAuto, BaseManualのそれぞれを、publicメンバ変数として定義しています。
    全ての基底クラスと全てのメンバ変数について保存/回復処理関数を実装しています。 BaseFullAutoのprivateメンバは全て初期値が維持されること、それら以外については全て保存/回復されることを検証しています。

5-3.三重組み合わせテスト

DerivedFullAuto, DerivedHalfAuto, DerivedManualを更に組み合わせて以下の自動テストを行います。

  1. 非侵入型完全自動クラス(TripledFullAuto)
    DrivedFullAuto, DrivedHalfAuto, DrivedManualのそれぞれを、public継承しています。合計3個のクラスを継承しています。
    DrivedFullAuto, DrivedHalfAuto, DrivedManualのそれぞれを、private, protected, publicメンバ変数として定義しています。
    TripledFullAutoクラスのprivateメンバ変数は全て初期値が維持されること、それら以外について、非侵入型完全自動クラスのprivateメンバは初期値が維持されること、それら以外については全て保存/回復されることを検証しています。

  2. 侵入型半自動クラス(TripledHalfAuto)
    DrivedFullAuto, DrivedHalfAuto, DrivedManualのそれぞれを、public継承しています。合計3個のクラスを継承しています。
    DrivedFullAuto, DrivedHalfAuto, DrivedManualのそれぞれを、private, protected, publicメンバ変数として定義しています。
    非侵入型完全自動クラスのprivateメンバは全て初期値が維持されること、それら以外については全て保存/回復されることを検証しています。

  3. 非侵入型手動クラス(TripledManual)
    DrivedFullAuto, DrivedHalfAuto, DrivedManualのそれぞれを、public継承しています。合計3個のクラスを継承しています。
    DrivedFullAuto, DrivedHalfAuto, DrivedManualのそれぞれを、publicメンバ変数として定義しています。
    全ての基底クラスと全てのメンバ変数について保存/回復処理関数を実装しています。
    非侵入型完全自動クラスのprivateメンバは全て初期値が維持されること、それら以外については全て保存/回復されることを検証しています。