2017年6月7日 Theolizer®最新版v1.1.0へ対応するために修正しました。
最新版のソース一式をGistに置いてます。

こんにちは。田原です。

今回は少し複雑な階層構造のデータとその要素へのポインタを含む構造体の保存と回復をやってみます。
一般にポインタはファイルへ保存しても回復できません。しかし、Theolizer®はオブジェクト追跡を行うことで回復できます。
ついでに例外を使わないでエラーを受け取る方法も簡単に説明します。

1.今回のデータ構造

家計簿では、いつ何を幾らで買ったか入力し、何に幾らつかったか、お金が幾ら残っているかを集計すると思います。
そこで、その「何」を「科目(Item)」とし例えば下記のような階層構造で管理してみたいと思います。下図の各箱を科目(Item)として管理します。

科目(Item)の定義サンプル

科目(Item)ツリーの定義サンプル

次に、前回定義した取引(Trade)を少し拡張します。前回は科目(Item)を1つだけ定義してましたが、それを上記の科目(Item)へのポインタ2つとしました。
デビット(増える方)科目とクレジット(減る方)科目の2つです。例えば、お米を財布のお金で購入した場合、お米が増えて財布の現金が減りますのでお米がデビット側、財布がクレジット側となります。デビットとクレジットの2つを使うことで銀行からお金を下ろしてくるような操作を記録できます。下ろしてくる操作はデビット(増える方)が財布、クレジット(減る方)が預金となるわけです。
上記の科目ツリーと下記のような取引データのサンプルを後述のsub.cppinitialize()関数で設定しています。

日付 デビット科目 金額 クレジット科目 備考
2016年8月22日 家計/支出/主食/パン 158円 家計/現金/財布 六切り1袋
2016年8月22日 家計/支出/副食/肉魚 269円 家計/現金/財布 鶏肉200g
2016年8月22日 家計/支出/副食/野菜 182円 家計/現金/財布 キャベツ1玉
2016年8月22日 家計/支出/副食/果物 113円 家計/現金/財布 バナナ1房
2016年8月22日 家計/現金/預金 254,536円 家計/収入/給料 セオライド テクノロジ
2016年8月25日 家計/現金/財布 50,000円 家計/現金/預金 生活費
2016年8月25日 家計/支出/主食/お米 4,652円 家計/現金/財布 こしひかり10Kg
2016年8月25日 家計/支出/副食/肉魚 685円 家計/現金/財布 豚肉500g
2016年8月25日 家計/支出/副食/野菜 152円 家計/現金/財布 ほうれん草
2016年8月25日 家計/支出/副食/野菜 136円 家計/現金/財布 人参

2.データ構造の定義とオブジェクト追跡について

common.hにて必要な構造体を定義しています。この中で、科目(Item)は自分の親へのポインタmParentを持ってます。そして、取引(Trade)は取引した科目(Item)を記録するため、科目(Item)へのポインタmDebitItemmCreditItemを持ってます。
これらのポインタをそのまま保存して、それを回復できることが今回の目玉です。
と言っても、特殊な操作はあまりありません。ポイント先も同じファイルへ保存していることが最大の条件です。今回は科目(Item)ツリーと取引(Trade)リストの両方を同じファイルへ保存します。上記のポインタ(mParentm, DebitItem, mCreditItem)の回復をTheolizer®にまかせています。

2-1.構造体定義

common.h
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//############################################################################
//      Theolizer解説用サンプル・プログラム2
//
//          簡単な家計簿用共通定義
//############################################################################
 
#if !defined(COMMON_H)
#define COMMON_H
 
// ***************************************************************************
//      インクルード
// ***************************************************************************
 
// 標準ライブラリ
#include <string>
 
// Theolizerライブラリ
#include <theolizer/serializer_json.h>
#include <theolizer/list.h>
 
// ***************************************************************************
//      構造体定義
// ***************************************************************************
 
#define POINTEE     THEOLIZER_ANNOTATE(FS:<>Pointee)
 
//----------------------------------------------------------------------------
//      科目管理
//----------------------------------------------------------------------------
 
struct Item
{
    Item*                           mParent;        // 親科目
    std::string                     mName;          // 科目名
    bool                            mIsAssets;      // 資産(財布や預金)
    int                             mAssetsIncrease;// 増加した資産金額
    bool                            mDoManage;      // 残高管理の有無
    int                             mAmount;        // 残高
    theolizer::ListPointee<Item>    mChildren;      // 子科目
};
 
typedef theolizer::ListPointee<Item>::iterator  ChildIterator;
 
//----------------------------------------------------------------------------
//      取引記録
//----------------------------------------------------------------------------
 
struct Date
{
    short       mYear;                  // 年
    char        mMonth;                 // 月(1-12)
    char        mDay;                   // 日(1-31)
};
 
struct Trade
{
    Date        mDate;                  // 取引日
    Item*       mDebitItem;             // 購入品(借方科目)
    int         mAmount;                // 金額
    Item*       mCreditItem;            // 財布 (貸方科目)
    std::string mNote;                  // 備考
};
 
