Theolizer  Version.1.1.3
serializer for C++ / Do you want to update your classes easily ?
スマート・ポインタ、および、標準コンテナ・サポートの使い方

ここでは、スマート・ポイントと標準コンテナをシリアライズするための機能について説明します。

基本は単にシリアライズするだけです。特別な操作は必要ありませんが、コンテナについては2つ応用的な使い方があります。

  • コンテナへ保存する要素をオブジェクト追跡する場合
  • 保存先指定機能(保存先指定について )により分割保存したコンテナのデータを合成する場合


1.スマート・ポインタのシリアライズ方法

Theolizerはスマート・ポインタが管理するポインタをオーナー指定ポインタとしてシリアライズします。シリアライズする手順は通常のクラスと同じですが、少し注意事項があります。

スマート・ポインタをシリアライズするために、theolizer/memory.hをインクルードして下さい。

  • unique_ptr
    unique_ptrは通常通りシリアライズ可能です。
    なお、std::unique_ptr<T[]>はサポートしていません。T[]をシリアライズするためには、その要素数が必要ですが管理されていないため、対応できないのです。
  • shared_ptr
    同じインスタンスを管理するshared_ptrを保存し回復すると、その状況を回復します。
    なお、同じインスタンスを管理するshared_ptrは同じオジェクト追跡単位内でシリアライズするようにして下さい。ポリモーフィズムにより回復処理中にポインタが置き換わった場合、シリアライズされていなかったshared_ptrとの共有状態が解除されます。
  • weak_ptr
    バインドしているshared_ptrをシリアライズします。ですので、shared_ptrと同様同じインスタンスを管理するものは同じオブジェクト追跡単位内でシリアライズして下さい。

スマート・ポインタの保存と回復処理(source/reference_and_test/basic2/test_support_stl.cpp)
unique_ptr、shared_ptr、weak_ptrの保存/回復のサンプル・ソースです。

// ---<<< 保存 >>>---
{
// 保存データ生成
std::unique_ptr<int> aUnique{new int{100}};
std::shared_ptr<int> aShared0{new int{200}};
std::shared_ptr<int> aShared1{aShared0};
std::shared_ptr<int> aSharedForWeak{new int{300}};
std::weak_ptr<int> aWeak{aSharedForWeak};
// 保存
std::ofstream aStream("tutorise_support_stl.json");
theolizer::JsonOSerializer<> aSerializer(aStream);
THEOLIZER_PROCESS(aSerializer, aUnique);
THEOLIZER_PROCESS(aSerializer, aShared0);
THEOLIZER_PROCESS(aSerializer, aShared1);
THEOLIZER_PROCESS(aSerializer, aSharedForWeak);
THEOLIZER_PROCESS(aSerializer, aWeak);
aSerializer.clearTracking();
}
// ---<<< 回復 >>>---
{
// 回復先データ生成
std::unique_ptr<int> aUnique{};
std::shared_ptr<int> aShared0{};
std::shared_ptr<int> aShared1{};
std::shared_ptr<int> aSharedForWeak{};
std::weak_ptr<int> aWeak{};
// 回復
std::ifstream aStream("tutorise_support_stl.json");
theolizer::JsonISerializer<> aSerializer(aStream);
THEOLIZER_PROCESS(aSerializer, aUnique);
THEOLIZER_PROCESS(aSerializer, aShared0);
THEOLIZER_PROCESS(aSerializer, aShared1);
THEOLIZER_PROCESS(aSerializer, aSharedForWeak);
THEOLIZER_PROCESS(aSerializer, aWeak);
// チェック
THEOLIZER_EQUAL(*(aUnique.get()), 100);
THEOLIZER_EQUAL(*(aShared0.get()), 200);
THEOLIZER_EQUAL_PTR(aShared1.get(), aShared0.get());
THEOLIZER_EQUAL(*(aSharedForWeak.get()), 300);
THEOLIZER_EQUAL_PTR(aWeak.lock().get(), aSharedForWeak.get());
aSerializer.clearTracking();
}

2.標準コンテナのシリアライズ方法

