Example: User Interfaces

Generally speaking user interfaces can be classified as follows:

menu-driven user interfaces (MUI)
interpreters or console user interfaces (CUI)
graphical user interfaces (GUI)

Unfortunately, C++ doesn't provide support for graphics or graphical user interfaces in the standard libraries. We would need a proprietary library such as MFC to build a GUI. This leaves CUIs or MUIs to choose from, and CUIs are easier to use then MUIs (nobody likes navigating through menu trees). Fortunately, there's a pretty standard pattern for CUI's:

prompt the user for a command
read command
execute command
print result
repeat until user quits

We can implement this as a reusable function:

void controlLoop()
{
   string cmmd;
   cout << "type \"quit\" to quit\n";
   while(true)
   {
      cout << "-> "; // prompt user for a command
      cin >> cmmd;   // read command
      if (cmmd == "quit") break;
      // execute command & print result:
      cout << execute(cmmd) << endl;
   }
   cout << "bye\n";
}

Of course the real work is being done by the execute function, which is application specific. For example, suppose we wanted to implement a simple calculator able to execute the following commands:

add ARG1 ARG2
mul ARG1 ARG2
sub ARG1 ARG2
div ARG1 ARG2

where ARG1 and ARG2 are arbitrary decimal numbers.

Our implementation doesn't do much error checking, yet. For example, we assume exactly arguments are waiting for us in the input stream, cin, and that both are well formed decimal numbers. After extracting our arguments, we use a multi-way conditional to identify the operator (add, mul, sub, div, or something else), then perform the specified arithmetic:

double execute(string cmmd)
{
   double arg1, arg2;
   cin >> arg1 >> arg2; // fetch arguments
   if (cmmd == "add") return arg1 + arg2;
   else if (cmmd == "mul") return arg1 * arg2;
   else if (cmmd == "sub") return arg1 - arg2;
   else if (cmmd == "div")
   {
      if (arg2) return arg1 / arg2;
      cerr << "Error: can't divide by 0\n";
      return 0;
   }
   else
   {
      cerr << "Error: unrecognized command: " << cmmd << endl;
      cin.sync(); // flush buffer (more on this later)
      return 0;
   }
}

There's not much for main to do:

int main()
{
   controlLoop();
   return 0;
}

Here's the output from a sample run:

type "quit" to quit
-> add 9.1 3.45
12.55
-> mul 2 18
36
-> sub 3 29.2
-26.2
-> div 4 0
Error: can't divide by 0
0
-> div 22 .03
733.333
-> mod 8 3
Error: unrecognized command: mod
0
-> quit
bye

Warning: we need to be a bit careful about assembling this program. The declaration of execute must precede the definition of controlLoop, or the compiler won't be able to figure out the return type of execute where it is called by controlLoop, more on this later. We also need the following include directives:

#include <iostream>
#include <string>
using namespace std;