Search code examples
c++hashhashmaphash-of-hashes

How to create a hash of hashes in C++?


Is there a way to create a hash of hashes in C++?

Effectively I am trying to do what you can do in Perl but only in C++. Here is an example of Perl code I would like to have happen in C++

%hash = (
gameobject1 => {
    position => {
        x_loc => 43,
        y_loc => 59,
    }
    rect_size => {
        width => 5,
        height => 3,
    }
    collidable => 1,
    sounds => {
        attack => "player_attack.ogg",
        jump => "player_jump1.ogg",
        jump_random => [qw/player_jump1.ogg player_jump2.ogg player_jump3.ogg/]
    }

},
gameobject2 => {
    position => {
        x_loc => 24,
        y_loc => 72,
    }
    rect_size => {
        width => 2,
        height => 4,
    }
    sounds => {
        attack => "goblin_attack.ogg",
    }
        items => [qw/sword helmet boots/]
},
);

The thing to note is the hashes with in gameobjects can exist or not... i.e. position might exist in gameobject1 but may not exist for gameobject35.

Any ideas?


Solution

  • Perl hashes let you use anything for values. C++ being a statically typed language, it won't let you do that: you have to specify exactly what type you want the values in the hash (in C++ lingo, the map) to have.

    Here's possible solution with C++11 and boost, with some strong typing thrown in :)

    #include <map>
    #include <vector>
    #include <string>
    #include <boost/optional.hpp>
    
    // Coordinates are always like this, aren't they?
    struct coords {
        int x_loc;
        int y_loc;
    };
    
    // Dimensions are always like this, aren't they?
    struct dims {
        int width;
        int height;
    };
    
    // Sound maps: each string key maps to a vector of filenames
    typedef std::map<std::string, std::vector<std::string>> sound_map;
    // Item lists: looks like it's just a collection of strings
    typedef std::vector<std::string> item_list;
    
    // Fancy names to improve readability
    enum collidability : bool {
        collidable = true,
        not_collidable = false
    };
    
    // A structure to describe a game object
    struct game_object {
        // An optional position
        boost::optional<coords> position;
        // An optional rectangle size
        boost::optional<dims> rect_size;
        // Assuming "false" can mean the same as "no collidable key"
        bool collidable;
        // Assuming an "empty map" can mean the same as "no map"
        sound_map sounds;
        // Assuming an "empty vector" can mean the same as "no vector"
        item_list items;
        // If any of the above assumptions is wrong,
        // sprinkle boost::optional liberally :)
    };
    
    // Finally, values for our "hash"
    std::map<std::string, game_object> hash {
        { "game_object1",
          {
            coords { 43, 59 },
            dims { 5, 3 },
            collidable, // remember those fancy names?
            sound_map {
                 { "attack", { "player_attack.ogg" } },
                 { "jump", { "player_attack.ogg" } },
                 { "jump_random", { "player_jump1.ogg", "player_jump2.ogg", "player_jump3.ogg" } }
            },
            item_list {}
        } },
        { "game_object2",
          {
            coords { 24, 72 },
            dims { 2, 4 },
            not_collidable,
            sound_map {
                 { "attack", { "goblin_attack.ogg" } }
            },
            item_list { "sword", "helmet", "boots" }
        } },
        { "game_object25",
          {
            boost::none, // no position
            dims { 2, 4 },
            not_collidable,
            sound_map {
                 { "attack", { "goblin_attack.ogg" } }
            },
            item_list { "sword", "helmet", "boots" }
        } }
    };
    

    If you really want something like a Perl hash of Perl hashes, you can use std::map<std::string, boost::any> to get the ability to store anything in the map. However, this requires you to test for the types of every value before obtaining it from the map. If only a certain set of types is possible, you can use something more strongly-typed than boost::any, like boost::variant.