0.9.6:Using the console

From DXFWiki

Jump to: navigation, search

Note that the following documentation is for an old version of DXFramework and may be incomplete or incorrect

The console is an extremely valuable debugging tool when programming real time applications with DXFramework. The console allows you to provide arbitrary input and output to all parts of your program during a run. You can use it to change variables and settings on the fly, or peek at what is really going on under the hood.

The console is created automatically in the Game class. The console is activated/deactivated using the tilde/accent grave key "`/~". The console is used for displaying text output and receiving command-line input during a run.

Contents

Output

Not being able to use printf or cout for debugging and other uses is a problem for programmers who grew up in console environments. The console class is a great way to assist programmers who prefer to use these methods to query internal workings of a program during a run.

Sending output to the console window

To use the console, simply add strings to the Console::output member, and end the string with a newline character. Use is very similar to std::cout. If you fail to end the string with a newline character, the output will not be flushed to the screen but will stay cached. I find that using std::endl is a great, clear way to flush console output.

Examples of writing console output

Here are some examples of console output:

Console::output << L"Joystick not found." << std::endl;

This line will display the string "Joystick not found" in the console output. std::endl flushes the console output to screen on the next frame render.

Console::output << L"X: " << x << L" Y: " << y << std::endl;

To view more examples, simply do a search in the solution for "Console::Output".

Output limitations

  • Console output that is longer than the amount of characters on a line does not wrap. Be careful to split long lines of output with newlines.

Input

The console has a very powerful command line feature that can be used to interactively query and change state during a run. Commands are essentially a mapping from command name to function pointer. The first word (whitespace delimited) typed in to the command line is looked up against the list of registered commands, and then the function pointer associated with that command is called with the entire command line passed as a special argument.

The command line is split up into tokens according to whitespace and quotes. These tokens are passed in a std::vector of wstrings to the function associated with the command (the first token on the line). For example, the command

add 1 2

is split up into 3 tokens: "add" "1" and "2" and the function pointer associated with "add" is called. The command

fnord "all your base" { are } 

is split up into 5 tokens: "fnord" "all your base" "{" "are" "}" and the function pointer associated with "fnord" is called. Commands thus need to be one token, preferably without quotes. Two word commands are illegal unless you implement the distinction inside the function that is called:

go up 14
go down 14

both split into 3 tokens and both call the same function, the function associated with the "go" command.

Default commands

The default commands registered by the engine are defined in the dxf_console.cpp file. You can list these commands by typing help, which itself is a command that lists all registered commands:

void help(std::vector<std::wstring>& argv) {
	Console::pConsole->ListCommands();
}

The default commands are registered by the Load function of the console class. The RegisterCommand function is used to actually create a mapping between a command name and the function pointer that implements the command:

RegisterCommand(L"help", &help);

This line calls help() whenever "help" is matched. Notice you can create a multiple to one mapping:

RegisterCommand(L"exit", &quit);
RegisterCommand(L"quit", &quit);

Both commands "exit" and "quit" call the same function quit().

Creating new commands

You create commands using an implementation of the RegistrarInterface. In your registrar definition file, define the functions that do the work intended by your command. You can look at the registrar files (registrar.cpp) in the demos for examples of how to do this, such as add in DXFramework-Demo registrar.cpp:

void add(std::vector<std::wstring>& argv) {
	if (argv.size() < 3) {
		dxf::Console::output << L"I need at least 2 numbers to add anything." << std::endl;
	} 

	int accumulator = 0;
	for (std::vector<std::wstring>::iterator iter = ++(argv.begin()); iter != argv.end(); ++iter) {
		accumulator += dxf::toint(*iter);
	}
	dxf::Console::output << L"Result is " << accumulator << std::endl;
}

Notice the function signature of this function matches the default commands defined in dxf_console.cpp. In fact, all commands to be registered must match this function signature in order for the mapping (the call to RegisterCommand) to compile.

Register your command by calling DXFRegisterCommand in the RegisterCommands function of your registrar:

void Registrar::RegisterCommands() {
	dxf::DXFRegisterCommand(L"add", &add);	
}

This is called when bootstrapping the system on startup. This will register your personalized commands so that the console can make the appropriate calls when you input your command in the console input line.

Listing commands

You can list all registered commands by typing 'help' in the console input line. 'help' is a default command defined and registered in dxf_console.cpp.

Partial matching of commands

A simple algorithm is employed so that only enough letters to resolve ambiguity between commands is required. For example, as long as 'help' is the only command that starts with an 'h', an 'h' is all that is required to call the help command. If there happened to be another command 'heat', then three letters are required to distinguish which command you intended on calling.

Personal tools