Coding the States
Here’s the code for the NumberEntry state:
class NumberEntryState : public FSMState<Calculator> { protected: string accum; public: NumberEntryState(Calculator *adriver) : FSMState<Calculator>(adriver, "NumberEntryState") { } void OnEntry() { Append(); } FSMStateBase *HandleEvent() { if (isIn("01234567890.Ee")) { Append(); return this; } else if (isIn("+-*/")) { ParseAndPush(); return driver->getState("OperatorState"); } else if (isIn("=")) { ParseAndPush(); return driver->getState("EqualsState"); } else { return driver->getState("ErrorState"); } } void Append() { accum += Current; } void ParseAndPush() { double x = strtod(accum.c_str(), NULL); accum = ""; driver->PostFixQueue.push(new CalcStackItem(x)); } };
Notice the parts involved. The OnEntry function gets called whenever the machine enters this state. There’s also a HandleEvent method, which handles all incoming characters. Inside the HandleEvent method, I just have an if statement that checks what character comes in. I wrote my own isIn function to check whether a character is in a string. Such functions already exist in the runtime library, but this one is specifically for this purpose.
Look at the different parts of the if block. The first part checks whether I received a digit or a decimal point, or a capital or lowercase E. If so, I perform the action described in the table, and the same for all the different possible events.
Additionally, I provided my two custom methods that serve as the actions—the Append function and the ParseAndPush function. Of course, the Append function is just a single line, so I could have just used that one line in place of calling Append; either is fine.
As for how the methods in this class get called, that’s the easy part. As you’ll see when I show you the rest of the code, you have to create the instance of this class, but the framework does the rest of the work, calling your methods for you.