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

こんにちは。田原です。

TCP/IP通信と言えばsocketですが、これは何にでも対応できる低レベルなインタフェースなので その分使うのがたいへんです。Theolizer®の通信サンプルを開発するにはちょっと大掛かりになりすぎるので躊躇していました。しかし、Boost.Asioにstd::iostream形式でTCP/IP通信を行える機能が含まれていることが判りました。
そこで、Boost.Asioを使って家計簿データの送受信をやってみました。予想通り実に簡単に通信できましたので、ご紹介します。

今回作ったサンプルは、下記処理を行います。

  1. 家計簿データをサーバへ送信
  2. サーバにて集計処理
  3. 集計結果だけをクライアントへ返却
  4. クライアントで表示

TCP/IP通信ですので、サーバとクライアントは別PC上にあっても動作します。
Windows PCとubuntu間での通信もあっさり成功しました。

1.Boost.Asioについて

Boost.Asioは非同期I/O処理ライブラリで、IP通信のサポートが充実しています。
std::iostreamを派生したTCP/IPストリーム機能を提供しています。今回はこの機能を使いました。

クライアント側は、fstreamとほぼ同じイメージで使えます。
fstreamではファイル名を指定してファイルをオープンしますが、Boost.Asioのクライアント側のストリームはサーバのドメイン名(IPアドレスも可)とポート番号を指定します。
例えば、下記にてhttpのGETリクエストを発行して、応答を受信しています。同期処理とは言え、たったこれだけでできます。

// TCP/IPコネクション接続
boost::asio::ip::tcp::iostream aTcpStream("theolizer.com", "80" );

// タイムアウト設定
aTcpStream.expires_from_now(boost::posix_time::seconds(3));

// 要求送信
aTcpStream << "GET / HTTP/1.0\nHost: theolizer.com\n" << std::endl;

// 応答受信
std::string line;
while(std::getline(aTcpStream, line))
    std::cout << line << "\n";

// エラー・チェック
if ((!aTcpStream) && (!aTcpStream.eof()))
    std::cout << "error: " << aTcpStream.error().message() << std::endl;

注)std::getline()はC言語標準ライブラリのgetline()とほぼ同じ機能を提供します。

サーバ側は接続待ちするのですが、何故かBoost.Asioのストリーム・サボートは接続待ちに対応していません。この部分はBoost.Asioの通常の同期処理か非同期処理を使って接続を待ちます。
基本はポート番号を指定して接続待ちするだけですので、本質的には1行に纏めることができますが、定形処理が若干あるので下記のように数行かかります。

//ポート番号12345で接続待ち
boost::asio::io_service         aIoService;
boost::asio::ip::tcp::endpoint  aEndPoint(boost::asio::ip::tcp::v4(), 12345);
boost::asio::ip::tcp::acceptor  aAcceptoracc(aIoService, aEndPoint);
boost::asio::ip::tcp::iostream  aTcpStream;
aAcceptoracc.accept(*aTcpStream.rdbuf());

今回のサンプル・ソースでは上記を少しまとめて使い易くしたTcpStreamクラスを用意しました。

boost1.59.0の問題について
Theolizer®開発中にも遭遇したのですが、boost 1.59.0にはいくつかのトラブルがあります。
1つはgcc(MinGW含む)5.4.0等の新しいバージョンでビルドする時、幾つか警告が生成されます。boostソース内で出ます。Theilizerのビルド時のみこれらの警告をコンパイラ・オプションでディセーブルして対処してます。
今回のサンプルもboostを使うためその警告が出てしまいます。(boostのソースで「std::auto_ptrは非推奨」警告が出ます。)asio_helper.hにてgccならば#pragma GCC diagnostic ignored “-Wdeprecated-declarations”を定義することでこの警告をディセーブルしています。

また、Visual Studio 2015でビルドする際にBoost.Asioに不具合が出ます。
これはboost 1.62.0にて対処されてます。
他のツールのバージョンも絡むので今はboostを1.62.0へ上げる時間が取れないため、暫定的にこのリンク先と同じ対処をboostboost1.59.0へ当てて使っています。

2.通信処理

今回は前々回の技術解説でご紹介した家計簿ソフトをファイル保存/回復ではなく通信するように変更しました。
クライアントは、家計簿データの初期設定と表示、サーバへの家計簿データ(集計要求)送信と集計結果(応答)受信、集計結果の表示を行います。
サーバは、家計簿データ(集計要求)受信、集計処理と表示、集計結果(応答)送信を行います。

2-1.クライアント側

今回はBinaryI/OSerializerを使ってみました。使い方はJsonI/OSerializerとほぼ同じです。PrettyPrint(人が見やすいように整形する)指定がないだけです。Json形式ではなく独自のバイナリー形式でシリアラズ/デシリアライズします。

エンディアン・フリーですので、データ・サイズに注意すれば異なる処理系間でのデータ交換にも使うことができます。
また、整数型変数は変数内の値を記録するのに必要十分なバイト数で保存します。例えばint型変数に100が入っていたらタグの1バイトとデータ1バイトの計2バイトで保存します。(タグ+int型データの5バイトではないです。)

2-1-1.クライアントの通信処理
client.cpp
[cpp] //############################################################################
// Theolizer解説用サンプル・プログラム5
//
// クライアント処理(家計簿データ送信→集計結果受信)
//############################################################################

#define THEOLIZER_GLOBAL_VERSION_TABLE

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

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

// boost::asio
#include "asio_helper.h"

// Theolizerライブラリ
#include <theolizer/serializer_binary.h>

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

// Theolizer自動生成先
#include "client.cpp.theolizer.hpp"

// ***************************************************************************
// メイン
// ***************************************************************************