Theolizerは各種標準コンテナのシリアライズにも対応しています。

  • それぞれ対応するインクルードが必要です。
  • 一部の標準コンテナについて
    保存先指定による合成回復、および、各要素を被ポインタとすること(他のシリアライズ対象のポインタから指す)に対応しています。

下記にその対応表を示します。

標準コンテナヘッダ被ポインタ対応合成回復
array<theolizer/array.h>YESYES
vector同上YESYES
vector<bool><theolizer/vector.h>NONO
deque<theolizer/deque.h>YESYES
forward_list<theolizer/forward_list.h>YESYES
list<theolizer/list.h>YESYES
set<theolizer/set.h>NONO
multiset同上NONO
map<theolizer/map.h>YESYES
multimap同上NONO
unordered_set<theolizer/unordered_set.h>NONO
unordered_multiset同上NONO
unordered_map<theolizer/unordered_map.h>YESYES
unordered_multimap同上NONO

特記事項が幾つかあります。

  • キー無しコンテナは先頭から順番にデータを回復します。
    arrayはコンテナを拡張できないので、余分なシリアライス・データは破棄されます。
    それ以外のキー無しコンテナ(vector, deque, forward_list, list)はシリアライズ・データの方が多い場合、コンテナを拡張して回復します。
    両者ともシリアライズ・データの方が少ない場合、コンテナの余分な要素はそのまま保持されます。

  • mapとunordered_mapは合成回復に対応しています。
    これらはキー基準で回復しますので、保存先指定機能で分割したファイルを別々に編集した場合でも、同じキーのデータを同じインスタンスへ合成回復できます。

  • setとunordered_setについて
    これらは回復する際、シリアライズ・データを新規にコンストラクトしたインスタンスへ回復してからコンテナへ追加します。ですので、対応するシリアライズ・データがあった要素は単純回復(非合成回復)します。対応するシリアライズ・データがなかったコンテナの要素はそのまま維持されます。

  • キーの重複を許すコンテナについて
    multiset, multimap, unordered_multiset, unordered_multimapは、回復する際、一旦コンテナをクリアしてから、シリアライズ・データを回復します。これはキーの重複を許すため、シリアライズ・データと対応するコンテナの要素を特定できないためです。

2-1.標準コンテナを通常の使い方で保存/回復するサンプル・ソース

サンプル・ソース(source/reference_and_test/basic2/test_support_stl.cpp)

// ---<<< 保存 >>>---
{
// 保存データ生成
std::list<int> aList;
aList.emplace_back(100);
aList.emplace_back(200);
aList.emplace_back(300);
// 保存
std::ofstream aStream("tutorise_support_stl_0.json");
theolizer::JsonOSerializer<> aSerializer(aStream);
THEOLIZER_PROCESS(aSerializer, aList);
}
// ---<<< 回復 >>>---
{
// 回復先データ生成
std::list<int> aList;
// 回復
std::ifstream aStream("tutorise_support_stl_0.json");
theolizer::JsonISerializer<> aSerializer(aStream);
THEOLIZER_PROCESS(aSerializer, aList);
// チェック
auto itr=aList.begin();
THEOLIZER_EQUAL(*itr, 100); ++itr;
THEOLIZER_EQUAL(*itr, 200); ++itr;
THEOLIZER_EQUAL(*itr, 300); ++itr;
}

2-2.標準コンテナの要素を被ポインタとする保存/回復するサンプル・ソース

サンプル・ソース(source/reference_and_test/basic2/test_support_stl.cpp)

