Question

I want to try and phrase this question as generally as I can, but it is really related to the game that I an programming.

I have a class that has a std::map member:

class Player {
    ...
    private:
        std::map<Action, Command>  mActionBinding;
    ...
}

Action is an enum within the Player class. It contains Action titles such as MoveLeft or MoveRight. Command is a struct that contains a function and a category.

struct Command
{
        Command();

        std::function<void(SceneNode&, sf::Time)>        action;
        unsigned int                                     category;
        // SceneNode&                                    node
};

The idea behind this system is that I can bind a function that will manipulate a SceneNode (a drawable object in my game) to a category of SceneNode. The game's update() will iterate through all the SceneNodes in my game and execute the command on a SceneNode if that SceneNode's category matches the one in the command. (In case this all sounds familiar, this is the GQE engine for SFML, and the source code can be found here: https://github.com/LaurentGomila/SFML-Game-Development-Book)

I want commands to be more specific than this implementation. I want a command to be executed on a SceneNode if that SceneNode matches the one specified by the command, not just a category comparison.

However, the commands are created in this Player class, and they are created in that std::map. Player's constructor calls a initializeAction() function which calls something like:

mActionBinding[MoveLeft].action = 
      derivedAction<SpaceCraft>(SpaceCraftMover(-playerSpeed, 0.f));

Basically, it sets action to some function that manipulates the space craft node. Later in the Player constructor, the mActionBinding's command's category is set like so:

for(auto& pair : mActionBinding)
            pair.second.category = Category::PlayerSpaceCraft;

My problem: Command's constructor got called no where that I can see. I want to change Command's constructor to accept a SceneNode& so I can set it to the SpaceCraft node (replacing the unsigned int with the commented SceneNode&). How can I create these commands in such a way that I can see the Command(SceneNode&), and therefore be able to set it properly? In other words, how can I get this:

Command::Command(SceneNode& n)
: action()
// , category(Category::None)
, node(n)
{}

Which currently gives me an error within stl_map.h that I do not understand, presumably because the Command constructor is getting called incorrectly in Player's functions

Thanks in advance, sorry for the long-winded question, but I think the context is necessary.

Was it helpful?

Solution

If I'm understanding the question correctly, I think you're observing the create-if-does-not-exist-and-return-a-reference behavior of std::map::operator[]:

If [the key] does not match the key of any element in the container, the function inserts a new element with that key and returns a reference to its mapped value. Notice that this always increases the container size by one, even if no mapped value is assigned to the element (the element is constructed using its default constructor).

If you want to construct the Command object yourself (possibly via a non-default constructor), you probably want to call std::map::insert or std::map::emplace, depending on the semantics you want (and your compiler's support for newer standards), instead of using operator[].

Pulling this all together, try something like this:

Command cmd(theSceneNode);
cmd.action = derivedAction<SpaceCraft>(SpaceCraftMover(-playerSpeed, 0.f));
// more command setup, possibly
mActionBinding.insert(std::make_pair(MoveLeft, cmd));
// or possibly, mActionBinding.insert(std::make_pair(MoveLeft, std::move(cmd)));
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top