//----------------------------------------------------------------------------
//      家計簿
//----------------------------------------------------------------------------
 
struct HouseholdAccounts
{
    Item                mItemTree  POINTEE; // 科目ツリー
    std::list<Trade>    mTradeList;         // 取引のリスト
};
 
typedef std::list<Trade>::iterator  TradeIterator;
 
// ***************************************************************************
//      関数群
// ***************************************************************************
 
//----------------------------------------------------------------------------
//      初期設定
//----------------------------------------------------------------------------
 
void initialize(HouseholdAccounts* oHouseholdAccounts);
void display(   HouseholdAccounts* iHouseholdAccounts);
void calculate( HouseholdAccounts* ioHouseholdAccounts);
 
#endif

2-2.C++の線形リスト(List)

std::list<>を使っていますので簡単に説明します。
C言語を学んでいる時に線形リストを学んだ方も多いと思います。std::list<>も線形リストです。
また、theolizer::ListPointee<>については次節で説明しますが、これもstd::list<>を少しだけ拡張したものでstd::list<>とほぼ同じ機能を提供しています。

さて、リストの要素を追跡する際、要素へのポインタを使います。そのポインタpを次の要素へ進めるにはp=p->next;のようなコードを書きますね。
std::list<>の場合、このポインタpに当たる変数はiteratorです。そして、iterator変数itrのインクリメント(++itrなど)はp=p->next;と同じ操作となります。
また*itrはC言語のポインタの場合と同様要素自体を示します。

サンプル・プロクラムではstd::list<>の下記メンバ関数を使っています。

メンバ関数 説明
begin() 先頭の要素へのiteratorが返却されます。
end() 最後の要素の次のiteratorが返却されます。
push_back(要素) 要素をリストの最後に追加します。
back() 最後の要素の「参照」が返却されます。

2-3.theolizer::ListPointeeについて補足

Theolizer®はポインタが指すオブジェクト(変数)を追跡することでポインタをファイルへ保存したり、回復したりすることができます。このオブジェクト追跡対象とする変数を明示的に指定します。
std::list<>の各要素は追跡しません。そしてtheolizer::ListPointee<>の要素はオブジェクト追跡します。この2つの相違点はこれだけです。

2-4.POINTEEについて

common.hの25行目を見て下さい。次のようにPOINTEEマクロを定義しています。
#define POINTEE THEOLIZER_ANNOTATE(FS:<>Pointee)
これはあまり長い文があると分かりにくくなるので短縮のためのマクロです。

これを70行目で使っています。
mItemTreeは配下のmChildrenが持つmParentからポイントされます。
そして、mParentはファイルへ保存されますので、これを適切に回復するためmItemTreeをオブジェクト追跡する必要があります。
そこでPOINTEE(THEOLIZER_ANNOTATE(FS:<>Pointee))指定することでこの変数をオブジェクト追跡するようTheolizerへ指定しています。

3.保存/回復処理

main.cppにて今回のデータ構造の保存/回復処理と、各種設定・表示・計算関数呼び出しを行っています。
今回はオブジェクト追跡を行いますので、その処理にミスがないことを確認するため、clearTracking()を呼び出して下さい。もし、ミスがある時(ポインタを保存しているのにボイント先が保存されていないなど)、エラーが通知されます。Theolizer®はデフォルトでは例外でエラーを通知しますので、標準ではここで例外がthrowされます。
しかし、例外を発行しない方法もあります。最初にTheolizer®を用意する(コンストラクトする)時に例外を発行しないよう指示しておき、必要に応じてgetError()でエラー・チェックして下さい。エラーが見つかったら、必ずresetError()して下さい。resetError()を忘れるとTheolizer®を破棄する(デストラクトする)時にプログラムを異常終了させています。(エラー・チェック漏れを防ぐことが目的です。)

3-1.データの保存と回復、および、簡単な集計と表示

main.cpp
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//############################################################################
//      Theolizer解説用サンプル・プログラム2
//
//          メイン処理(データ保存/回復サンプル)
//############################################################################
 
// ***************************************************************************
//      インクルード
// ***************************************************************************
 
// 標準ライブラリ
#include <iostream>
#include <fstream>
 
// 共通定義
#include "common.h"
 
// Theolizer自動生成先
#include "main.cpp.theolizer.hpp"
 
// ***************************************************************************
//      メイン
// ***************************************************************************
 