int main(int argc, char* argv[])
{
//—————————————————————————-
// バラメータ解析
//—————————————————————————-

char const* aHostAddress="localhost";
if (1 < argc)
{
aHostAddress=argv[1];
}
std::cout << theolizer::print("HostAddress=%s\n", aHostAddress);

//—————————————————————————-
// 家計簿データ生成
//—————————————————————————-

// 家計簿
HouseholdAccounts aHouseholdAccounts;

// 初期設定(科目と取引データを設定している)
initialize(&aHouseholdAccounts);

//—————————————————————————-
// 家計簿計算処理(サーバへ計算処理を依頼する)
//—————————————————————————-

// サーバへ接続
TcpStream aTcpStream(aHostAddress, PORT_NO);

// 通信完了タイムアウト設定
aTcpStream.expires_from_now(3);

// 家計簿データをサーバへ送信
{
theolizer::BinaryOSerializer<theolizerD::Request>
js(aTcpStream, theolizer::CheckMode::NoTypeCheck, true);
THEOLIZER_PROCESS(js, aHouseholdAccounts);
js.clearTracking();

// シリアライザのエラー・チェック(例外を禁止している場合に必要)
theolizer::ErrorInfo aErrorInfo=js.getErrorInfo();
if (aErrorInfo)
{
std::cout << aErrorInfo.getMessage() << std::endl;
js.resetError(); // チェックしたことを通知する
}
}

// 集計結果をサーバから受信
{
theolizer::BinaryISerializer<theolizerD::Response> js(aTcpStream, true);
THEOLIZER_PROCESS(js, aHouseholdAccounts.mItemTree);
js.clearTracking();

// シリアライザのエラー・チェック(例外を禁止している場合に必要)
theolizer::ErrorInfo aErrorInfo=js.getErrorInfo();
if (aErrorInfo)
{
std::cout << aErrorInfo.getMessage() << std::endl;
js.resetError(); // チェックしたことを通知する
}
}

// TCP/IP回線のエラー・チェック
if ((!aTcpStream) && (!aTcpStream.eof()))
{
std::cout << "error: " << aTcpStream.error_message() << std::endl;
return 1;
}

// 集計結果表示
std::cout << u8"\n\n<<< 家計簿の集計結果(クライアント・サイド) >>>\n";
displayAmount(&aHouseholdAccounts.mItemTree);

return 0;
}
[/cpp]

2-2.サーバ側

サーパ側は上述の通りです。送信しているデータの内容をできるように、最後にJson書式で出力しています。

2-2-1.サーバの通信処理
server.cpp
[cpp] //############################################################################
// Theolizer解説用サンプル・プログラム5
//
// サーバ処理(家計簿データ受信→集計結果送信)
//############################################################################

#define THEOLIZER_GLOBAL_VERSION_TABLE

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

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

// boost::asio
#include "asio_helper.h"

// Theolizerライブラリ
#include <theolizer/serializer_binary.h>
#include <theolizer/serializer_json.h>

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

// Theolizer自動生成先
#include "server.cpp.theolizer.hpp"

// ***************************************************************************
// メイン
// ***************************************************************************

int main()
{
//—————————————————————————-
// 家計簿データ受信
//—————————————————————————-

// クライアントからの接続待ち
TcpStream aTcpStream(boostA::ip::tcp::v4(), PORT_NO, 3);

// 通信完了タイムアウト設定
aTcpStream.expires_from_now(3);

// 家計簿データをクライアントから受信
HouseholdAccounts aHouseholdAccounts; // 受信領域
{
theolizer::BinaryISerializer<theolizerD::Request> js(aTcpStream, true);
THEOLIZER_PROCESS(js, aHouseholdAccounts);
js.clearTracking();

// シリアライザのエラー・チェック(例外を禁止している場合に必要)
theolizer::ErrorInfo aErrorInfo=js.getErrorInfo();
if (aErrorInfo)
{
std::cout << aErrorInfo.getMessage() << std::endl;
js.resetError(); // チェックしたことを通知する
}
}

//—————————————————————————-
// 集計処理と表示
//—————————————————————————-

std::cout << u8"\n\n<<< 家計簿の集計結果(サーバ・サイド) >>>\n";
calculate(&aHouseholdAccounts);

//—————————————————————————-
// 集計結果をクライアントへ送信
//—————————————————————————-

{
theolizer::BinaryOSerializer<theolizerD::Response>
js(aTcpStream, theolizer::CheckMode::NoTypeCheck, true);
THEOLIZER_PROCESS(js, aHouseholdAccounts.mItemTree);
js.clearTracking();

// シリアライザのエラー・チェック(例外を禁止している場合に必要)
theolizer::ErrorInfo aErrorInfo=js.getErrorInfo();
if (aErrorInfo)
{
std::cout << aErrorInfo.getMessage() << std::endl;
js.resetError(); // チェックしたことを通知する
}
}

// TCP/IP回線のエラー・チェック
if ((!aTcpStream) && (!aTcpStream.eof()))
{
std::cout << "error: " << aTcpStream.error_message() << std::endl;
return 1;
}

//—————————————————————————-
// 解説用に送信データをファイルへ保存
//—————————————————————————-

// 集計結果を読みやすい形式のJsonファイルへ保存
{
std::ofstream aStream("response.json");
theolizer::JsonOSerializer<theolizerD::Response> js(aStream);
THEOLIZER_PROCESS(js, aHouseholdAccounts.mItemTree);
js.clearTracking();
}

return 0;
}
[/cpp]

2-3.データ構造定義

家計簿データ構造を定義しています。内容は前々回のcommon.hとほぼ同じですが、クライアント→サーバへの要求(Request)とサーバ→クライアントへの応答(Response)を保存先指定として追加し、必要なデータのみを通信でやり取りするようにしています。

2-3-1.サーバとクライアントの共通ヘッダ
common.h
[cpp] //############################################################################
// Theolizer解説用サンプル・プログラム5
//
// 簡単な家計簿用共通定義
//############################################################################

#if !defined(COMMON_H)
#define COMMON_H

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

// 標準ライブラリ
#include <string>

// Theolizerライブラリ
#include <theolizer/serializer.h>
#include <theolizer/list.h>

// ***************************************************************************
// 保存先定義
// ***************************************************************************

THEOLIZER_DESTINATIONS
(
All, // 引き継ぎ元保存先定義の最後のシンボル
Settings, // 設定情報
Data, // データ
Request, // 処理要求
Response // 処理応答
);

// dはDestinationのd
#define dS theolizerD::Settings
#define dD theolizerD::Data
#define dR theolizerD::Request
#define dP theolizerD::Response
#define SAVE(…) THEOLIZER_ANNOTATE(FS:<__VA_ARGS__>)

// ***************************************************************************
// グローバル・バージョン番号テーブル
// ***************************************************************************

THEOLIZER_DEFINE_GLOBAL_VERSION_TABLE(GlobalVersionTable, 2);

