std::vector issues

Started by
5 comments, last by Rasmadrak 15 years, 8 months ago
Hey all, I'm in the process of making a Game Engine, and am currently doing the Console. I have a vector for the current commands in the queue, and a map of commands (name -> function). The problem is, the commands map says that it is empty, but the debugger says that it contains items. Console.hpp:
...
typedef void (*ConCommandFn)(vector<string>);

class Console
{
public:
	Console();
	~Console() {};

	void msg2(string sMsg);
	void msg(string sMsg);
	void debug(int nLevel, string sMsg);
	void warning(string sMsg);

	ConVar *getVariable(string sName);
	bool variableExists(string sName);
	ConVar *createVariable(string sName, string sDefaultValue, bool bMin=false, float flMin=0.0, bool bMax=false, float flMax=0.0);

    void createCommand(string sCommand, ConCommandFn fnCallback);
    bool commandExists(string sName);
    void addCommand(string sCommand);
    void insertCommand(string sCommand);
    void forceCommand(string sCommand);
	void processQueue();

private:
	map<string,ConVar> m_variables;
    map<string,ConCommandFn> m_commands;
    vector<string> m_cmdQueue;
};
...
Console.cpp
...
//-----------------------------------------------------------------------------
// Name: createCommand()
// Desc: Adds a command to the command map.
//-----------------------------------------------------------------------------
void Console::createCommand(string sCommand, ConCommandFn fnCallback)
{
    if(commandExists(sCommand))
    {
        debug(1, "Cannot createCommand(): command already exists.\n");
        return;
    }

    m_commands[sCommand] = fnCallback;
}


//-----------------------------------------------------------------------------
// Name: commandExists()
// Desc: Returns true or false depending on whether the command is in the
//       command map.
//-----------------------------------------------------------------------------
bool Console::commandExists(string sName)
{
	// Is the variable map empty?
	if(m_commands.empty())
		return false;

	return m_commands.find(sName) != m_commands.end();
}

//-----------------------------------------------------------------------------
// Name: processQueue()
// Desc: Called every frame. Process all commands in the command buffer.
//-----------------------------------------------------------------------------
void Console::processQueue()
{
    // Run forceCommand() on all the items in the queue
	for (vector<string>::iterator i = m_cmdQueue.begin(); i != m_cmdQueue.end(); ++i)
        forceCommand(*i);

    // Clear the command queue
    m_cmdQueue.clear();
}


//-----------------------------------------------------------------------------
// Name: addCommand()
// Desc: Adds a command to the command queue. Processed the next frame.
//-----------------------------------------------------------------------------
void Console::addCommand(string sCommand)
{
    // Ensure that the item won't get discarded by the vector (overflow
    // protection)
    if (m_cmdQueue.size()+1 >= m_cmdQueue.max_size())
    {
        debug(1, "Command queue overflow imminent. Processing queue now...\n");
        processQueue();
    }

    // Put to the end of the queue
    m_cmdQueue.push_back(sCommand);
}


//-----------------------------------------------------------------------------
// Name: insertCommand()
// Desc: Adds a command to the start of the command queue. Processes first the
//       next frame.
//-----------------------------------------------------------------------------
void Console::insertCommand(string sCommand)
{
    // Ensure that the item won't get discarded by the vector (overflow
    // protection)
    if (m_cmdQueue.size()+1 >= m_cmdQueue.max_size())
    {
        debug(1, "Command queue overflow imminent. Processing queue now...\n");
        processQueue();
    }

    // Insert at the start of the queue
    m_cmdQueue.insert(m_cmdQueue.begin(), sCommand);
}