int main(int argc, char* argv[])
{
//----------------------------------------------------------------------------
//      家計簿設定
//----------------------------------------------------------------------------
 
    // 保存用家計簿
    HouseholdAccounts   aHouseholdAccountsSave;
 
    // 初期設定(科目と取引データを設定している)
    initialize(&aHouseholdAccountsSave);
 
//----------------------------------------------------------------------------
//      保存
//----------------------------------------------------------------------------
 
    // 保存処理
    {
        // 保存先のファイルをオープンする
        std::ofstream   aStream("HouseholdAccounts.json");
 
        // パラメータの意味
        //  aStream                             出力先のファイル
        //  theolizer::CheckMode::NoTypeCheck   型チェック無し
        //  false                               PrettyPrintする(デフォルト)
        //  true                                例外を発行しない
        theolizer::JsonOSerializer<> js(aStream, theolizer::CheckMode::NoTypeCheck, false, true);
 
        // 家計簿をHouseholdAccounts.jsonファイルへ保存する
        THEOLIZER_PROCESS(js, aHouseholdAccountsSave);
 
        // オブジェクト追跡の締め
        js.clearTracking();
 
        // エラー・チェック(例外を禁止している場合に必要)
        theolizer::ErrorInfo aErrorInfo=js.getErrorInfo();
        if (aErrorInfo)
        {
            std::cout << aErrorInfo.getMessage() << std::endl;
            js.resetError();    // チェックしたことを通知する
        }
    }
 
//----------------------------------------------------------------------------
//      回復
//----------------------------------------------------------------------------
 
    // 回復用家計簿(正確に回復できていることを示すため、保存用と別領域)
    HouseholdAccounts   aHouseholdAccountsLoad;
 
    // 回復処理
    {
        // 回復元のファイルをオープンする
        std::ifstream   aStream("HouseholdAccounts.json");
 
        // パラメータの意味
        //  aStream                             入力元のファイル
        //  true                                例外を発行しない
        theolizer::JsonISerializer<>  js(aStream, true);
 
        // 家計簿をHouseholdAccounts.jsonファイルから回復する
        THEOLIZER_PROCESS(js, aHouseholdAccountsLoad);
 
        // オブジェクト追跡の締め
        js.clearTracking();
 
        // エラー・チェック(例外を禁止している場合に必要)
        theolizer::ErrorInfo aErrorInfo = js.getErrorInfo();
        if (aErrorInfo)
        {
            std::cout << aErrorInfo.getMessage() << std::endl;
            js.resetError();    // チェックしたことを通知する
        }
    }
 
//----------------------------------------------------------------------------
//      内容表示と簡単な集計処理
//----------------------------------------------------------------------------
 
    std::cout << u8"<<< 保存したサンプル・データ >>>\n";
    display(&aHouseholdAccountsSave);
 
    std::cout << u8"\n\n<<< 回復したサンプル・データ >>>\n";
    display(&aHouseholdAccountsLoad);
 
    std::cout << u8"\n\n<<< サンプルの集計結果 >>>\n";
    calculate(&aHouseholdAccountsLoad);
 
    return 0;
}

4.実行結果、および、保存されたファイル

4-1.実行結果

result.log
[text gutter=”false”] <<< 保存したサンプル・データ >>>
— 科目 —
家計
収入
給料
雑収入
支出
主食
お米
パン
副食
肉魚
野菜
果物
現金
財布 残高:35592
預金 残高:147135

— 取引 —
2016/08/22 家計/支出/主食/パン <- 家計/現金/財布( 158) : 六切り1袋
2016/08/22 家計/支出/副食/肉魚 <- 家計/現金/財布( 269) : 鶏肉200g
2016/08/22 家計/支出/副食/野菜 <- 家計/現金/財布( 182) : キャベツ1玉
2016/08/22 家計/支出/副食/果物 <- 家計/現金/財布( 113) : バナナ1房
2016/08/22 家計/現金/預金 <- 家計/収入/給料(254536) : セオライド テクノロジー
2016/08/25 家計/現金/財布 <- 家計/現金/預金( 50000) : 生活費
2016/08/25 家計/支出/主食/お米 <- 家計/現金/財布( 4652) : こしひかり10Kg
2016/08/25 家計/支出/副食/肉魚 <- 家計/現金/財布( 685) : 豚肉500g
2016/08/25 家計/支出/副食/野菜 <- 家計/現金/財布( 152) : ほうれん草
2016/08/25 家計/支出/副食/野菜 <- 家計/現金/財布( 136) : 人参

<<< 回復したサンプル・データ >>>
— 科目 —
家計
収入
給料
雑収入
支出
主食
お米
パン
副食
肉魚
野菜
果物
現金
財布 残高:35592
預金 残高:147135

— 取引 —
2016/08/22 家計/支出/主食/パン <- 家計/現金/財布( 158) : 六切り1袋
2016/08/22 家計/支出/副食/肉魚 <- 家計/現金/財布( 269) : 鶏肉200g
2016/08/22 家計/支出/副食/野菜 <- 家計/現金/財布( 182) : キャベツ1玉
2016/08/22 家計/支出/副食/果物 <- 家計/現金/財布( 113) : バナナ1房
2016/08/22 家計/現金/預金 <- 家計/収入/給料(254536) : セオライド テクノロジー
2016/08/25 家計/現金/財布 <- 家計/現金/預金( 50000) : 生活費
2016/08/25 家計/支出/主食/お米 <- 家計/現金/財布( 4652) : こしひかり10Kg
2016/08/25 家計/支出/副食/肉魚 <- 家計/現金/財布( 685) : 豚肉500g
2016/08/25 家計/支出/副食/野菜 <- 家計/現金/財布( 152) : ほうれん草
2016/08/25 家計/支出/副食/野菜 <- 家計/現金/財布( 136) : 人参

