C++ Builder CE fragment and/or tips

どちらかというとC++だけど、コンテナクラスのvectorの紹介

C++ Builder ないし Delphiには動的に割り付けられる配列がありますが、C++には標準的なコンテナクラスvectorがあり、便利なのでよく使います。実例を中心に紹介できればと思います。

コンテナクラスというのは文字通り何かを収容できるクラスなんですが、ここで重要というか便利なのは、プログラマが定義した型を持ったインスタンスを収容できるということです。C++でよく使うユーザー定義型といえば構造体ですが、具体例を挙げましょう。

まず鋭意制作中のBattlefield 2142 Gamer Server Browserですが、in-gameのブラウザは、

こんな感じです。一覧表示する項目は、サーバー名、現在のプレイヤー数/最大プレイヤー数、latency、現在のマップの名前、ゲームの種類ですかね。とりあえずは、これくらいで十分でしょうかね?よって構造体(struct)でserver_infoを定義すると、サーバのipアドレスとアクセスポート等を足して、

struct server_info {
		String ip;
		String port;
		String hostname;
		String mapname;
		String gametype;
		String numplayers;
		String maxplayers;
		int latency;
 };

こんな感じでしょうか?これがいくつか出てくるのですが、それを格納する入れ物は、vectorです。vectorを宣言して使う場合は、ヘッダ<vector>が必要ですので、頭で#includeします

#include <vector>
#include <algorithm>

std::vector<server_info> servers;

勘の良い方は察知されていると思いますが、後で各サーバーのlatencyでソート(並べ替える)ので、そのためのアルゴリズムを使うために<algorithm>もincludeしておきます。

上記のvector(ベクター)serversですけど、新要素はまずservers.push_back(entry)という形で追加します。もう少し具体的には、関数

void get_aserver_info(struct server_info& aentry)
 {

	String aping = ping(aentry.ip);

	if( aping == "NA" ){
		//aentry.latency= 9999; // no adding entry
		Form1->Memo1->Lines->Add("skipping " + aentry.ip);
		return;
	}

	aentry.latency = StrToInt(aping);

<途中大々的に省略>

	aentry.hostname = values[0];
	aentry.mapname = values[1];
	aentry.gametype = values[2];
	aentry.numplayers = values[3];
	aentry.maxplayers = values[4];

	Form1->Memo1->Lines->Add("adding entry " + aentry.ip);
	servers.push_back(aentry);

 }

aentryは参照呼び出しで引数で与えられた一個のエントリーです。値を入れつつついでにserversへ格納してます。要素を取り出す前に、sortの仕方を示しましょうかね?要素の順序を決める関数を用意します。今回は、latencyの大小で並び順を決めますので、bool(真偽値)を返す関数を定義します。この場合は、昇順ですね。

bool compare_latency(const struct server_info &a, const struct server_info &b)
{

	return a.latency < b.latency;

}

実際のソートは、

	sort(servers.begin(),servers.end(),compare_latency);

これだけです。servers.begin()はソートの先頭要素=serversの先頭要素、servers.end()はソートの末尾要素=serversの末尾要素です。要するにまるごと全体をソートするわけですね。その際にcompare_latencyの戻り値で順序を決めるというわけです。ソートのあと、要素群をTListViewに表示するルーチンは、

void __fastcall TForm1::Button1Click(TObject *Sender)
{
	sort(servers.begin(),servers.end(),compare_latency);

	for( auto itr = servers.begin(); itr != servers.end() ; ++itr ){

				TListItem* item = ListView1->Items->Add();
				item->Caption = itr->hostname;
				item->SubItems->Add(itr->mapname);
				item->SubItems->Add(itr->gametype);
				item->SubItems->Add(itr->numplayers);
				item->SubItems->Add(itr->maxplayers);
				item->SubItems->Add(IntToStr(itr->latency));

	}

}

になります。

auto itr = servers.begin()

のauto itrは型推測を使ったスマートポインタなんですが、vectorの全要素にアクセスする(よく舐めると表現する人もいます)場合の鉄板ループです。itrはイテレータ(反復子)と呼ばれるもので、実体はポインタです。iteratorなので略してitrですね。indexの場合だとよくiを使いますよね。上のitrはfor文のスコープ内だけで有効(通用可能)です。

便利なコンテナクラスは他にもあって、キュー(queue)を使うこともあります。具体例はまた別記事で。

コメント