概要
cocos2d::Mapのキーの値は、主にstd::string
などを使うことが想定されているようですが、独自に定義したり、既存の構造体を使用したい場合があったりします。
例えば、cocos2d::Vec2
をキーとして、指定した場所にあるcocos2d::Sprite
を取り出したい。などというMapを作りたいという場合。
// Vec2をキー、Spriteを値とするMapを定義する
typedef cocos2d::Map<cocos2d::Vec2, cocos2d::Sprite *> DefinedMap;
// メンバ変数にする
CC_SYNTHESIZE_READONLY(DefinedMap, _map, Map);
このように書いても上手くいきません。これは、cocos2d::Vec2
を同一の物と比較する関数が定義されていないため。
ある型をキーとして使うためには、値から固有のハッシュを生成するハッシュ関数と、2つの値が同一かどうかを判定する比較関数を定義してあげる必要があります。
std::unordered_mapの場合
前提としてcocos2d::Map
はC++11から追加されたstd::unordered_map
のラッパーになっています。
std::unordered_map
では、コンストラクタでHash
, Pred
という値が取れ、これらを渡すことでハッシュ関数と比較関数の挙動をカスタマイズすることができます。
unordered_map (C++11) - cpprefjp - C++ Library Reference
そのため、以下のようにVec2Hash
, Vec2Pred
というクラスを定義して、unordered_map
のコンストラクタとして渡せばOK
class Vec2Hash
{
public:
size_t operator () (const cocos2d::Vec2& key) const
{
// Vec2から固有の文字列を作成する
auto str = cocos2d::StringUtils::format("%f,%f", key.x, key.y);
// この文字列をハッシュ化する
return std::hash<std::string>()(str);
}
};
class Vec2Pred
{
public:
bool operator () (const cocos2d::Vec2& lh, const cocos2d::Vec2& rh) const
{
// 2つのVec2が等しいかどうかを返す
return lh == rh;
}
};
typedef std::unordered_map<cocos2d::Vec2, cocos2d::Sprite *, Vec2Hash, Vec2Pred> DefinedMap;
しかし、cocos2d::Map
では、これらの値をコンストラクタに渡すことができず、この方法は利用できません。
cocos2d::Mapの場合
前述のHash
とPred
は、何も渡さないデフォルトの挙動の場合、それぞれstd::hash<K>(K key)
, std::to_equal<K>(K lh, K rh)
の実行結果が利用されるため、これらを定義してあげればcocos2d::Map
でも独自の型をキーに使うことができます。
hash (C++11) - cpprefjp - C++ Library Reference
比較演算関数オブジェクト - cpprefjp - C++ Library Reference
namespace std
{
template <>
struct hash<cocos2d::Vec2>
{
std::size_t operator () (const cocos2d::Vec2& key) const
{
// Vec2から固有の文字列を作成する
auto str = cocos2d::StringUtils::format("%f,%f", key.x, key.y);
// この文字列をハッシュ化する
return std::hash<std::string>()(str);
}
};
template<>
struct equal_to<cocos2d::Vec2>
{
bool operator () (const cocos2d::Vec2& lh, const cocos2d::Vec2& rh) const
{
// 2つのVec2が等しいかどうかを返す
return lh == rh;
}
};
}
typedef cocos2d::Map<cocos2d::Vec2, cocos2d::Sprite *> DefinedMap;
このように、std::hash<cocos2d::Vec2>
とstd::equal_to<cocos2d::Vec2>
を定義してあげる。
std
名前空間を変更するのは汚い感じがするので、もっと良い方法があれば是非とも教えてください。