<<< サンプルの集計結果 >>>
家計 : 0円の収入
家計/収入 : 254536円の収入
家計/収入/給料 : 254536円の収入
家計/収入/雑収入 : 0円の収入
家計/支出 : 6347円の消費
家計/支出/主食 : 4810円の消費
家計/支出/主食/お米 : 4652円の消費
家計/支出/主食/パン : 158円の消費
家計/支出/副食 : 1537円の消費
家計/支出/副食/肉魚 : 954円の消費
家計/支出/副食/野菜 : 470円の消費
家計/支出/副食/果物 : 113円の消費
家計/現金 : 248189円の増加
家計/現金/財布 : 43653円の増加 残高は 79245円
家計/現金/預金 : 204536円の増加 残高は351671円
[/text] (桁が揃ってなく見難くてすいません。)

4-2.Theolzierにて保存されたJsonファイル

HouseholdAccounts.json
[text] {
"SerialzierName":"JsonTheolizer",
"GlobalVersionNo":1,
"TypeInfoList":[1] }
{
"mItemTree":[1,{
"mParent":0,
"mName":"家計",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
3,
[2,{
"mParent":1,
"mName":"収入",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
2,
[3,{
"mParent":2,
"mName":"給料",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
0
] }],
[4,{
"mParent":2,
"mName":"雑収入",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
0
] }] ] }],
[5,{
"mParent":1,
"mName":"支出",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
2,
[6,{
"mParent":5,
"mName":"主食",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
2,
[7,{
"mParent":6,
"mName":"お米",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
0
] }],
[8,{
"mParent":6,
"mName":"パン",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
0
] }] ] }],
[9,{
"mParent":5,
"mName":"副食",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
3,
[10,{
"mParent":9,
"mName":"肉魚",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
0
] }],
[11,{
"mParent":9,
"mName":"野菜",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
0
] }],
[12,{
"mParent":9,
"mName":"果物",
"mIsAssets":0,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
0
] }] ] }] ] }],
[13,{
"mParent":1,
"mName":"現金",
"mIsAssets":1,
"mAssetsIncrease":0,
"mDoManage":0,
"mAmount":0,
"mChildren":[
2,
[14,{
"mParent":13,
"mName":"財布",
"mIsAssets":1,
"mAssetsIncrease":0,
"mDoManage":1,
"mAmount":35592,
"mChildren":[
0
] }],
[15,{
"mParent":13,
"mName":"預金",
"mIsAssets":1,
"mAssetsIncrease":0,
"mDoManage":1,
"mAmount":147135,
"mChildren":[
0
] }] ] }] ] }],
"mTradeList":[
10,
{
"mDate":{
"mYear":2016,
"mMonth":8,
"mDay":22
},
"mDebitItem":8,
"mAmount":158,
"mCreditItem":14,
"mNote":"六切り1袋"
},
{
"mDate":{
"mYear":2016,
"mMonth":8,
"mDay":22
},
"mDebitItem":10,
"mAmount":269,
"mCreditItem":14,
"mNote":"鶏肉200g"
},
{
"mDate":{
"mYear":2016,
"mMonth":8,
"mDay":22
},
"mDebitItem":11,
"mAmount":182,
"mCreditItem":14,
"mNote":"キャベツ1玉"
},
{
"mDate":{
"mYear":2016,
"mMonth":8,
"mDay":22
},
"mDebitItem":12,
"mAmount":113,
"mCreditItem":14,
"mNote":"バナナ1房"
},
{
"mDate":{
"mYear":2016,
"mMonth":8,
"mDay":22
},
"mDebitItem":15,
"mAmount":254536,
"mCreditItem":3,
"mNote":"セオライド テクノロジー"
},
{
"mDate":{
"mYear":2016,
"mMonth":8,
"mDay":25
},
"mDebitItem":14,
"mAmount":50000,
"mCreditItem":15,
"mNote":"生活費"
},
{
"mDate":{
"mYear":2016,
"mMonth":8,
"mDay":25
},
"mDebitItem":7,
"mAmount":4652,
"mCreditItem":14,
"mNote":"こしひかり10Kg"
},
{
"mDate":{
"mYear":2016,
"mMonth":8,
"mDay":25
},
"mDebitItem":10,
"mAmount":685,
"mCreditItem":14,
"mNote":"豚肉500g"
},
{
"mDate":{
"mYear":2016,
"mMonth":8,
"mDay":25
},
"mDebitItem":11,
"mAmount":152,
"mCreditItem":14,
"mNote":"ほうれん草"
},
{
"mDate":{
"mYear":2016,
"mMonth":8,
"mDay":25
},
"mDebitItem":11,
"mAmount":136,
"mCreditItem":14,
"mNote":"人参"
}
] }
[/text]