//-----------------------------------------------------------------------------
// Name: forceCommand()
// Desc: Doesn't add the command to the command queue, but process the command
//       immediately.
//-----------------------------------------------------------------------------
void Console::forceCommand(string sCommand)
{
    vector<string> vTokens;

    // Ensure the string is not empty
    if (sCommand == "\n")
        return;

    if (sCommand == "")
        return;

    // Initialize the seperator and tokenizer
    escaped_list_separator<char> sep('\\', ' ', '\"');
    tokenizer<escaped_list_separator<char>> tok(sCommand, sep);

    // Put all the tokens in a vector
    for(tokenizer<escaped_list_separator<char>>::iterator i = tok.begin(); i != tok.end(); ++i)
        vTokens.push_back(*i);

    // No tokens?
    if(vTokens.size() == 0)
        return;

    // Get the command name
    string sName = vTokens[0];

    // Is a variable
    if (variableExists(sName))
    {
        if(vTokens.size() != 2)
        {
            // Print out the value of the variable
            msg("Value of ");
            msg2(sName);
            msg(" is ");
            msg2(getVariable(sName)->GetString());
            msg("\n");

            return;
        }

        // Set the value of the variable
        getVariable(sName)->SetValue(vTokens[1]);

        // TODO: Add event code
        return;
    }

    // Is a command
    if (commandExists(sCommand))
    {
        // Call command
        m_commands[sCommand](vTokens);
        return;
    }

    // Show "Unknown command".
    msg("Unknown command: ");
    msg2(sName);
    msg("\n");
}
...
And the code which I use to create the command:
Console g_console;
...
void Con_echo_f(vector<string> arg)
{
    if (arg.size() <= 1)
    {
        g_console.msg("Syntax: echo <message>");
        return;
    }

    for (vector<string>::iterator i = arg.begin()+1; i != arg.end(); ++i)
        g_console.msg(i->append(" "));
    
    g_console.msg("\n");
}
...
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR lpCmdLine, INT )
{
    ...
    g_console.createCommand("echo", Con_echo_f);
    g_console.addCommand("echo hey there!");
    ...
}
Please could someone help me? I know that it is definately not the command queue processing / tokenizing as variables work fine. It seems m_commands.empty() is true, although the debugger shows that createCommand is working correctly. I'm sure that I just have a major typo somewhere, but I have been looking all night and can't find what is up... Thanks in advance, - Saul. [Edited by - Saul on August 13, 2008 9:09:49 AM]
Advertisement
Step through with a debugger and see where it fails.

Is the behaviour same in release mode? Is there some un-initialized variable that gets filled with random data?

Make sure you delete messages properly so that no garbage pointers exists etc.

And in the future, use [sour'ce] and [/sour'ce] when posting code (without the '). Makes it easier to read. :)

/Robert
"Game Maker For Life, probably never professional thou." =)
Thanks mate. Haven't looked at Release mode yet, I'm pretty sure there are no uninitialised variables around (wouldn't that cause a runtime error, anyway?).

Also, the only pointers I have around are the Direct3D ones, so no worries there.
Same error in Releae mode. It seems m_commands.size() returns 1?!. Its not empty after all? I get crashes when I try to do:
cout << (*m_commands.begin()).first;


Any ideas?
m_Commands seems to be a std::map, and I'm not entirely familiar with it - but it looks to me as if you never delete the new command from m_Commands when you're finished with it.

I think the following happens:

m_Commands gets a new entry.

// Put to the end of the queue
m_cmdQueue.push_back(sCommand);
The entry is processed and when finished the data of the command is deleted/removed.

m_Commands still hold a reference to the data, which is now gone.

m_Commands.first = NULL / broken.


I'm really tired now, so I could be terribly wrong as well. :)
But since size > 0 and .first crashes it seems plausible.

Good night!

/Robert
"Game Maker For Life, probably never professional thou." =)
Quote:Original post by Saul
Hey all,

I'm in the process of making a Game Engine, and am currently doing the Console. I have a vector for the current commands in the queue, and a map of commands (name -> function). The problem is, the commands map says that it is empty, but the debugger says that it contains items.


No, it only says that it doesn't contain the command that is searched for. That is because you are searching for the full command string (including arguments), but the command map only uses the command names for map keys.

Notice:

    if (commandExists(sCommand))//                    ^^^^^^^^    {        // Call command        m_commands[sCommand](vTokens);        return;    }    // Show "Unknown command".    msg("Unknown command: ");    msg2(sName);//       ^^^^^


BTW: zero is not a special case. There is no need or reason to check if a container is empty before searching for an element within it: if it's empty, and you run the search, it will do the exact same thing as if it contained items but not the one you were looking for - i.e. examine every element in the container (yes, all zero of them), fail to find the element, and return the .end() iterator.

Similarly, there's no point checking if a string is empty before tokenizing it. If it's empty, the tokenization process will simply create a container of zero tokens, which you already check for.

Also, checking against vector.max_size() is incredibly paranoid. I've never seen anyone else do it, in fact. You probably can't actually allocate nearly as much memory as the vector claims for its "max size", anyway.

Oh, and try to get in the habit of passing arguments of object type by const reference. :)

@Rasmadrak: Sorry, completely off. Standard library containers make copies of the things you put into them, and the vector values and map keys are std::strings, which are perfectly capable of being copied.
Well, that should teach me not posting in a near zombie-state! :D
I really thought I saw a couple of new/delete a couple of hours ago...
Maybe there's an extra m_Commands[] call somewhere, that initializes a new entry in the std::map?

Off to work, they say!

/Robert
"Game Maker For Life, probably never professional thou." =)

This topic is closed to new replies.

Advertisement