// ***************************************************************************
// 構造体定義
// ***************************************************************************

#define POINTEE THEOLIZER_ANNOTATE(FS:<>Pointee)

//—————————————————————————-
// 科目管理
//—————————————————————————-

// 管理種別
enum ManageType
{
emtNone, // 在庫管理無し
emtMoney, // 金額管理
emtCount // 数量管理
};

// 科目
struct Item
{
Item* mParent SAVE(dS, dR); // 親科目
std::string mName SAVE(dS, dR); // 科目名
bool mIsAssets SAVE(dS, dR); // 資産(財布や預金)
int mAssetsIncrease SAVE( dR, dP); // 増加した資産金額(保存無し)
ManageType mManageType SAVE(dS, dR); // 管理種別
int mAmount SAVE(dD, dR, dP); // 残高
std::string mUnit SAVE(dS, dR); // 単位
theolizer::ListPointee<Item> mChildren; // 子科目

Item()
{
mParent=NULL;
mManageType=emtNone;
}

THEOLIZER_INTRUSIVE(CS, (Item), 2);
};

typedef theolizer::ListPointee<Item>::iterator ChildIterator;

// バージョン・アップ/ダウン処理
template<class tTheolizerVersion, class tNextVersion>
struct Item::TheolizerUserDefine<tTheolizerVersion, tNextVersion, 1>
{
// Initialize members that is deleted in next version.
static void initialize(tTheolizerVersion& oNowVersion)
{
oNowVersion.mDoManage=false;
}

// Members version down.
static void downVersion(tNextVersion const& iNextVersion, tTheolizerVersion& oNowVersion)
{
if (iNextVersion.mManageType == emtMoney)
{
oNowVersion.mDoManage=true;
}
else
{
oNowVersion.mDoManage=false;
oNowVersion.mAmount=0;
}
}

// Members version up.
static void upVersion(tTheolizerVersion const& iNowVersion, tNextVersion& oNextVersion)
{
if (iNowVersion.mDoManage)
{
oNextVersion.mManageType.set(emtMoney, iNowVersion.mDoManage.getDoSucceed());
}
else
{
oNextVersion.mManageType.set(emtNone, iNowVersion.mDoManage.getDoSucceed());
}
}
};

//—————————————————————————-
// 取引記録
//—————————————————————————-

struct Date
{
short mYear; // 年
char mMonth; // 月(1-12)
char mDay; // 日(1-31)
};

struct Trade
{
Date mDate SAVE(dS, dD); // 取引日
Item* mDebitItem SAVE(dS, dD, dR); // 購入品(借方科目)
int mDebitAmount SAVE(dS, dD, dR); // 借方数量
int mCreditAmount SAVE(dS, dD, dR); // 貸方数量
Item* mCreditItem SAVE(dS, dD, dR); // 財布 (貸方科目)
std::string mNote SAVE(dS, dD); // 備考

THEOLIZER_INTRUSIVE(CS, (Trade), 2);
};

// バージョン・アップ/ダウン処理
template<class tTheolizerVersion, class tNextVersion>
struct Trade::TheolizerUserDefine<tTheolizerVersion, tNextVersion, 1>
{
// Initialize members that is deleted in next version.
static void initialize(tTheolizerVersion& oNowVersion)
{
oNowVersion.mAmount=0;
}

// Members version down.
static void downVersion(tNextVersion const& iNextVersion, tTheolizerVersion& oNowVersion)
{
// 新データへ読み込む時(デフォルト・コンストラクタで生成されている)に呼ばれるので対応
if ((!iNextVersion.mDebitItem) || (!iNextVersion.mCreditItem))
return;

int aMoney=0;
if (iNextVersion.mDebitItem->mManageType == emtMoney)
{
aMoney=iNextVersion.mDebitAmount;
}
else if (iNextVersion.mCreditItem->mManageType == emtMoney)
{
aMoney=iNextVersion.mCreditAmount;
}
else if ((iNextVersion.mDebitItem->mManageType != emtCount)
&& (iNextVersion.mCreditItem->mManageType != emtCount))
{
aMoney=iNextVersion.mDebitAmount;
}
oNowVersion.mAmount=aMoney;
}

// Members version up.
static void upVersion(tTheolizerVersion const& iNowVersion, tNextVersion& oNextVersion)
{
// 回復順序によっては未設定なので対応
if ((!oNextVersion.mDebitItem) || (!oNextVersion.mCreditItem))
return;

if (oNextVersion.mDebitItem->mManageType != emtCount)
{
oNextVersion.mDebitAmount.set(iNowVersion.mAmount,iNowVersion.mAmount.getDoSucceed());
}
if (oNextVersion.mCreditItem->mManageType != emtCount)
{
oNextVersion.mCreditAmount.set(iNowVersion.mAmount,iNowVersion.mAmount.getDoSucceed());
}
}
};

//—————————————————————————-
// 家計簿
//—————————————————————————-

struct HouseholdAccounts
{
Item mItemTree POINTEE; // 科目ツリー
std::list<Trade> mTradeList SAVE(dD, dR); // 取引のリスト

THEOLIZER_INTRUSIVE(CS, (HouseholdAccounts), 2);
};

typedef std::list<Trade>::iterator TradeIterator;

// ***************************************************************************
// 関数群
// ***************************************************************************

//—————————————————————————-
// 初期設定
//—————————————————————————-

void initialize(HouseholdAccounts* oHouseholdAccounts);
void display( HouseholdAccounts* iHouseholdAccounts);
void calculate( HouseholdAccounts* ioHouseholdAccounts);

std::string getFullName(Item* iItem);
void displayAmount(Item* iItem);

#endif
[/cpp]

2-4.エラー処理について

前々回はファイル保存/回復なのでエラーが発生しにくいため、エラー処理は手を抜いてました。今回は通信なのでクライアント→サーバの順で起動した場合など、簡単にエラーが発生するのでエラー処理を入れてます。

表示しているメッセージはboostから返却されたものですが、いまいち適切ではないメッセージになっています。
クライアントもサーバも一定時間接続できなければタイムアウトしているのですが、「対象のコンピューターによって拒否されたため、接続できませんでした。」と言うエラーメッセージが表示されます。

2-5.Boost.Asio用ヘルパー・クラスTcpStream