5.その他のファイル

5-1.アプリケーションのメインに当たる部分

本来の家計簿アプリケーションのメイン処理に当たる部分をsub.cppに入れてます。(保存/回復処理の説明用のサンプル・プログラムですので保存/回復部分をmain.cpp、その他の部分をsub.cppとにいれてます。)
sub.cppでは下記をおこなっています。本来の家計簿アプリケーションではウィンドウに家計簿を表示し、科目ツリーのメンテナンス、および、取引リストの入力を行い、必要に応じて集計を自動的に行います。それらの代わりにデータ設定と表示、集計を行っています。

関数 説明
initialize() 科目(Item)ツリーと取引(Trade)リストに冒頭で述べたデータを設定しています。
display() 家計簿の内容を単純に表示しています。
calculate() 家計簿を集計して表示しています。
sub.cpp
[cpp] //############################################################################
// Theolizer解説用サンプル・プログラム2
//
// 補助関数群(家計簿にサンプル・データ設定/表示)
//############################################################################

// ***************************************************************************
// インクルード
// ***************************************************************************

// 標準ライブラリ
#include <iostream>
#include <fstream>

// 共通定義
#include "common.h"

// ***************************************************************************
// 家計簿にサンプル・データ設定
// ***************************************************************************

//—————————————————————————-
// find 科目
//—————————————————————————-

Item* find(Item* iItem, char const* iItemName)
{
if (iItem->mName == iItemName)
return iItem;

for (ChildIterator itr=iItem->mChildren.begin(); itr != iItem->mChildren.end(); ++itr)
{
Item* ret=find(&(*itr), iItemName);
if (ret)
return ret;
}

return NULL;
}

//—————————————————————————-
// 科目ツリー設定
// 家計
// 収入
// 給料
// 雑収入
// 支出
// 食費
// 主食
// お米
// パン
// 副食
// 肉魚
// 野菜
// 果物
// 現金
// 財布
// 預金
//—————————————————————————-

// —<<< 子科目設定 >>>—

Item* pushItem
(
Item* oItem,
char const* iItemName,
bool iIsAssets=false,
bool iDoManage=false,
int iAmount=0
)
{
Item aItem;
aItem.mParent =oItem;
aItem.mName =iItemName;
aItem.mIsAssets =iIsAssets;
aItem.mAssetsIncrease =0;
aItem.mDoManage =iDoManage;
aItem.mAmount =iAmount;

oItem->mChildren.push_back(aItem);
return &(oItem->mChildren.back());
}

// —<<< 科目ツリー設定 >>>—

void makeItems(Item* oItemTree)
{
oItemTree->mParent =NULL;
oItemTree->mName =u8"家計";
oItemTree->mIsAssets =false;
oItemTree->mAssetsIncrease =0;
oItemTree->mDoManage =false;
oItemTree->mAmount =0;

Item* aLayer0=oItemTree;
{
Item* aLayer1;
aLayer1=pushItem(aLayer0, u8"収入");
{
pushItem(aLayer1, u8"給料");
pushItem(aLayer1, u8"雑収入");
}
aLayer1=pushItem(aLayer0, u8"支出");
{
Item* aLayer2;
aLayer2=pushItem(aLayer1, u8"主食");
{
pushItem(aLayer2, u8"お米");
pushItem(aLayer2, u8"パン");
}
aLayer2=pushItem(aLayer1, u8"副食");
{
pushItem(aLayer2, u8"肉魚");
pushItem(aLayer2, u8"野菜");
pushItem(aLayer2, u8"果物");
}
}
aLayer1=pushItem(aLayer0, u8"現金", true);
{
pushItem(aLayer1, u8"財布", true, true, 35592);
pushItem(aLayer1, u8"預金", true, true, 147135);
}
}
}

//—————————————————————————-
// 取引リスト設定
//—————————————————————————-

// —<<< 取引設定サブ >>>—

bool pushTrade
(
HouseholdAccounts* oHA,
short iYear,
char iMonth,
char iDay,
char const* iDebitName,
int iAmount,
char const* iCreditName,
char const* iNote
)
{
Trade aTrade;
aTrade.mDate.mYear = iYear;
aTrade.mDate.mMonth = iMonth;
aTrade.mDate.mDay = iDay;
aTrade.mDebitItem = find(&(oHA->mItemTree), iDebitName);
aTrade.mAmount = iAmount;
aTrade.mCreditItem = find(&(oHA->mItemTree), iCreditName);
aTrade.mNote = iNote;

if (aTrade.mDebitItem == NULL)
{
std::cerr << theolizer::print(u8"商品名(%s)が不正です。", iDebitName) << std::endl;
return false;
}
if (aTrade.mCreditItem == NULL)
{
std::cerr << theolizer::print(u8"商品名(%s)が不正です。", iCreditName) << std::endl;
return false;
}

oHA->mTradeList.push_back(aTrade);
return true;
}

