スキップしてメイン コンテンツに移動

ComponentID

Win32のUUID(GUID)構造体のラッパークラス。VS.NET2003付属STLのhashコンテナ(hash_map/hash_set /hash_multimap/hash_multiset)用比較関数、SGI系STLのhashコンテナ用ハッシュ関数オブジェクト付き。システムの用意した構造体を継承してクラスを作るというのは割とよくやる方法で、継承した場合vtableが無ければメモリレイアウトの先頭に継承元が入るので、後ろに追加のデータをペイロードとして付けた上で、親の構造体を引数とするAPIの引数に使えるという利点がある。換言すれば、templateと、template policyを利用したmix-inによって、C++における継承は、Java等のクラスライブラリに顕著な、現実世界やデザインパターンとして観念されるモデルのマッピングに近いカテゴリ概念の素朴な具体化のための道具というよりは、本来の意味でのデータ抽象/アルゴリズム抽象に向けての手段として、あるいはCOMのバイナリ仕様の表現として活用(exploit)される箇所しか出る幕が無くなってしまった。




// ComponentID クラス (c) RyuK

#ifndef CALC_COMPONENTID_H
#define CALC_COMPONENTID_H

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0500 // Windows 2000 以降の機能を使用
#endif _WIN32_WINNT

#include <windows.h>

// あくまでMS Windows依存。UUID<=>文字列変換はわざわざプラットフォーム
// 依存のAPIに頼る必然性は無いものの、適当なGUIDの生成だけは依存せざる
// を得ない。
// Windowsの場合、最近になってMACアドレスが生成結果から判別できないように
// UUIDを生成できるようになった。Windowsは必要な物は大概
// Platform SDKの中に道具が用意されているので手間の節約のためにできる
// だけ依存することにする。

#include <rpc.h> // UUID

#include <string>
#include <functional>
#include <hash_map>
#include <hash_set>

#ifdef _STLPORT_VERSION // STLport使用時
using namespace std;
#else // VS.NET2003付属STL(Dinkumware)使用時
using namespace std;
using namespace stdext;
#endif

// CスタイルUUID文字列クラス(ASCII)(間のハイフン無し)
struct ComponentIDString
{
ComponentIDString()
{
m_szBuffer[32] = 0x00;
}

~ComponentIDString() {}

char m_szBuffer[36];
};