Boost.AsioのTCP/IPストリーム機能を使いやすくするため、TcpStreamクラスを用意しました。
これはBoost.AsioのTCP/IPストリーム(ip::tcp::iostream)クラスに下記機能を追加したものです。

  1. クライアント側が指定するポート番号を文字列ではなくunsigned shortで指定
  2. サーバ側はポート番号とタイムアウトを指定して接続待ち
  3. 通信タイムアウトをunsigned int型の秒で指定
  4. エラー・メッセージをUTF-8へ変換(boostからはMultiByte文字列で返ってきます)

C++の比較的高度な機能を多用しています。できるだけC++の深い機能を使わない当技術解説としてはよろしくないのですが、将来的にTcpStreamをTheolizer®内部に取り込む方向で考えていますのでご勘弁頂ければ幸いです。

2-5-1.Boost.Asioを使うためのヘルパー・クラス
asio_helper.h
[cpp] //############################################################################
// Theolizer解説用サンプル・プログラム5
//
// boost::asioのヘルパー
//############################################################################

#if !defined(ASIO_HELPER_H)
#define ASIO_HELPER_H

// ***************************************************************************
// boost::asio使用準備
// ***************************************************************************

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#define BOOST_DATE_TIME_NO_LIB
#define BOOST_REGEX_NO_LIB
#define BOOST_ASIO_ENABLE_CANCELIO
#include <boost/asio.hpp>
namespace boostA=boost::asio;

// Theolizerライブラリ
#include <theolizer/u8string.h>

// ***************************************************************************
// boost::asio::tcpストリーム改
// ***************************************************************************

class TcpStream : public boostA::ip::tcp::iostream
{
boostA::io_service& mIoService;
boostA::ip::tcp::endpoint mEndPoint;
boostA::ip::tcp::acceptor mAcceptoracc;
boostA::deadline_timer mTimer;
boost::system::error_code mErrorCode;
public:
// クライアント・ストリーム生成(ポート番号をunsignd short)
TcpStream
(
std::string const& iServerAddress,
unsigned short iPortNo
) : boostA::ip::tcp::iostream(iServerAddress, std::to_string(iPortNo).c_str()),
mIoService(rdbuf()->get_io_service()),
mEndPoint(),
mAcceptoracc(mIoService),
mTimer(mIoService),
mErrorCode()
{ }

// サーバ・ストリーム生成(接続タイムアウト追加)
TcpStream
(
boostA::ip::tcp const& iTcpProtocol,
unsigned short iPortNo,
unsigned int iSec=0
) : boostA::ip::tcp::iostream(),
mIoService(rdbuf()->get_io_service()),
mEndPoint(iTcpProtocol, iPortNo),
mAcceptoracc(mIoService, mEndPoint),
mTimer(mIoService),
mErrorCode()
{
if (iSec)
{
mTimer.expires_from_now(boost::posix_time::seconds(iSec));
mTimer.async_wait
(
[this](boost::system::error_code ec)
{
if (!ec)
{
mAcceptoracc.cancel();
setstate(std::ios::badbit);
}
}
);
}
mAcceptoracc.async_accept
(
*rdbuf(),
[this](boost::system::error_code ec)
{
mTimer.cancel();
mErrorCode=boost::asio::error::connection_refused;
}
);
mIoService.run();
}

void expires_from_now(unsigned int iSec)
{
boostA::ip::tcp::iostream::expires_from_now(boost::posix_time::seconds(iSec));
}

theolizer::u8string error_message() const
{
if (mErrorCode)
{
return theolizer::u8string(mErrorCode.message(), theolizer::MultiByte());
}
return theolizer::u8string(error().message(), theolizer::MultiByte());
}
};

#endif
[/cpp]

3.実行結果

3-1.クライアント(Windows 10)の実行画面

クライアントは家計簿データをサーバへ送り、サーバが送り返してきた集計結果を表示しています。
下記画面はサーバから送り返されてきた集計結果です。

 

3-2.サーバ(ubuntu 16.04 LTS)の実行画面

サーバはクライアントから受け取った家計簿データを集計して表示し、クライアントへ送り返しています。

3-3.サーバが送り返しているデータ

実際にはバイナリー・データですが、人が読み取ることはちょっと無理があるので、Json形式でダンプしてみました。(JsonOSerializerを使ってお手軽に)
ざっと見て頂ければ分かりますが、Itemクラス内のmParent, mName, mIsAssets, mManageType, mUnit変数は送信されていません。集計結果が保存されているmAssetsIncrease, mAmountとItemツリーとしてアクセスするためのmChildrenのみを含みます。

3-3-1.サーバがクライアントへ返却した集計結果のJson形式ダンプ
response.json
[text] {
"SerialzierName":"JsonTheolizer",
"GlobalVersionNo":2,
"TypeInfoList":[1] }
{
"mAssetsIncrease":0,
"mAmount":0,
"mChildren":[
4,
[1,{
"mAssetsIncrease":254536,
"mAmount":0,
"mChildren":[
2,
[2,{
"mAssetsIncrease":254536,
"mAmount":0,
"mChildren":[
0
] }],
[3,{
"mAssetsIncrease":0,
"mAmount":0,
"mChildren":[
0
] }] ] }],
[4,{
"mAssetsIncrease":-6347,
"mAmount":0,
"mChildren":[
2,
[5,{
"mAssetsIncrease":-4810,
"mAmount":0,
"mChildren":[
2,
[6,{
"mAssetsIncrease":-4652,
"mAmount":10700,
"mChildren":[
0
] }],
[7,{
"mAssetsIncrease":-158,
"mAmount":4,
"mChildren":[
0
] }] ] }],
[8,{
"mAssetsIncrease":-1537,
"mAmount":0,
"mChildren":[
3,
[9,{
"mAssetsIncrease":-954,
"mAmount":500,
"mChildren":[
0
] }],
[10,{
"mAssetsIncrease":-470,
"mAmount":1100,
"mChildren":[
0
] }],
[11,{
"mAssetsIncrease":-113,
"mAmount":0,
"mChildren":[
0
] }] ] }] ] }],
[12,{
"mAssetsIncrease":248189,
"mAmount":0,
"mChildren":[
2,
[13,{
"mAssetsIncrease":43653,
"mAmount":79245,
"mChildren":[
0
] }],
[14,{
"mAssetsIncrease":204536,
"mAmount":351671,
"mChildren":[
0
] }] ] }],
[15,{
"mAssetsIncrease":0,
"mAmount":0,
"mChildren":[
2,
[16,{
"mAssetsIncrease":0,
"mAmount":0,
"mChildren":[
0
] }],
[17,{
"mAssetsIncrease":0,
"mAmount":0,
"mChildren":[
0
] }] ] }] ] }
[/text]

 