// —<<< 取引リスト設定 >>>—

void makeTrades(HouseholdAccounts* oHA)
{
pushTrade(oHA, 2016, 8,22, u8"パン", 158, u8"財布", u8"六切り1袋");
pushTrade(oHA, 2016, 8,22, u8"肉魚", 269, u8"財布", u8"鶏肉200g");
pushTrade(oHA, 2016, 8,22, u8"野菜", 182, u8"財布", u8"キャベツ1玉");
pushTrade(oHA, 2016, 8,22, u8"果物", 113, u8"財布", u8"バナナ1房");
pushTrade(oHA, 2016, 8,22, u8"預金", 254536, u8"給料", u8"セオライド テクノロジー");
pushTrade(oHA, 2016, 8,25, u8"財布", 50000, u8"預金", u8"生活費");
pushTrade(oHA, 2016, 8,25, u8"お米", 4652, u8"財布", u8"こしひかり10Kg");
pushTrade(oHA, 2016, 8,25, u8"肉魚", 685, u8"財布", u8"豚肉500g");
pushTrade(oHA, 2016, 8,25, u8"野菜", 152, u8"財布", u8"ほうれん草");
pushTrade(oHA, 2016, 8,25, u8"野菜", 136, u8"財布", u8"人参");
}

//—————————————————————————-
// 設定
//—————————————————————————-

void initialize(HouseholdAccounts* oHouseholdAccounts)
{
makeItems(&(oHouseholdAccounts->mItemTree));
makeTrades(oHouseholdAccounts);
}

// ***************************************************************************
// 家計簿表示
// ***************************************************************************

//—————————————————————————-
// 科目名結合
//—————————————————————————-

std::string getFullName(Item* iItem)
{
if (iItem->mParent != NULL)
{
return getFullName(iItem->mParent) + "/" + iItem->mName;
}
else
{
return iItem->mName;
}
}

//—————————————————————————-
// 科目表示
//—————————————————————————-

void displayItems(int iIndent, Item* iItem)
{
std::cout << std::string(iIndent*2, ‘ ‘) << iItem->mName;
if (iItem->mDoManage)
{
std::cout << u8" 残高:" << iItem->mAmount;
}
std::cout << "\n";
for (ChildIterator itr=iItem->mChildren.begin(); itr != iItem->mChildren.end(); ++itr)
{
displayItems(iIndent+1, &(*itr));
}
}

//—————————————————————————-
// 取引表示
//—————————————————————————-

void displayTrades(HouseholdAccounts* iHA)
{
for (TradeIterator itr=iHA->mTradeList.begin(); itr != iHA->mTradeList.end(); ++itr)
{
std::cout << theolizer::print("%04d/%02d/%02d %s <- %s(%6d) : %s\n",
itr->mDate.mYear,
itr->mDate.mMonth+0,
itr->mDate.mDay+0,
getFullName(itr->mDebitItem),
getFullName(itr->mCreditItem),
itr->mAmount,
itr->mNote);
}
}

//—————————————————————————-
// 全体表示
//—————————————————————————-

void display(HouseholdAccounts* iHouseholdAccounts)
{
std::cout << u8"— 科目 —\n";
displayItems(0, &(iHouseholdAccounts->mItemTree));

std::cout << u8"\n— 取引 —\n";
displayTrades(iHouseholdAccounts);
}

// ***************************************************************************
// 家計簿処理と結果表示
// ***************************************************************************

//—————————————————————————-
// 初期化
//—————————————————————————-

void clearIncrease(Item* iItem)
{
iItem->mAssetsIncrease=0;
for (ChildIterator itr=iItem->mChildren.begin(); itr != iItem->mChildren.end(); ++itr)
{
clearIncrease(&(*itr));
}
}

//—————————————————————————-
// 結果表示
//—————————————————————————-

void displayAmount(Item* iItem)
{
std::cout << getFullName(iItem);
if (iItem->mIsAssets)
{
if (0 <= iItem->mAssetsIncrease)
{
std::cout << theolizer::print(u8" : %6d円の増加", iItem->mAssetsIncrease);
}
else
{
std::cout << theolizer::print(u8" : %6d円の減少", -iItem->mAssetsIncrease);
}
}
else
{
if (0 <= iItem->mAssetsIncrease)
{
std::cout << theolizer::print(u8" : %6d円の収入", iItem->mAssetsIncrease);
}
else
{
std::cout << theolizer::print(u8" : %6d円の消費", -iItem->mAssetsIncrease);
}
}

if (iItem->mDoManage)
{
std::cout << theolizer::print(u8" 残高は%6d円\n", iItem->mAmount);
}
else
{
std::cout << "\n";
}
for (ChildIterator itr=iItem->mChildren.begin(); itr != iItem->mChildren.end(); ++itr)
{
displayAmount(&(*itr));
}
}