// ---<<< 保存 >>>---
{
// 保存データ生成
theolizer::VectorPointee<int> aVectorPointee;
aVectorPointee.emplace_back(400);
aVectorPointee.emplace_back(500);
aVectorPointee.emplace_back(600);
auto itr=aVectorPointee.begin();
int* aPtr0=&*itr++;
int* aPtr1=&*itr++;
int* aPtr2=&*itr++;
// 保存
std::ofstream aStream("tutorise_support_stl_1.json");
theolizer::JsonOSerializer<> aSerializer(aStream);
THEOLIZER_PROCESS(aSerializer, aVectorPointee);
THEOLIZER_PROCESS(aSerializer, aPtr0);
THEOLIZER_PROCESS(aSerializer, aPtr1);
THEOLIZER_PROCESS(aSerializer, aPtr2);
aSerializer.clearTracking();
}
// ---<<< 回復 >>>---
{
// 回復先データ生成
theolizer::VectorPointee<int> aVectorPointee;
int* aPtr0=nullptr;
int* aPtr1=nullptr;
int* aPtr2=nullptr;
// 回復
std::ifstream aStream("tutorise_support_stl_1.json");
theolizer::JsonISerializer<> aSerializer(aStream);
THEOLIZER_PROCESS(aSerializer, aVectorPointee);
THEOLIZER_PROCESS(aSerializer, aPtr0);
THEOLIZER_PROCESS(aSerializer, aPtr1);
THEOLIZER_PROCESS(aSerializer, aPtr2);
// チェック
auto itr=aVectorPointee.begin();
THEOLIZER_EQUAL_PTR(&*itr, aPtr0);
THEOLIZER_EQUAL(*itr, 400); ++itr;
THEOLIZER_EQUAL_PTR(&*itr, aPtr1);
THEOLIZER_EQUAL(*itr, 500); ++itr;
THEOLIZER_EQUAL_PTR(&*itr, aPtr2);
THEOLIZER_EQUAL(*itr, 600); ++itr;
aSerializer.clearTracking();
}

2-3.標準コンテナの要素を合成回復サンプル・ソース

サンプル・ソースsource/reference_and_test/basic2/test_support_stl.cpp)

// ---<<< 保存 >>>---
{
// 保存データ生成
std::map<std::string, StlTutorial> aMap;
aMap.emplace("first", StlTutorial(100, 200));
aMap.emplace("second", StlTutorial(101, 201));
aMap.emplace("third", StlTutorial(102, 202));
// 保存
std::ofstream aStreamA("tutorise_support_stl_a.json");
std::ofstream aStreamB("tutorise_support_stl_b.json");
THEOLIZER_PROCESS(aSerializerA, aMap);
THEOLIZER_PROCESS(aSerializerB, aMap);
aSerializerA.clearTracking();
aSerializerB.clearTracking();
}
// ---<<< 回復 >>>---
{
// 回復先データ生成
std::map<std::string, StlTutorial> aMap;
// 回復準備
std::ifstream aStreamA("tutorise_support_stl_a.json");
theolizer::JsonISerializer<> aSerializerA(aStreamA);
std::ifstream aStreamB("tutorise_support_stl_b.json");
theolizer::JsonISerializer<> aSerializerB(aStreamB);
// 回復(DestA)
THEOLIZER_PROCESS(aSerializerA, aMap);
// チェックA(DestBは回復されていない)
auto itr=aMap.find("first");
THEOLIZER_CHECK(itr != aMap.end(), "first");
THEOLIZER_EQUAL(itr->second.mDataA, 100);
THEOLIZER_EQUAL(itr->second.mDataB, 0);
itr=aMap.find("second");
THEOLIZER_CHECK(itr != aMap.end(), "second");
THEOLIZER_EQUAL(itr->second.mDataA, 101);
THEOLIZER_EQUAL(itr->second.mDataB, 0);
itr=aMap.find("third");
THEOLIZER_CHECK(itr != aMap.end(), "third");
THEOLIZER_EQUAL(itr->second.mDataA, 102);
THEOLIZER_EQUAL(itr->second.mDataB, 0);
// 回復(DestB)
THEOLIZER_PROCESS(aSerializerB, aMap);
// チェックB
itr=aMap.find("first");
THEOLIZER_CHECK(itr != aMap.end(), "first");
THEOLIZER_EQUAL(itr->second.mDataA, 100);
THEOLIZER_EQUAL(itr->second.mDataB, 200);
itr=aMap.find("second");
THEOLIZER_CHECK(itr != aMap.end(), "second");
THEOLIZER_EQUAL(itr->second.mDataA, 101);
THEOLIZER_EQUAL(itr->second.mDataB, 201);
itr=aMap.find("third");
THEOLIZER_CHECK(itr != aMap.end(), "third");
THEOLIZER_EQUAL(itr->second.mDataA, 102);
THEOLIZER_EQUAL(itr->second.mDataB, 202);
aSerializerA.clearTracking();
aSerializerB.clearTracking();
}


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