4.従来のソース(分割)

クライアントとサーバに分けたので従来sub.cppに入れていたソースを3つに分割しました。

4-1.共通部

集計結果の表示とItemのフルネーム生成処理です。

4-1-1.サーバとクライアントの共通処理
common.cpp
[cpp] //############################################################################
// Theolizer解説用サンプル・プログラム5
//
// 補助関数群(サーバとクライアント共通部)
//############################################################################

#define THEOLIZER_NO_ANALYZE

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

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

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

// ***************************************************************************
// 結果表示
// ***************************************************************************

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

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

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

void displayAmount(Item* iItem)
{
if (iItem->mAssetsIncrease)
{
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);
}
}

switch(iItem->mManageType)
{
case emtMoney:
std::cout << theolizer::print(u8" 残高は%6d%2s\n", iItem->mAmount, iItem->mUnit);
break;

case emtCount:
std::cout << theolizer::print(u8" 在庫は%6d%2s\n", iItem->mAmount, iItem->mUnit);
break;

default:
std::cout << "\n";
break;
}
}

for (ChildIterator itr=iItem->mChildren.begin(); itr != iItem->mChildren.end(); ++itr)
{
displayAmount(&(*itr));
}
}
[/cpp]

4-2.クライアント側

家計簿データの初期化と表示処理です。

4-2-1.クライアントのサブ・ルーチン
client_sub.cpp
[cpp] //############################################################################
// Theolizer解説用サンプル・プログラム5
//
// 補助関数群(データ生成と結果表示)
//############################################################################

#define THEOLIZER_NO_ANALYZE

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

// 標準ライブラリ
#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;
}

//—————————————————————————-
// 科目ツリー設定
// 家計
// 収入
// 給料
// 雑収入
// 支出
// 食費
// 主食
// お米 在庫管理(g)
// パン 在庫管理(枚)
// 副食
// 肉魚 在庫管理(g)
// 野菜 在庫管理(g)
// 果物
// 現金
// 財布 残高管理
// 預金 残高管理
// 消費
// 利用
// 廃棄
//—————————————————————————-

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

Item* pushItem
(
Item* oItem,
char const* iItemName,
std::string iUnit="",
bool iIsAssets=false,
ManageType iManageType=emtNone,
int iAmount=0
)
{
Item aItem;
aItem.mParent =oItem;
aItem.mName =iItemName;
aItem.mUnit =iUnit;
aItem.mIsAssets =iIsAssets;
aItem.mAssetsIncrease =0;
aItem.mManageType =iManageType;
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->mManageType =emtNone;
oItemTree->mAmount =0;
oItemTree->mUnit ="";

Item* aLayer0=oItemTree;
{
Item* aLayer1;
aLayer1=pushItem(aLayer0, u8"収入", u8"円");
{
pushItem(aLayer1, u8"給料", u8"円");
pushItem(aLayer1, u8"雑収入", u8"円");
}
aLayer1=pushItem(aLayer0, u8"支出");
{
Item* aLayer2;
aLayer2=pushItem(aLayer1, u8"主食");
{
pushItem(aLayer2, u8"お米", u8"g", false, emtCount, 1000);
pushItem(aLayer2, u8"パン", u8"枚", false, emtCount, 1);
}
aLayer2=pushItem(aLayer1, u8"副食");
{
pushItem(aLayer2, u8"肉魚", u8"g", false, emtCount, 300);
pushItem(aLayer2, u8"野菜", u8"g", false, emtCount, 400);
pushItem(aLayer2, u8"果物");
}
}
aLayer1=pushItem(aLayer0, u8"現金", u8"円", true);
{
pushItem(aLayer1, u8"財布", u8"円", true, emtMoney, 35592);
pushItem(aLayer1, u8"預金", u8"円", true, emtMoney, 147135);
}
aLayer1=pushItem(aLayer0, u8"消費");
{
pushItem(aLayer1, u8"利用");
pushItem(aLayer1, u8"廃棄");
}
}
}

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

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

bool pushTrade
(
HouseholdAccounts* oHA,
short iYear,
char iMonth,
char iDay,
char const* iDebitName,
int iDebitAmount,
int iCreditAmount,
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.mDebitAmount = iDebitAmount;
aTrade.mCreditAmount= iCreditAmount;
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,20, u8"利用", 300, 300, u8"お米", u8"");
pushTrade(oHA, 2016, 8,20, u8"利用", 300, 300, u8"肉魚", u8"牛肉");
pushTrade(oHA, 2016, 8,20, u8"廃棄", 400, 400, u8"野菜", u8"セロリ");
pushTrade(oHA, 2016, 8,22, u8"パン", 6, 158, u8"財布", u8"六切り1袋");
pushTrade(oHA, 2016, 8,22, u8"肉魚", 200, 269, u8"財布", u8"鶏肉200g");
pushTrade(oHA, 2016, 8,22, u8"野菜", 1200, 182, u8"財布", u8"キャベツ1玉");
pushTrade(oHA, 2016, 8,22, u8"果物", 0, 113, u8"財布", u8"バナナ1房");
pushTrade(oHA, 2016, 8,22, u8"利用", 3, 3, u8"パン", u8"食事");
pushTrade(oHA, 2016, 8,20, u8"利用", 200, 200, u8"肉魚", u8"鶏肉");
pushTrade(oHA, 2016, 8,22, u8"利用", 600, 600, u8"野菜", u8"キャベツ");
pushTrade(oHA, 2016, 8,25, u8"預金", 254536, 254536, u8"給料", u8"セオライド テクノロジー");
pushTrade(oHA, 2016, 8,25, u8"財布", 50000, 50000, u8"預金", u8"生活費");
pushTrade(oHA, 2016, 8,25, u8"お米", 10000, 4652, u8"財布", u8"こしひかり10Kg");
pushTrade(oHA, 2016, 8,25, u8"肉魚", 500, 685, u8"財布", u8"豚肉500g");
pushTrade(oHA, 2016, 8,25, u8"野菜", 200, 152, u8"財布", u8"ほうれん草");
pushTrade(oHA, 2016, 8,25, u8"野菜", 300, 136, u8"財布", u8"人参");
}

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

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

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

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

