LoginSignup
8
8

More than 5 years have passed since last update.

【cocos2d-x 3.x】cocos2d::Mapのキーを独自の型にする

Last updated at Posted at 2014-06-19

概要

cocos2d::Mapのキーの値は、主にstd::stringなどを使うことが想定されているようですが、独自に定義したり、既存の構造体を使用したい場合があったりします。

例えば、cocos2d::Vec2をキーとして、指定した場所にあるcocos2d::Spriteを取り出したい。などというMapを作りたいという場合。

error.cpp
// 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の場合

前述のHashPredは、何も渡さないデフォルトの挙動の場合、それぞれ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名前空間を変更するのは汚い感じがするので、もっと良い方法があれば是非とも教えてください。

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8