struct ComponentID : public UUID
{
typedef unsigned __int64 UInt64;

// デフォルトのコンストラクタ。UUID生成有り
ComponentID()
{
UuidCreate(this);
}

// UUID生成無し
ComponentID(bool init) {}

~ComponentID() {}

// (STLportなどのSGI系STL向けに)hash_mapのキーとするために必要な等
// 値演算子を定義する
// MicrosoftがData1等抽象的なメンバ名を付けた理由は不明である
__forceinline bool operator==(const ComponentID& right) const
{
return (
(*reinterpret_cast<const UInt64*>(&Data1)
== *reinterpret_cast<const UInt64*>(&(right.Data1)))
&& (*reinterpret_cast<const UInt64*>(&Data4)
== *reinterpret_cast<const UInt64*>(&(right.Data4)))
);
}

// (VS.NET2003付属STL - Dinkumware STL向けに)hash_mapのキーとする
// ために必要な比較演算子を定義する。
__forceinline bool operator<(const ComponentID& right) const
{
if (*reinterpret_cast<const UInt64*>(&Data4)
< *reinterpret_cast<const UInt64*>(&(right.Data4)))
return true;
else if (*reinterpret_cast<const UInt64*>(&Data4)
> *reinterpret_cast<const UInt64*>(&(right.Data4)))
return false;
else {
return (*reinterpret_cast<const UInt64*>(&Data1)
< *reinterpret_cast<const UInt64*>(&(right.Data1)));
}
}

// VS.NET2003付属STL - Dinkumware STL向けに必要なハッシュ関数をsize_t
// へのキャスト演算子として定義する
#ifndef _STLPORT_VERSION
__forceinline operator size_t() const
{ // Win32APIをUnix上で実装するWine ProjectがUuidHashで借用している
// FreeDCEのコードを又借り
const BYTE* data = reinterpret_cast<const BYTE*>(this);
short c0 = 0;
short c1 = 0;
for (DWORD i = 0; i < sizeof(UUID); ++i) {
c0 += data[i];
c1 += c0;
}

short x = -c1 % 255;
if (x < 0)
x += 255;

short y = (c1 - c0) % 255;
if (y < 0)
y += 255;

return y * 256 + x;
}
#endif

__forceinline ComponentID& operator=(const ComponentID& right)
{
if (&right != this) {
CopyMemory(this, &right, sizeof(ComponentID));
}

return *this;
}

__forceinline const ComponentID& initialize()
{
UuidCreate(this);
return *this;
}

// ハイフン入りASCII文字列として文字列化。sprintfなんかより実はこちら
// の方がかなり速い
string toString() const
{
unsigned char* pszUuid;
UuidToStringA(
const_cast<ComponentID*>(this),
&pszUuid
);
string result(reinterpret_cast<char*>(pszUuid));
RpcStringFreeA(&pszUuid);
return result;
}

// ハイフン無し文字列としてComponentIDStringへエクスポート
// (バイトオーダーを変換)
void toString(ComponentIDString& dst) const
{
const char szHexTable[] = "0123456789abcdef";
const BYTE* s
= reinterpret_cast<BYTE*>(const_cast<ComponentID*>(this));

dst.m_szBuffer[0] = szHexTable[s[3] >> 4];
dst.m_szBuffer[1] = szHexTable[s[3] & 15];
dst.m_szBuffer[2] = szHexTable[s[2] >> 4];
dst.m_szBuffer[3] = szHexTable[s[2] & 15];
dst.m_szBuffer[4] = szHexTable[s[1] >> 4];
dst.m_szBuffer[5] = szHexTable[s[1] & 15];
dst.m_szBuffer[6] = szHexTable[s[0] >> 4];
dst.m_szBuffer[7] = szHexTable[s[0] & 15];

dst.m_szBuffer[8] = szHexTable[s[5] >> 4];
dst.m_szBuffer[9] = szHexTable[s[5] & 15];
dst.m_szBuffer[10] = szHexTable[s[4] >> 4];
dst.m_szBuffer[11] = szHexTable[s[4] & 15];

dst.m_szBuffer[12] = szHexTable[s[7] >> 4];
dst.m_szBuffer[13] = szHexTable[s[7] & 15];
dst.m_szBuffer[14] = szHexTable[s[6] >> 4];
dst.m_szBuffer[15] = szHexTable[s[6] & 15];

dst.m_szBuffer[16] = szHexTable[Data4[0] >> 4];
dst.m_szBuffer[17] = szHexTable[Data4[0] & 15];
dst.m_szBuffer[18] = szHexTable[Data4[1] >> 4];
dst.m_szBuffer[19] = szHexTable[Data4[1] & 15];
dst.m_szBuffer[20] = szHexTable[Data4[2] >> 4];
dst.m_szBuffer[21] = szHexTable[Data4[2] & 15];
dst.m_szBuffer[22] = szHexTable[Data4[3] >> 4];
dst.m_szBuffer[23] = szHexTable[Data4[3] & 15];
dst.m_szBuffer[24] = szHexTable[Data4[4] >> 4];
dst.m_szBuffer[25] = szHexTable[Data4[4] & 15];
dst.m_szBuffer[26] = szHexTable[Data4[5] >> 4];
dst.m_szBuffer[27] = szHexTable[Data4[5] & 15];
dst.m_szBuffer[28] = szHexTable[Data4[6] >> 4];
dst.m_szBuffer[29] = szHexTable[Data4[6] & 15];
dst.m_szBuffer[30] = szHexTable[Data4[7] >> 4];
dst.m_szBuffer[31] = szHexTable[Data4[7] & 15];
}

// ハイフン入りASCII文字列からインポート
__forceinline bool importFromString(const string& s)
{
return (UuidFromStringA(
reinterpret_cast<unsigned char*>(const_cast<char*>(s.c_str())),
(UUID*)this
) == RPC_S_OK);
}

// ハイフン無しASCII文字列からインポート。s.size() >= 32に留意せよ
bool importFromStringWithoutHyphen(const string& s)
{
char szBuffer[] = {0, 0, 0, 0, 0, 0, 0, 0,
'-', 0, 0, 0, 0,
'-', 0, 0, 0, 0,
'-', 0, 0, 0, 0,
'-', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0
};

CopyMemory(&szBuffer[0], s.data(), 8);
CopyMemory(&szBuffer[9], s.data() + 8, 4);
CopyMemory(&szBuffer[14], s.data() + 12, 4);
CopyMemory(&szBuffer[19], s.data() + 16, 4);
CopyMemory(&szBuffer[24], s.data() + 20, 12);

return (UuidFromStringA(
reinterpret_cast<unsigned char*>(&szBuffer[0]),
(UUID*)this
) == RPC_S_OK);
}
};