void displayItems(int iIndent, Item* iItem)
{
std::cout << std::string(iIndent*2, ‘ ‘) << iItem->mName;
switch(iItem->mManageType)
{
case emtMoney:
std::cout << u8" 残高:" << iItem->mAmount;
break;

case emtCount:
std::cout << u8" 在庫:" << iItem->mAmount;
break;
}
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)
{
if (itr->mDebitAmount == itr->mCreditAmount)
{
std::cout << theolizer::print("%04d/%02d/%02d %s <- %s(%6d%2s) : %s\n",
itr->mDate.mYear,
itr->mDate.mMonth+0,
itr->mDate.mDay+0,
getFullName(itr->mDebitItem),
getFullName(itr->mCreditItem),
itr->mCreditAmount,
itr->mCreditItem->mUnit,
itr->mNote);
}
else
{
std::cout << theolizer::print("%04d/%02d/%02d %s(%6d%2s) <- %s(%6d%2s) : %s\n",
itr->mDate.mYear,
itr->mDate.mMonth+0,
itr->mDate.mDay+0,
getFullName(itr->mDebitItem),
itr->mDebitAmount,
itr->mDebitItem->mUnit,
getFullName(itr->mCreditItem),
itr->mCreditAmount,
itr->mCreditItem->mUnit,
itr->mNote);
}
}
}

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

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

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

[/cpp]

4-3.サーバ側

家計簿データの集計処理です。

4-3-1.サーバのサブ・ルーチン
server_sub.cpp
[cpp] //############################################################################
// Theolizer解説用サンプル・プログラム5
//
// 補助関数群(計算処理と結果表示)
//############################################################################

#define THEOLIZER_NO_ANALYZE

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

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

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

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

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

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

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

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

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

// 金額を決定(資産が増える方をplus)
int aMoney=0;
if (itr->mDebitItem->mManageType == emtMoney)
{
aMoney=itr->mDebitAmount;
}
else if (itr->mCreditItem->mManageType == emtMoney)
{
aMoney=itr->mCreditAmount;
}

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

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

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

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

// 貸方
if (itr->mCreditItem->mManageType == emtMoney)
{
itr->mCreditItem->mAmount -= itr->mCreditAmount;
}
}

displayAmount(&(ioHouseholdAccounts->mItemTree));
}
[/cpp]

5.自動生成ソース

大半の部分はサーバとクライアントで共通なcommon.hから生成されるため、client.cpp.theolizer.hppとほぼ同じです。しかし、それぞれclient.cppとserver.cppも用いて生成されるため、微妙に異なります。

5-1.クライアントの自動生成ソース

client.cpp.theolizer.hpp
[cpp] #ifdef THEOLIZER_WRITE_CODE // ###### ManageType ######

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

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

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,1)
#define THEOLIZER_GENERATED_ENUM_NAME u8"ManageType"
#define THEOLIZER_GENERATED_SAVE_TYPE estName
#define THEOLIZER_GENERATED_BASE_TYPE int
#define THEOLIZER_GENERATED_ENUM_LIST()\
THEOLIZER_GENERATED_ELEMENT((u8"emtNone"),(0),(0))\
THEOLIZER_GENERATED_ELEMENT((u8"emtMoney"),(1),(1))\
THEOLIZER_GENERATED_ELEMENT((u8"emtCount"),(2),(2))
#define THEOLIZER_GENERATED_DEFAULT_VALUE 0
#include <theolizer/internal/version_enum.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

#endif//THEOLIZER_WRITE_CODE // ###### ManageType ######

#ifdef THEOLIZER_WRITE_CODE // ###### Item ######

#define THEOLIZER_GENERATED_LAST_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kLastVersionNo,2)
#define THEOLIZER_GENERATED_CLASS_TYPE THEOLIZER_INTERNAL_UNPAREN(Item)

// —<<< Version.2 >>>—

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,2)
#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::Settings, theolizerD::Request),\
(Item *))\
THEOLIZER_INTERNAL_ELEMENT_N((mName),mName,etmDefault,\
(theolizerD::Settings, theolizerD::Request),\
(std::string))\
THEOLIZER_INTERNAL_ELEMENT_N((mIsAssets),mIsAssets,etmDefault,\
(theolizerD::Settings, theolizerD::Request),\
(bool))\
THEOLIZER_INTERNAL_ELEMENT_N((mAssetsIncrease),mAssetsIncrease,etmDefault,\
(theolizerD::Request, theolizerD::Response),\
(int))\
THEOLIZER_INTERNAL_ELEMENT_KN((mManageType),mManageType,etmDefault,\
(theolizerD::Settings, theolizerD::Request),\
(ManageType),1)\
THEOLIZER_INTERNAL_ELEMENT_N((mAmount),mAmount,etmDefault,\
(theolizerD::Data, theolizerD::Request, theolizerD::Response),\
(int))\
THEOLIZER_INTERNAL_ELEMENT_N((mUnit),mUnit,etmDefault,\
(theolizerD::Settings, theolizerD::Request),\
(std::string))\
THEOLIZER_INTERNAL_ELEMENT_N((mChildren),mChildren,etmDefault,\
(theolizerD::All),\
(theolizer::ListPointee<Item>))
#include <theolizer/internal/version_auto.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