スマート・ポインタと標準コンテナのサポートについて、全てのシリアライザ、全ての書式でテストしています。 また、保存先指定で分割保存後、合成回復のテストも同様に全てのシリアライザ、全ての書式でテストしています。

3-1.スマート・ポインタ

3-1-1.通常

unique_ptrとweak_ptrについては、使い方説明にて保存/回復テストしています。

オーナー指定ポインタを回復する時、例えばある派生クラスAのインスタンスを指しているポインタへ異なる派生クラスBを回復する場合があります。その時、元の派生クラスAのインスタンスは解放し、派生クラスBのインスタンスをコンストラクトして回復します。 しかし、shared_ptrは他のshared_ptrとポイント先を共有しているため元のインスタンスを解放できません。そのための対策を実装していいます。(shared_ptr回復中フラグを設け、元のインスタンスはshared_ptr処理側で管理しています。) その機能のテストのため、回復先のポインタが下記の3種類についてテストします。

  • nullptrの時
  • 回復するものと同じインスタンスを指している時
  • 回復するものと異なるインスタンスを指している時

この時のシリアライズ対象クラスはSmartBaseとそれを派生したSmartDerivedです。

そして、それぞれについて、手動(トップ・レベル)、自動、手動(非トップ・レベル)による保存/回復をテストしています。 そのための補助クラスとして、SmartTestAutoとSmartTestManualを使っています。

source/reference_and_test/basic2/test_support_stl.cppでテスト関数を定義してます。

  1. 保存処理
    template<class tSerializer>
    void saveSupportStl(tSerializer& iSerializer)の前半

  2. 回復処理
    template<class tSerializer>
    void loadSupportStl(tSerializer& iSerializer)の前半

3-1-2.合成回復

unique_ptrとshared_ptrについて、それが管理するクラスをDestAとDestBに分割して保存し合成回復できることを確認しています。 source/reference_and_test/basic2/test_support_stl.cppでテスト関数を定義してます。

  1. 保存処理
    template<class tSerializer>
    void saveSupportStlDestinations(tSerializer& iSerializer)の前半

  2. 回復処理
    template<class tSerializer>
    void saveSupportStlDestinations(tSerializer& iSerializer)の前半

3-2.標準コンテナのテスト

3-2-1.通常

サポートしている全てのコンテナについて、下記について正しく保存/回復できることをテストしています。

  • 要素の型はint型とクラス(TestStl)
  • 被ポインタをサポートしているコンテナについてはオブジェクト追跡

unorderedコンテナに対応するため、TestStlはstd::hashクラスを特殊化しています。

下記関数テンプレートを使っています。

対象コンテナ関数テンプレート
arraysaveContainerFixed, loadContainerFixed
vector(bool以外), deque, list, forward_listsaveContainer, loadContainer
set, multiset, unordered_set, unordered_multisetsaveContainerSet, loadContainerSet
map, multimap, unordered_map, unordered_multimapsaveContainerMap, loadContainerMap

source/reference_and_test/basic2/test_support_stl.cppでテスト関数を定義してます。

  1. 保存処理
    template<class tSerializer>
    void saveSupportStl(tSerializer& iSerializer)の後半

  2. 回復処理
    template<class tSerializer>
    void loadSupportStl(tSerializer& iSerializer)の後半

3-2-2.合成回復

合成回復をサポートしている全てのコンテナについて、下記について正しく保存/回復できることをテストしています。

  • 要素の型はクラス(TestStlDestinations)
    TestStlDestinationsは保存先としてDestAとDestBを指定したメンバを定義しています。

下記関数テンプレートを使っています。

対象コンテナ関数テンプレート
arraysaveFixed, loadFixed
vector(bool以外), deque, list, forward_listsaveNoKey, loadNoKey
map, unordered_mapsaveKey, loadeKey

source/reference_and_test/basic2/test_support_stl.cppでテスト関数を定義してます。

  1. 保存処理
    template<class tSerializer>
    void saveSupportStlDestinations(tSerializer& iSerializer)の後半

  2. 回復処理
    template<class tSerializer>
    void loadSupportStlDestinations(tSerializer& iSerializer)の後半