// SGI系STL(STLport他)でhash_map等のキーとしてComponentIDを使用するためのhasher
// 関数オブジェクト

#include <functional>
#include <hash_map>
#include <hash_set>

struct ComponentIDHasher : public unary_function<ComponentID, size_t>
{
// Win32APIをUnix上で実装するWine ProjectがUuidHashで借用している
// FreeDCEのコードを又借り
__forceinline size_t operator()(const ComponentID& arg) const
{
const BYTE* data = reinterpret_cast<const BYTE*>(&arg);
short c0 = 0;
short c1 = 0;
for (DWORD i = 0; i < sizeof(UUID); ++i) {
c0 += data[i];
c1 += c0;
}

short x = -c1 % 255;
if (x < 0)
x += 255;

short y = (c1 - c0) % 255;
if (y < 0)
y += 255;

return y * 256 + x;
}
};

/*
// 別バージョン - UuidHash使用

class ComponentIDHasher : public unary_function<ComponentID, size_t>
{
private:
mutable RPC_STATUS m_rpc_status;
public:
ComponentIDHasher() {}
~ComponentIDHasher() {}

// 都合良くUuidHashなるAPI(128bit->16bit)が用意されているのでそれを使用。
// C++のconstは重要なDesign by Contractの手段である。
// ここではテンプレートの要求に応えるために引数のみならず関数もconst指定する
// 必要がある
size_t operator()(const ComponentID& arg) const
{
return static_cast<size_t>(
UuidHash(
reinterpret_cast<UUID*>(const_cast<ComponentID*>(&arg)),
&m_rpc_status
)
);
}
};
*/

#ifdef _STLPORT_VERSION
typedef hash_set<ComponentID, ComponentIDHasher> ComponentIDSet;
typedef hash_map<ComponentID, DWORD, ComponentIDHasher> ComponentID2DWORD;
typedef hash_map<ComponentID, ComponentID, ComponentIDHasher>
ComponentID2ComponentID;
typedef hash_map<string, ComponentID> String2ComponentID;
typedef hash_map<ComponentID, string, ComponentIDHasher> ComponentID2String;
#else
typedef hash_set<ComponentID> ComponentIDSet;
typedef hash_map<ComponentID, DWORD> ComponentID2DWORD;
typedef hash_map<ComponentID, ComponentID> ComponentID2ComponentID;
typedef hash_map<string, ComponentID> String2ComponentID;
typedef hash_map<ComponentID, string> ComponentID2String;
#endif

#endif CALC_COMPONENTID_H

コメント