//—————————————————————————-
// 計算
//—————————————————————————-

void calculate(HouseholdAccounts* ioHouseholdAccounts)
{
// 初期化
clearIncrease(&(ioHouseholdAccounts->mItemTree));

// 集計
for (TradeIterator itr=ioHouseholdAccounts->mTradeList.begin();
itr != ioHouseholdAccounts->mTradeList.end();
++itr)
{
// —<<< 増減処理 >>>—

// 借方の資産は増える/借方の非資産は減る
for (Item* p=itr->mDebitItem; p != NULL; p=p->mParent)
{
if (p->mIsAssets)
{
p->mAssetsIncrease += itr->mAmount;
}
else
{
p->mAssetsIncrease -= itr->mAmount;
}
}

// 貸方の資産は減る/借方の非資産は増える
for (Item* p=itr->mCreditItem; p != NULL; p=p->mParent)
{
if (p->mIsAssets)
{
p->mAssetsIncrease -= itr->mAmount;
}
else
{
p->mAssetsIncrease += itr->mAmount;
}
}

// —<<< 残高処理 >>>—

// 借方
if (itr->mDebitItem->mDoManage)
{
itr->mDebitItem->mAmount += itr->mAmount;
}

// 貸方
if (itr->mCreditItem->mDoManage)
{
itr->mCreditItem->mAmount -= itr->mAmount;
}
}

displayAmount(&(ioHouseholdAccounts->mItemTree));

}
[/cpp]

5-2.CMakeLists.txt

CMakeLists.txt
[text] #[[###########################################################################
Theolizer紹介用サンプルCMakeLists.txt
]]############################################################################

if("${CMAKE_VERSION}" STREQUAL "")
set(CMAKE_VERSION, 3.5.0)
endif()
cmake_minimum_required(VERSION ${CMAKE_VERSION})

message(STATUS "BOOST_ROOT=${BOOST_ROOT}")

#—————————————————————————–
# プロジェクト設定
#—————————————————————————–

set(TARGET_NAME sample)
set(SOURCE_LIST main.cpp sub.cpp)
set(HEADER_LIST common.h)

project(${TARGET_NAME} VERSION 1.0.0)

#—————————————————————————–
# ビルド設定
#—————————————————————————–

# MSVCの通常使わないビルド・モードとZERO_CHECKプロジェクトの削除
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "Configs" FORCE)
set(CMAKE_SUPPRESS_REGENERATION TRUE)

# Theolizer
find_package(THEOLIZER)

# Options
if (${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
add_definitions(-D_UNICODE -DUNICODE)
set(CMAKE_DEBUG_POSTFIX "d")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

# MinGWの不具合(https://sourceforge.net/p/mingw-w64/discussion/723797/thread/c6b70624/#7f0a)暫定対処
if((CMAKE_COMPILER_IS_MINGW) AND (CMAKE_SIZEOF_VOID_P EQUAL 8))
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj")
endif()
endif()

# example
add_executable(${TARGET_NAME} ${SOURCE_LIST} ${HEADER_LIST})
setup_theolizer(${TARGET_NAME} StaticWithBoost)

#—————————————————————————–
# テスト実行
#—————————————————————————–

enable_testing()
add_test(NAME ${TARGET_NAME} COMMAND $<TARGET_FILE:${TARGET_NAME}>)

add_custom_target(BuildTest COMMAND "ctest" "-V" "-C" $<CONFIG>)
add_dependencies(BuildTest ${TARGET_NAME})
[/text]

5-3.自動生成されたソース

main.cpp.theolizer.hpp
[cpp] #ifdef THEOLIZER_WRITE_CODE // ###### Item ######

#define THEOLIZER_GENERATED_LAST_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kLastVersionNo,1)
#define THEOLIZER_GENERATED_FULL_AUTO Item

// —<<< Version.1 >>>—

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,1)
#define THEOLIZER_GENERATED_CLASS_NAME()\
THEOLIZER_INTERNAL_CLASS_NAME((u8"Item"))
#define THEOLIZER_GENERATED_ELEMENT_MAP emName
#define THEOLIZER_GENERATED_ELEMENT_LIST()\
THEOLIZER_INTERNAL_ELEMENT_N((mParent),mParent,etmDefault,\
(theolizerD::All),\
(Item *))\
THEOLIZER_INTERNAL_ELEMENT_N((mName),mName,etmDefault,\
(theolizerD::All),\
(std::string))\
THEOLIZER_INTERNAL_ELEMENT_N((mIsAssets),mIsAssets,etmDefault,\
(theolizerD::All),\
(bool))\
THEOLIZER_INTERNAL_ELEMENT_N((mAssetsIncrease),mAssetsIncrease,etmDefault,\
(theolizerD::All),\
(int))\
THEOLIZER_INTERNAL_ELEMENT_N((mDoManage),mDoManage,etmDefault,\
(theolizerD::All),\
(bool))\
THEOLIZER_INTERNAL_ELEMENT_N((mAmount),mAmount,etmDefault,\
(theolizerD::All),\
(int))\
THEOLIZER_INTERNAL_ELEMENT_N((mChildren),mChildren,etmDefault,\
(theolizerD::All),\
(theolizer::ListPointee<Item>))
#include <theolizer/internal/version_auto.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

#endif//THEOLIZER_WRITE_CODE // ###### Item ######

#ifdef THEOLIZER_WRITE_CODE // ###### Date ######

#define THEOLIZER_GENERATED_LAST_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kLastVersionNo,1)
#define THEOLIZER_GENERATED_FULL_AUTO Date

// —<<< Version.1 >>>—

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,1)
#define THEOLIZER_GENERATED_CLASS_NAME()\
THEOLIZER_INTERNAL_CLASS_NAME((u8"Date"))
#define THEOLIZER_GENERATED_ELEMENT_MAP emName
#define THEOLIZER_GENERATED_ELEMENT_LIST()\
THEOLIZER_INTERNAL_ELEMENT_N((mYear),mYear,etmDefault,\
(theolizerD::All),\
(short))\
THEOLIZER_INTERNAL_ELEMENT_N((mMonth),mMonth,etmDefault,\
(theolizerD::All),\
(char))\
THEOLIZER_INTERNAL_ELEMENT_N((mDay),mDay,etmDefault,\
(theolizerD::All),\
(char))
#include <theolizer/internal/version_auto.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

