MASATOの開発日記


前の開発日記 次の開発日記 一覧

2004/02/22

やる気が出ないとコードが書けないなぁと最近感じます。 やる気が出たときと出ないときではコードを書く早さが10倍位違います。 やる気が出ないときはコードなんて書かずにやる気を出すことに専念するべきなんですが、 一日あれこれやっても結局やる気が出ないことも多いです。やる気を出すって難しい・・・。

名前空間stdext

Visual C++.NET 2003では、std::hash_mapやstd::hash_setを使おうとするとWarning C4996が発生します。 これを避けるためには、stdではなくstdext名前空間を使い、stdext::hash_mapやstdext::hash_setとする必要があります。 これについては、MSDNに全て書いてあります。 <hash_map>と<hash_set>ヘッダファイルのメンバは、現時点でのISO C++標準ではないので、 std名前空間からstdext名前空間へ移動したそうです。std::hash_mapとstd::hash_setは互換性のために残されていますが、できる限り使わないようにすること、というようです。

stdext名前空間の話は以上ですが、「互換性のために残されていますが、できる限り使わないようにすること」という宣言は、 __declspec(deprecated)を使うことにより(Microsoftに限らず)誰でも使うことができます。 互換性のために残されていますが、できる限り使って欲しくない関数Testは、次のように宣言しても良いですね。

__declspec(deprecated) void Test(void)
{
  // 色々
}

但しこの構文はVisual C++専用であると思われます。注意しましょう。

hash_mapのキーにCStringを使えるようにする方法

環境Visual C++.NET 2003
言語C++
使用ライブラリMFC
使用ライブラリSTL(Dinkumware)

C++標準ライブラリには、ハッシュマップを扱うためのhash_mapというクラスがあります (正確にはhash_mapはまだC++標準ライブラリには含まれて居ません。現在のところMicrosoftの独自拡張という扱いになるはずです)。 ハッシュマップのキーとして、文字列を使うケースは良くあると思います。 しかし、hash_mapのキーとして、std::stringを使うことはできますが、CStringを使うことはできません。 そこで、hash_mapのキーとしてCStringも使えるようにしよう、というお話です。

CStringがhash_mapのキーとして使えない理由は、CStringがhash_map用のハッシュ値取得インターフェースを持っていないからです。 そこで、CStringからハッシュ値を取得できるクラスを外部から与えることにします。
肝心のハッシュ値の計算方法ですが、MFCコレクションクラスのヘルパとしてHashMapという関数があり、 これがCString(正確にはLPCTSTR)のハッシュ値を計算することができます。 これが的確なハッシュ値計算方法かどうか私は知りませんが、自分で作るよりはマシでしょうから、このHashMap関数を使うことにします。

HashMap関数を使って求めたハッシュ値を、hash_map用インターフェースで提供するのが次のクラスです。

#include <functional>
#include <hash_map>
#include <afxtempl.h>

template <class KEY, class ARG_KEY = KEY, class TRAITS = std::less<KEY> >
class CMfcHash : public stdext::hash_compare<KEY, TRAITS>
{
public:
  using stdext::hash_compare<KEY, TRAITS>::operator();
  size_t operator()(const KEY& Key) const
  {
    return static_cast<size_t>(HashKey<ARG_KEY>(Key));
  }
};

このクラスは、CppUnitを用いた次のテストに通りました。

stdext::hash_map<CString, int, CMfcHash<CString, LPCTSTR> > mapTest;
mapTest["Test1"] = 10;
mapTest["Test2"] = 20;
mapTest["Test3"] = 30;

CPPUNIT_ASSERT_EQUAL(0, mapTest["Test0"]);
CPPUNIT_ASSERT_EQUAL(10, mapTest["Test1"]);
CPPUNIT_ASSERT_EQUAL(20, mapTest["Test2"]);
CPPUNIT_ASSERT_EQUAL(30, mapTest["Test3"]);

ついでに、次のテストにも通りました。Unicode文字列を扱うCComBSTRを使う場合です。

stdext::hash_map<CComBSTR, int, CMfcHash<CComBSTR> > mapTest;
mapTest[L"Test1"] = 10;
mapTest[L"Test2"] = 20;
mapTest[L"Test3"] = 30;

CPPUNIT_ASSERT_EQUAL(0, mapTest[L"Test0"]);
CPPUNIT_ASSERT_EQUAL(10, mapTest[L"Test1"]);
CPPUNIT_ASSERT_EQUAL(20, mapTest[L"Test2"]);
CPPUNIT_ASSERT_EQUAL(30, mapTest[L"Test3"]);

HashMap関数は、COleVariantのハッシュ値を求めることもできそうなのでちょっと試してみましたが、 COleVariantは、operator<が存在しないので、コンパイルに通りませんでした。残念。

前の開発日記 次の開発日記 一覧