- Rapidjson headers need to be placed in the /src/Step_5/rapidjson/ directory for the program to build. For convenience, the headers from the 66eb606 commit have been included already. If desired, it may be possible to update the headers to the current commit by replacing the entire rapidjson directory in this project with the current rapidjson/include/rapidjson directory.
- In the /src/Step_5/build/ directory run
cmake ../
. The build environment will be set up in that directory using the CMakeLists.txt file in the parent directory. - Enure that the bool value for
TEST_ALL
on line 39 of main.cpp is set to the desired value:- A value of
true
will cause all test commands to be sent to the dispatcher upon program startup. - A value of
false
will skip all tests and immediately fall through to a user prompt.
- A value of
- In the /src/Step_5/build/ directory run
make
. Executable will build asdispatcher
. - To run executable, run
./dispatcher
. - If the final command (exit_command) on line 813 of main.cpp is not commented out, the exit_command will succeed, terminating execution. If it is commented out, execution will fall through to a user prompt.
- At the user prompt, JSON may be entered for parsing.
- main.cpp (with DEBUG code)
- main_no_debug.cpp (identical, except without DEBUG code)
- CMakeLists.txt file (slightly modified from prompt file)
Doxygen documentation is available for this step of the project.
Step 5 main page | ||
---|---|---|
main.cpp | Documentation | Code |
main_no_debug.cpp | Documentation | Code |
Running the program with:
TEST_ALL
enabledVERBOSE
disabledexit_command
not commented out
produced this output to the terminal:
armaan@ubuntuVM:build$ ./dispatcher
COMMAND DISPATCHER: STARTED
CommandDispatcher: addCommandHandler: help
CommandDispatcher: addCommandHandler: exit
CommandDispatcher: addCommandHandler: sum_ints
CommandDispatcher: addCommandHandler: query_payload
CommandDispatcher: addCommandHandler: mean_ints
CommandDispatcher: addCommandHandler: help
Command help already existed. Addition failed
COMMAND:
{
"command":"fail",
"payload": {
"Does not":"really matter what is in here."
}
}
EXCEPTION: Oops, no match for command found in map. Please try again.
COMMAND:
{
"command":"help",
"payload": {
"usage":"Enter json command in 'command':'<command>','payload': { // json payload of arguments }"
}
}
Controller::help: command:
Enter json command in 'command':'<command>','payload': { // json payload of arguments }
COMMAND:
{
"command":"help",
"payload": {
"reason":"Not usage."
}
}
Controller::help: command:
EXCEPTION: Oops, no member "usage" present in payload JSON. Please try again.
COMMAND:
{
"command": "sum_ints",
"payload": {
"addends": [1, 2, 3]
}
}
Controller::sum_ints command:
6
COMMAND:
{
"command": "sum_ints",
"payload": {
"addends": [1, 2, 3, 4, 5, 100, 999999999, -1, 0, -999]
}
}
Controller::sum_ints command:
999999114
COMMAND:
{
"command": "sum_ints",
"payload": {
"addends": [1, 2, 3, 4.5, 5.0, 100.1, 999999999.1, -1.1, 0, -999.1]
}
}
Controller::sum_ints command:
6
COMMAND:
{
"command": "sum_ints",
"payload": {
"addends": ["apples", "oranges", "turtles"]
}
}
Controller::sum_ints command:
EXCEPTION: Oops, no integers in array. Please try again.
COMMAND:
{
"command": "sum_ints",
"payload": {
"well":"formed",
"json":"test"
}
}
Controller::sum_ints command:
EXCEPTION: Oops, no member "addends" present in payload JSON. Please try again.
COMMAND:
{
"command": "mean_ints",
"payload": {
"addends": [1, 2, 3, 4, 5]
}
}
Controller::mean_ints command:
3
COMMAND:
{
"command": "mean_ints",
"payload": {
"addends": [1, 2, 2]
}
}
Controller::mean_ints command:
1
COMMAND:
{
"command": "mean_ints",
"payload": {
"addends": [1, 2, 3, 4, 5, 100, 999999999, -1, 0, -999]
}
}
Controller::mean_ints command:
99999911
COMMAND:
{
"command": "query_payload",
"payload": {
"hello": "world",
"t": true ,
"f": false,
"n": null,
"i": 123,
"pi": 3.1416,
"a": [1, 2, 3, 4]
}
}
Controller::payload_type command:
Type of member hello is String
Type of member t is True
Type of member f is False
Type of member n is Null
Type of member i is Number
Type of member pi is Number
Type of member a is Array
COMMAND:
{
"command":"exit",
"payload": {
"booga":"gooba"
}
}
Controller::exit: command:
EXCEPTION: Oops, no member "reason" present in payload JSON. Please try again.
COMMAND:
{
"command":"exit",
"payload": {
"reason":"Exiting program on user request."
}
}
Controller::exit: command:
Exiting program on user request.
COMMAND DISPATCHER: ENDED
armaan@ubuntuVM:build$
This output matches expectations.
Running the program with:
TEST_ALL
disabledVERBOSE
enable
produced this output to the terminal, including user commands entered at prompt:
armaan@ubuntuVM:build$ ./dispatcher
COMMAND DISPATCHER: STARTED
CommandDispatcher: addCommandHandler: help
Command help added to map.
CommandDispatcher: addCommandHandler: exit
Command exit added to map.
CommandDispatcher: addCommandHandler: sum_ints
Command sum_ints added to map.
CommandDispatcher: addCommandHandler: query_payload
Command query_payload added to map.
CommandDispatcher: addCommandHandler: mean_ints
Command mean_ints added to map.
CommandDispatcher: addCommandHandler: help
Command help already existed. Addition failed
COMMANDS: {"command":"exit", "payload":{"reason":"User requested exit."}}
enter command : {"command":"help","payload":{"usage":"something arbitrary"}}
COMMAND: {"command":"help","payload":{"usage":"something arbitrary"}}
found command: help
found payload: {"usage":"something arbitrary"}
help matched a value in map!
Attempting to start command handler
Controller::help: command:
something arbitrary
COMMANDS: {"command":"exit", "payload":{"reason":"User requested exit."}}
enter command : {"command":"exit","payload":{"reason":"I've been looking at the computer for far too long, and my eyes are going square."}}
COMMAND: {"command":"exit","payload":{"reason":"I've been looking at the computer for far too long, and my eyes are going square."}}
found command: exit
found payload: {"reason":"I've been looking at the computer for far too long, and my eyes are going square."}
exit matched a value in map!
Attempting to start command handler
Controller::exit: command:
I've been looking at the computer for far too long, and my eyes are going square.
COMMAND DISPATCHER: ENDED
armaan@ubuntuVM:build$
This output also matches expectations.
- Pass:
- Avoid anomalous behavior, including but not limited to:
- unhandled exceptions
- seg faults
- other memory errors
- Produce output identical to examples from prompt.
- Successfully add commands to map.
- If conflict, throw and handle exception.
- Parse strings for JSON.
- If no JSON present, throw and handle exception.
- If JSON present, parse JSON for "command" and "payload" key/value pairs.
- If one or both not present, throw and handle exception.
- If both present, query map for matching command.
- If no matching command, throw and handle exception.
- If matching command found, pass value stored by "payload" key to command handler function.
- Inside of each command handler function, parse value passed in for JSON.
- If no match for required key/value pair, throw and handle exception.
- If match for required key/value pair, safely determine if value contains valid data.
- If value does not contain valid data, throw and handle exception.
- If value either contains only valid data, or both valid and invalid data, handle command using valid data.
- Continue indefinitely until "exit" command successfully handled.
- Avoid anomalous behavior, including but not limited to:
- Fail: Any other result.
The project involved gluing together parts of an already-established codebase, and creating new functionality on top of it. To facilitate smooth operation, needed to establish what constraints the existing code placed on my implementations. Here are the constraints identified:
- main()
- adds command handlers in Controller class to CommandDispatcher using addCommandHandler
- invokes CommandDispatcher::dispatchCommand using a raw string.
- CommandDispatcher()
- stores command handlers in Controller class using a private map
- invokes command handler functions using either rapidjson::value or rapidjson::document objects
- Controller()
- implements command handler functions
- return values are bool
To design around these constraints, I needed to parse out a reasonable division of labor for the functions I implemented, and determine how information would be passed back and forth. I chose to allow functions at all depths to print to the user, except on error conditions. On errors, I chose to throw exceptions which get passed back to and handled by main(). Were I to continue to improve on this code, I would consider creating a shared print function for all the other functions to call so the output stream is consolidated to one place.
Here is a psuedo-code division of labor for my implementations:
- main()
- prints success to user
- catches failure message exceptions from all other parts of program and passes them to exceptionPrinter
- exceptionPrinter()
- prints to user
- returns to main
- CommandDispatcher()
- parses raw string from main into a rapidjson::document
- performs initial checks to verify that "command" and "payload" values exist
- attempts to find command handler function matching "command"
- extracts "payload" value from document and passes by reference to command handler
- prints success to user
- passes failures as exceptions back to main
- passes exceptions from Controller back to main
- Controller()
- performs commanded functions
- functions check whether their individual parameters are satisfied.
- prints success to user
- passes failures as exceptions back to CommandDispatcher
- Platform Dispatcher See attached *.cpp file
The prompt cpp file is available here: /prompt/dispatcher_challenge.cpp
Since I have not had prior exposure to several concepts needed for this task, I performed significant research and brainstorming to understand what was going on and what I needed to do. Some information about that process is available in these sections of the Appendix:
The general environment remains the same as the previous steps.
I set up my CMAKE environment based on the prompt CMakeLists.txt available here: /prompt/CMakeLists.txt. My final CMakeLists.txt file has some changes. Commentary is available in the Reference in Appendix.
Considerations for creating documentation remain the same as for Step 3: Reference in Step 3 Appendix