// —<<< 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::Settings),\
(Item *))\
THEOLIZER_INTERNAL_ELEMENT_N((mName),mName,etmDefault,\
(theolizerD::Settings),\
(std::string))\
THEOLIZER_INTERNAL_ELEMENT_N((mIsAssets),mIsAssets,etmDefault,\
(theolizerD::Settings),\
(bool))\
THEOLIZER_INTERNAL_ELEMENT_N_DEL((mDoManage),,etmDefault,\
(theolizerD::Settings),\
(bool))\
THEOLIZER_INTERNAL_ELEMENT_N((mAmount),mAmount,etmDefault,\
(theolizerD::Data),\
(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,2)
#define THEOLIZER_GENERATED_CLASS_TYPE THEOLIZER_INTERNAL_UNPAREN(Trade)

// —<<< Version.2 >>>—

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,2)
#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::Settings, theolizerD::Data),\
(Date),1)\
THEOLIZER_INTERNAL_ELEMENT_N((mDebitItem),mDebitItem,etmDefault,\
(theolizerD::Settings, theolizerD::Data, theolizerD::Request),\
(Item *))\
THEOLIZER_INTERNAL_ELEMENT_N((mDebitAmount),mDebitAmount,etmDefault,\
(theolizerD::Settings, theolizerD::Data, theolizerD::Request),\
(int))\
THEOLIZER_INTERNAL_ELEMENT_N((mCreditAmount),mCreditAmount,etmDefault,\
(theolizerD::Settings, theolizerD::Data, theolizerD::Request),\
(int))\
THEOLIZER_INTERNAL_ELEMENT_N((mCreditItem),mCreditItem,etmDefault,\
(theolizerD::Settings, theolizerD::Data, theolizerD::Request),\
(Item *))\
THEOLIZER_INTERNAL_ELEMENT_N((mNote),mNote,etmDefault,\
(theolizerD::Settings, theolizerD::Data),\
(std::string))
#include <theolizer/internal/version_auto.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

// —<<< 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_DEL((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,2)
#define THEOLIZER_GENERATED_CLASS_TYPE THEOLIZER_INTERNAL_UNPAREN(HouseholdAccounts)

// —<<< Version.2 >>>—

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,2)
#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_KI((mItemTree),mItemTree,etmPointee,\
(theolizerD::All),\
(Item),2)\
THEOLIZER_INTERNAL_ELEMENT_N((mTradeList),mTradeList,etmDefault,\
(theolizerD::Data, theolizerD::Request),\
(std::list<Trade>))
#include <theolizer/internal/version_auto.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

// —<<< 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_KI((mItemTree),mItemTree,etmPointee,\
(theolizerD::All),\
(Item),1)\
THEOLIZER_INTERNAL_ELEMENT_N((mTradeList),mTradeList,etmDefault,\
(theolizerD::Data),\
(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 ######

namespace theolizer{namespace internal{
namespace global_table{
GlobalVersionTable::GlobalVersionTable()
{
THEOLIZER_INTERNAL_ADD(theolizer::internal::BinaryMidOSerializer,1u,1u);
THEOLIZER_INTERNAL_ADD(theolizer::internal::BinaryMidISerializer,1u,1u);
THEOLIZER_INTERNAL_ADD(pairTheolizer,1u,1u);
THEOLIZER_INTERNAL_ADD(listTheolizer,1u,1u);
THEOLIZER_INTERNAL_ADD(ListPointeeTheolizer,1u,1u);
THEOLIZER_INTERNAL_ADD(Item,1u,2u);
THEOLIZER_INTERNAL_ADD(Trade,1u,2u);
THEOLIZER_INTERNAL_ADD(HouseholdAccounts,1u,2u);
}
} // namespace global_table
}} // namespace theolizer

#endif//THEOLIZER_WRITE_CODE // ###### Global VersionNo. Table ######
[/cpp]

5-2.サーバの自動生成ソース

server.cpp.theolizer.hpp
[cpp] #ifdef THEOLIZER_WRITE_CODE // ###### ManageType ######

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

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

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,1)
#define THEOLIZER_GENERATED_ENUM_NAME u8"ManageType"
#define THEOLIZER_GENERATED_SAVE_TYPE estName
#define THEOLIZER_GENERATED_BASE_TYPE int
#define THEOLIZER_GENERATED_ENUM_LIST()\
THEOLIZER_GENERATED_ELEMENT((u8"emtNone"),(0),(0))\
THEOLIZER_GENERATED_ELEMENT((u8"emtMoney"),(1),(1))\
THEOLIZER_GENERATED_ELEMENT((u8"emtCount"),(2),(2))
#define THEOLIZER_GENERATED_DEFAULT_VALUE 0
#include <theolizer/internal/version_enum.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

#endif//THEOLIZER_WRITE_CODE // ###### ManageType ######

#ifdef THEOLIZER_WRITE_CODE // ###### Item ######

#define THEOLIZER_GENERATED_LAST_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kLastVersionNo,2)
#define THEOLIZER_GENERATED_CLASS_TYPE THEOLIZER_INTERNAL_UNPAREN(Item)

// —<<< Version.2 >>>—

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,2)
#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::Settings, theolizerD::Request),\
(Item *))\
THEOLIZER_INTERNAL_ELEMENT_N((mName),mName,etmDefault,\
(theolizerD::Settings, theolizerD::Request),\
(std::string))\
THEOLIZER_INTERNAL_ELEMENT_N((mIsAssets),mIsAssets,etmDefault,\
(theolizerD::Settings, theolizerD::Request),\
(bool))\
THEOLIZER_INTERNAL_ELEMENT_N((mAssetsIncrease),mAssetsIncrease,etmDefault,\
(theolizerD::Request, theolizerD::Response),\
(int))\
THEOLIZER_INTERNAL_ELEMENT_KN((mManageType),mManageType,etmDefault,\
(theolizerD::Settings, theolizerD::Request),\
(ManageType),1)\
THEOLIZER_INTERNAL_ELEMENT_N((mAmount),mAmount,etmDefault,\
(theolizerD::Data, theolizerD::Request, theolizerD::Response),\
(int))\
THEOLIZER_INTERNAL_ELEMENT_N((mUnit),mUnit,etmDefault,\
(theolizerD::Settings, theolizerD::Request),\
(std::string))\
THEOLIZER_INTERNAL_ELEMENT_N((mChildren),mChildren,etmDefault,\
(theolizerD::All),\
(theolizer::ListPointee<Item>))
#include <theolizer/internal/version_auto.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