#endif//THEOLIZER_WRITE_CODE // ###### Date ######

#ifdef THEOLIZER_WRITE_CODE // ###### Trade ######

#define THEOLIZER_GENERATED_LAST_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kLastVersionNo,1)
#define THEOLIZER_GENERATED_FULL_AUTO Trade

// —<<< Version.1 >>>—

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,1)
#define THEOLIZER_GENERATED_CLASS_NAME()\
THEOLIZER_INTERNAL_CLASS_NAME((u8"Trade"))
#define THEOLIZER_GENERATED_ELEMENT_MAP emName
#define THEOLIZER_GENERATED_ELEMENT_LIST()\
THEOLIZER_INTERNAL_ELEMENT_KN((mDate),mDate,etmDefault,\
(theolizerD::All),\
(Date),1)\
THEOLIZER_INTERNAL_ELEMENT_N((mDebitItem),mDebitItem,etmDefault,\
(theolizerD::All),\
(Item *))\
THEOLIZER_INTERNAL_ELEMENT_N((mAmount),mAmount,etmDefault,\
(theolizerD::All),\
(int))\
THEOLIZER_INTERNAL_ELEMENT_N((mCreditItem),mCreditItem,etmDefault,\
(theolizerD::All),\
(Item *))\
THEOLIZER_INTERNAL_ELEMENT_N((mNote),mNote,etmDefault,\
(theolizerD::All),\
(std::string))
#include <theolizer/internal/version_auto.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

#endif//THEOLIZER_WRITE_CODE // ###### Trade ######

#ifdef THEOLIZER_WRITE_CODE // ###### HouseholdAccounts ######

#define THEOLIZER_GENERATED_LAST_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kLastVersionNo,1)
#define THEOLIZER_GENERATED_FULL_AUTO HouseholdAccounts

// —<<< Version.1 >>>—

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,1)
#define THEOLIZER_GENERATED_CLASS_NAME()\
THEOLIZER_INTERNAL_CLASS_NAME((u8"HouseholdAccounts"))
#define THEOLIZER_GENERATED_ELEMENT_MAP emName
#define THEOLIZER_GENERATED_ELEMENT_LIST()\
THEOLIZER_INTERNAL_ELEMENT_KN((mItemTree),mItemTree,etmPointee,\
(theolizerD::All),\
(Item),1)\
THEOLIZER_INTERNAL_ELEMENT_N((mTradeList),mTradeList,etmDefault,\
(theolizerD::All),\
(std::list<Trade>))
#include <theolizer/internal/version_auto.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

#endif//THEOLIZER_WRITE_CODE // ###### HouseholdAccounts ######

#ifdef THEOLIZER_WRITE_CODE // ###### Global VersionNo. Table ######
THEOLIZER_GENERATED_GLOBAL_TABLE();
#endif//THEOLIZER_WRITE_CODE // ###### Global VersionNo. Table ######
[/cpp]

6.まとめ

一般にポインタを保存/回復する場合、ポインタのリンクを維持するためのIDを何にするか決めて、それを適切に保存/回復する処理を書く必要があります。
今回のサンプル・データならば科目名が重複していないのでこれをIDとして使えますが、重複を許して科目ツリーのパスをIDとする場合、面倒な設計とコーディングか必要になります。
しかし、Theolizer®を使えば、ポイント先も同じファイルへ保存することに注意を払うだけでポインタも保存/回復できます。
その分あなたのアプリケーションの肝心な部分に注力できると思います。