// —<<< 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::Settings),\
(Item *))\
THEOLIZER_INTERNAL_ELEMENT_N((mName),mName,etmDefault,\
(theolizerD::Settings),\
(std::string))\
THEOLIZER_INTERNAL_ELEMENT_N((mIsAssets),mIsAssets,etmDefault,\
(theolizerD::Settings),\
(bool))\
THEOLIZER_INTERNAL_ELEMENT_N_DEL((mDoManage),,etmDefault,\
(theolizerD::Settings),\
(bool))\
THEOLIZER_INTERNAL_ELEMENT_N((mAmount),mAmount,etmDefault,\
(theolizerD::Data),\
(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,2)
#define THEOLIZER_GENERATED_CLASS_TYPE THEOLIZER_INTERNAL_UNPAREN(Trade)

// —<<< Version.2 >>>—

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,2)
#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::Settings, theolizerD::Data),\
(Date),1)\
THEOLIZER_INTERNAL_ELEMENT_N((mDebitItem),mDebitItem,etmDefault,\
(theolizerD::Settings, theolizerD::Data, theolizerD::Request),\
(Item *))\
THEOLIZER_INTERNAL_ELEMENT_N((mDebitAmount),mDebitAmount,etmDefault,\
(theolizerD::Settings, theolizerD::Data, theolizerD::Request),\
(int))\
THEOLIZER_INTERNAL_ELEMENT_N((mCreditAmount),mCreditAmount,etmDefault,\
(theolizerD::Settings, theolizerD::Data, theolizerD::Request),\
(int))\
THEOLIZER_INTERNAL_ELEMENT_N((mCreditItem),mCreditItem,etmDefault,\
(theolizerD::Settings, theolizerD::Data, theolizerD::Request),\
(Item *))\
THEOLIZER_INTERNAL_ELEMENT_N((mNote),mNote,etmDefault,\
(theolizerD::Settings, theolizerD::Data),\
(std::string))
#include <theolizer/internal/version_auto.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

// —<<< 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_DEL((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,2)
#define THEOLIZER_GENERATED_CLASS_TYPE THEOLIZER_INTERNAL_UNPAREN(HouseholdAccounts)

// —<<< Version.2 >>>—

#define THEOLIZER_GENERATED_VERSION_NO THEOLIZER_INTERNAL_DEFINE(kVersionNo,2)
#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_KI((mItemTree),mItemTree,etmPointee,\
(theolizerD::All),\
(Item),2)\
THEOLIZER_INTERNAL_ELEMENT_N((mTradeList),mTradeList,etmDefault,\
(theolizerD::Data, theolizerD::Request),\
(std::list<Trade>))
#include <theolizer/internal/version_auto.inc>
#undef THEOLIZER_GENERATED_VERSION_NO

// —<<< 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_KI((mItemTree),mItemTree,etmPointee,\
(theolizerD::All),\
(Item),1)\
THEOLIZER_INTERNAL_ELEMENT_N((mTradeList),mTradeList,etmDefault,\
(theolizerD::Data),\
(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 ######

namespace theolizer{namespace internal{
namespace global_table{
GlobalVersionTable::GlobalVersionTable()
{
THEOLIZER_INTERNAL_ADD(theolizer::internal::BinaryMidOSerializer,1u,1u);
THEOLIZER_INTERNAL_ADD(theolizer::internal::BinaryMidISerializer,1u,1u);
THEOLIZER_INTERNAL_ADD(theolizer::internal::JsonMidOSerializer,1u,1u);
THEOLIZER_INTERNAL_ADD(theolizer::internal::JsonMidISerializer,1u,1u);
THEOLIZER_INTERNAL_ADD(pairTheolizer,1u,1u);
THEOLIZER_INTERNAL_ADD(listTheolizer,1u,1u);
THEOLIZER_INTERNAL_ADD(ListPointeeTheolizer,1u,1u);
THEOLIZER_INTERNAL_ADD(Item,1u,2u);
THEOLIZER_INTERNAL_ADD(Trade,1u,2u);
THEOLIZER_INTERNAL_ADD(HouseholdAccounts,1u,2u);
}
} // namespace global_table
}} // namespace theolizer

#endif//THEOLIZER_WRITE_CODE // ###### Global VersionNo. Table ######
[/cpp]

5-3.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)

project(${TARGET_NAME} VERSION 1.0.0)

#boostを組み込む
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)

find_package(Boost REQUIRED COMPONENTS filesystem system)
message(STATUS " Boost_INCLUDE_DIR=${Boost_INCLUDE_DIR}")
message(STATUS " Boost_LIBRARY_DIR=${Boost_LIBRARY_DIR}")
message(STATUS " Boost_LIBRARIES =${Boost_LIBRARIES}")
include_directories("${Boost_INCLUDE_DIR}")
link_directories("${Boost_LIBRARY_DIR}")

if (NOT ${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
if(CMAKE_COMPILER_IS_MINGW)
set(LINK_LIB "ws2_32;wsock32")
else()
set(LINK_LIB "pthread")
endif()
endif()

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

# 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()

# —<<< 通信に用いるポート番号指定 >>>—

add_definitions(-DPORT_NO=12345)

# —<<< サーバ >>>—

set(SERVER_LIST server.cpp server_sub.cpp common.cpp common.h asio_helper.h)

add_executable(server ${SERVER_LIST})
setup_theolizer(server StaticWithBoost)
target_link_libraries(server ${Boost_LIBRARIES} ${LINK_LIB})

# —<<< クライアント >>>—

set(CLIENT_LIST client.cpp client_sub.cpp common.cpp common.h asio_helper.h)

add_executable(client ${CLIENT_LIST})
setup_theolizer(client StaticWithBoost)
target_link_libraries(client ${Boost_LIBRARIES} ${LINK_LIB})

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

enable_testing()
add_test(NAME ServerTest COMMAND $<TARGET_FILE:server>)
add_test(NAME ClientTest COMMAND $<TARGET_FILE:client>)
add_custom_target(BuildTest COMMAND "ctest" "-V" "-C" $<CONFIG> -j2)
add_dependencies(BuildTest server)
add_dependencies(BuildTest client)

# TheolzierライブラリのPATH設定
set(PATH "${THEOLIZER_LIB};$ENV{PATH}")
STRING(REPLACE ";" "\\;" PATH "${PATH}")

set_tests_properties(ServerTest PROPERTIES ENVIRONMENT "PATH=${PATH}")
set_tests_properties(ServerTest PROPERTIES TIMEOUT 5)

set_tests_properties(ClientTest PROPERTIES ENVIRONMENT "PATH=${PATH}")
set_tests_properties(ClientTest PROPERTIES TIMEOUT 5)
[/text]

 

6.最後に

TCP/IPで通信して異なる処理系間でもデータ交換できることを確認しました。
意外に簡単なことに驚かれたのではないでしょうか。
私はもう少し苦労すると思っていたのですが、Boost.Asioのお陰で1日も掛かりませんでした。
Boost、素晴らしいです。