Dispatch Challenge
Syncmultiplethreadssharingaresource
main_no_debug.cpp
Go to the documentation of this file.
1 #include <iostream>
2 #include <map>
3 #include <string>
4 #include <iostream>
5 #include <functional>
6 #include <exception>
7 #include <vector>
8 
9 //
10 // supporting tools and software
11 //
12 // Validate and test your json commands
13 // https://jsonlint.com/
14 
15 // RapidJSON : lots and lots of examples to help you use it properly
16 // https://github.com/Tencent/rapidjson
17 //
18 
19 // std::function
20 // std::bind
21 // std::placeholders
22 // std::map
23 // std::make_pair
24 
25 #include "rapidjson/document.h" // DOM API
26 #include "rapidjson/writer.h"
27 #include "rapidjson/stringbuffer.h"
28 
29 using namespace rapidjson;
30 using namespace std;
31 // namespace placeholders used in main()
32 
33 
34 // Operational globals. Do not change.
35 bool g_done = false;
36 
37 
38 // User globals. Ok to change.
39 bool TEST_ALL = true;
40 bool VERBOSE = false;
41 
42 
43 
52 void exceptionPrinter(const char* excpt) {
53  cout << "EXCEPTION: Oops, " << excpt
54  << ". Please try again." << endl;
55 }
56 
57 
58 
59 //
60 // TEST COMMANDS
61 //
62 
63 auto fail_command = R"(
64  {
65  "command":"fail",
66  "payload": {
67  "Does not":"really matter what is in here."
68  }
69  }
70 )";
71 
72 
73 auto help_command = R"(
74  {
75  "command":"help",
76  "payload": {
77  "usage":"Enter json command in 'command':'<command>','payload': { // json payload of arguments }"
78  }
79  }
80 )";
81 
83  {
84  "command":"help",
85  "payload": {
86  "reason":"Not usage."
87  }
88  }
89 )";
90 
91 auto exit_command = R"(
92  {
93  "command":"exit",
94  "payload": {
95  "reason":"Exiting program on user request."
96  }
97  }
98 )";
99 
101  {
102  "command":"exit",
103  "payload": {
104  "booga":"gooba"
105  }
106  }
107 )";
108 
109 auto sum_command = R"(
110  {
111  "command": "sum_ints",
112  "payload": {
113  "addends": [1, 2, 3]
114  }
115  }
116 )";
117 
119  {
120  "command": "sum_ints",
121  "payload": {
122  "addends": [1, 2, 3, 4, 5, 100, 999999999, -1, 0, -999]
123  }
124  }
125 )";
126 
128  {
129  "command": "sum_ints",
130  "payload": {
131  "addends": [1, 2, 3, 4.5, 5.0, 100.1, 999999999.1, -1.1, 0, -999.1]
132  }
133  }
134 )";
135 
137  {
138  "command": "sum_ints",
139  "payload": {
140  "addends": ["apples", "oranges", "turtles"]
141  }
142  }
143 )";
144 
146  {
147  "command": "sum_ints",
148  "payload": {
149  "well":"formed",
150  "json":"test"
151  }
152  }
153 )";
154 
155 auto mean_command = R"(
156  {
157  "command": "mean_ints",
158  "payload": {
159  "addends": [1, 2, 3, 4, 5]
160  }
161  }
162 )";
163 
165  {
166  "command": "mean_ints",
167  "payload": {
168  "addends": [1, 2, 2]
169  }
170  }
171 )";
172 
174  {
175  "command": "mean_ints",
176  "payload": {
177  "addends": [1, 2, 3, 4, 5, 100, 999999999, -1, 0, -999]
178  }
179  }
180 )";
181 
182 // This fails to register as valid JSON for some reason.
183 // Think there might be a whitespace issue.
185  {
186  "command": "query_payload",
187  "payload": {
188  "well": "formed",
189  "json": "test",
190  "a": [1,2,3,4],
191  "number1": 1,
192  "number2": 34.7,
193  "b": [
194  "something":"more",
195  "here":"too"
196  ]
197  }
198  }
199 )";
200 
202  {
203  "command": "query_payload",
204  "payload": {
205  "hello": "world",
206  "t": true ,
207  "f": false,
208  "n": null,
209  "i": 123,
210  "pi": 3.1416,
211  "a": [1, 2, 3, 4]
212  }
213  }
214 )";
215 
216 
217 
221 class Controller {
222 public:
223 
235  bool help(rapidjson::Value &payload)
236  {
237  cout << "Controller::help: command: \n";
238 
239  // create iterator to look for "usage".
240  rapidjson::Value::ConstMemberIterator itr = payload.FindMember("usage");
241 
242  // safely check for a value "usage"
243  if (itr != payload.MemberEnd()) {
244 
245  // value exists. print to user
246  cout << itr->value.GetString() << endl;
247  }
248  // if does not exist, throw exception
249  else {
250  throw "no member \"usage\" present in payload JSON";
251  }
252 
253  return true;
254  }
255 
256 
266  bool exit(rapidjson::Value &payload)
267  {
268  cout << "Controller::exit: command: \n";
269 
270  // create iterator to look for "reason".
271  rapidjson::Value::ConstMemberIterator itr = payload.FindMember("reason");
272 
273  // safely check for a value "reason"
274  if (itr != payload.MemberEnd()) {
275 
276  // value exists. print to user
277  cout << itr->value.GetString() << endl;
278 
279  // terminate loop in main, causing controlled exit.
280  g_done = true;
281 
282  }
283  // if does not exist, throw exception
284  else {
285  throw "no member \"reason\" present in payload JSON";
286  }
287 
288  return true;
289  }
290 
291 
292  //
293  // Additional commands below
294  //
295 
296 
306  bool query_payload(rapidjson::Value &payload)
307  {
308  cout << "Controller::payload_type command: \n";
309 
310  // array storing typenames
311  static const char* kTypeNames[] = {
312  "Null",
313  "False",
314  "True",
315  "Object",
316  "Array",
317  "String",
318  "Number"
319  };
320 
321  for (Value::ConstMemberIterator itr = payload.MemberBegin();
322  itr != payload.MemberEnd(); ++itr) {
323 
324  cout << "Type of member " << itr->name.GetString()
325  << " is " << kTypeNames[itr->value.GetType()] << endl;
326  }
327 
328  return true;
329  }
330 
331 
332 
348  bool sum_ints(rapidjson::Value &payload)
349  {
350  cout << "Controller::sum_ints command: \n";
351 
352  // create iterator to look for "addends".
353  rapidjson::Value::ConstMemberIterator itr = payload.FindMember("addends");
354 
355  // safely check for a value "addends"
356  if ( itr != payload.MemberEnd( )) {
357 
358  // value exists. check for array of numbers
359 
360  int num_ints = 0; // efficient vector allocation
361 
362  // reference for consecutive access
363  const Value& a = payload["addends"];
364 
365  // make sure addends is an array. throw exception if not
366  if ( ! a.IsArray() )
367  throw "payload value \"addends\" is not an array";
368 
369  // initial loop for vector allocation optimization
370  for ( auto& v : a.GetArray() ) {
371  if (v.IsInt() ) {
372  ++num_ints;
373  }
374  }
375 
376  // if no integers in array, throw exception
377  if ( num_ints == 0 )
378  throw "no integers in array";
379 
380  // create vector and allocate
381  vector<int> int_vect;
382  int_vect.reserve(num_ints);
383 
384  int sum_i = 0;
385 
386  // loop through array again, filling vector
387  for (auto& v : a.GetArray()) {
388 
389  // only add int elements
390  if (v.IsInt()) {
391  int_vect.push_back(v.GetInt());
392  }
393  }
394 
395  sum_i = summation(int_vect);
396 
397  cout << sum_i << endl;
398 
399  }
400  // if does not exist, throw exception
401  else {
402  throw "no member \"addends\" present in payload JSON";
403  }
404 
405  return true;
406  }
407 
408 
409 
429  bool mean_ints(rapidjson::Value &payload)
430  {
431  cout << "Controller::mean_ints command: \n";
432 
433  // create iterator to look for "addends".
434  rapidjson::Value::ConstMemberIterator itr = payload.FindMember("addends");
435 
436  // safely check for a value "addends"
437  if ( itr != payload.MemberEnd() ) {
438 
439  // value exists. check for array of numbers
440 
441  int num_ints = 0; // efficient vector allocation
442 
443  // reference for consecutive access
444  const Value& a = payload["addends"];
445 
446  // make sure addends is an array. throw exception if not
447  if ( ! a.IsArray() )
448  throw "payload value \"addends\" is not an array";
449 
450  // initial loop for vector allocation optimization
451  for ( auto& v : a.GetArray() ) {
452  if ( v.IsInt() ) {
453  ++num_ints;
454  }
455  }
456 
457  // if no integers in array, throw exception
458  if ( num_ints == 0 )
459  throw "no integers in array";
460 
461  // create vector and allocate
462  vector<int> int_vect;
463  int_vect.reserve(num_ints);
464 
465  int sum_i = 0;
466 
467  // loop through array again, filling vector
468  for ( auto& v : a.GetArray() ) {
469 
470  // only add int elements
471  if ( v.IsInt() )
472  int_vect.push_back(v.GetInt());
473  }
474 
475  sum_i = summation(int_vect);
476 
478  cout << (sum_i/num_ints) << endl;
479  }
480  // if does not exist, throw exception
481  else {
482  throw "no member \"addends\" present in payload JSON";
483  }
484 
485  return true;
486  }
487 
488 
493 
494 
495 private:
496 
511  template<typename T>
512  static auto summation(vector<T> collection) -> T {
513 
514  T sum = 0; // initialize
515 
516  // loop through array
517  for (auto& num : collection) {
518 
519  // add current number to sum
520  sum += num;
521  }
522 
523  return sum;
524  }
525 
526 };
527 
528 
529 
530 // Bonus Question: why did I type cast this?
531 //
532 // typedef: To make life easier
533 // typecast: To pass in a rapidjson::Value& and return a bool
534 // I am more or less bypassing any false returns by throwing
535 // exceptions, but true returns indicate that the command
536 // was successfully handled.
537 typedef std::function<bool(rapidjson::Value &)> CommandHandler;
538 
539 
540 
541 class CommandDispatcher {
542 public:
543  // ctor - need impl
545  {
546  // My implementation uses the default constructor.
547  // I suspect that a more complex constructor might be needed if the map was
548  // populated using a different method.
549  }
550 
551 
552  // dtor - need impl
554  {
555  // question why is it virtual? Is it needed in this case?
556  //
557  // Virtual refers to members that an inhereting class will implement that the
558  // superclass does not implement, or to a member that the inhereting class will
559  // overload.
560  //
561  // I am not sure why this class would not have an explicit destructor but one of
562  // its inhereting classes would. I am not using inhereting classes for this. If
563  // I was, perhaps this class would use the implicit destructor, but the inhereting
564  // classes would need more complex ones.
565  //
566  // In my case, I do not believe it is needed.
567  }
568 
569 
578  bool addCommandHandler(std::string command, CommandHandler handler)
579  {
580  cout << "CommandDispatcher: addCommandHandler: " << command << std::endl;
581 
582  bool add_succeeded = true;
583 
585  auto test = command_handlers_.insert( std::make_pair( command, handler));
586 
588  if( test.second == false) {
589  cout << "Command " << command << " already existed. Addition failed" << endl;
590  add_succeeded = false;
591  }
592  else {
593  if(VERBOSE) {
594  cout << "Command " << command << " added to map." << endl;
595  }
596  }
597 
598  return add_succeeded;
599  }
600 
601 
616  bool dispatchCommand(std::string command_json)
617  {
618  cout << "\n\nCOMMAND: " << command_json << endl;
619 
621  const char *command_ptr = command_json.c_str();
622 
623  // parse the received string
624  this->doc.Parse(command_ptr);
625 
626  // check if JSON passed in. If not, throw error.
627  if( this->doc.HasParseError() )
628  throw "malformed JSON";
629 
630  // create iterators for "command" and "payload", if they exist.
631  rapidjson::Value::ConstMemberIterator itr_c = this->doc.FindMember("command");
632  rapidjson::Value::ConstMemberIterator itr_p = this->doc.FindMember("payload");
633 
634 
635  // safely check for a value "command"
636  if (itr_c != this->doc.MemberEnd()) {
637 
638  if(VERBOSE) {
639  cout << "found command: " << itr_c->value.GetString() << endl;
640  }
641  }
642  // if does not exist, throw exception
643  else {
644  throw "no member \"command\" present in JSON";
645  }
646 
647 
648  // safely check for a value "payload"
649  if (itr_p != this->doc.MemberEnd()) {
650 
651  if(VERBOSE) {
652  // stringify the value
653  StringBuffer buffer;
654  Writer<StringBuffer> writer(buffer);
655  itr_p->value.Accept(writer);
656 
657  cout << "found payload: " << buffer.GetString() << endl;
658  }
659  }
660  // if does not exist, throw exception
661  else {
662  throw "no member \"payload\" present in JSON";
663  }
664 
665 
666  // command routing
667  bool command_found = false; // until proven otherwise
668  for( this->map_itr = command_handlers_.begin() ;
669  this->map_itr != command_handlers_.end(); this->map_itr++ ) {
670 
671  // try to find match in map for command string
672  if( itr_c->value.GetString() == (*map_itr).first ) {
673 
674  if(VERBOSE) {
675  cout << itr_c->value.GetString() << " matched a value in map!" << endl
676  << "Attempting to start command handler" << endl;
677  }
678 
679  command_found = true;
680 
681  // dispatch payload to command handler
682  (*map_itr).second(this->doc["payload"]);
683 
684  break; // don't keep looping longer than necessary
685  }
686  }
687 
688  // if command does not exist, throw exception
689  if ( ! command_found) {
690  throw "no match for command found in map";
691  }
692 
693  return command_found; // should always be true if execution falls through to here
694  }
695 
696 
697 private:
698  std::map<std::string, CommandHandler> command_handlers_;
699 
700  std::map<std::string, CommandHandler>::iterator map_itr =
701  this->command_handlers_.begin();
702 
703  rapidjson::Document doc; // DOM API document
704 
705  // Question: why delete these?
706  //
707  // generate compile error if copy attempted
708  // (supposed to be un-copyable)
709 
710  // delete unused constructors
711  CommandDispatcher (const CommandDispatcher&) = delete;
712  CommandDispatcher& operator= (const CommandDispatcher&) = delete;
713 
714 };
715 
716 
717 
718 int main()
719 {
720  std::cout << "COMMAND DISPATCHER: STARTED" << std::endl;
721 
722  CommandDispatcher command_dispatcher;
723  Controller controller; // controller class of functions to "dispatch" from Command Dispatcher
724 
725  // bind controller functions to function objects to facilitate map addition
726  //
727  // std::placeholders::_1 refers to first (and only) argument for Controller member
728  // functions of type rapidjson::Value
729  using namespace placeholders;
730 
731  auto help_b = std::bind( &Controller::help, controller, _1 );
732  auto exit_b = std::bind( &Controller::exit, controller, _1 );
733  auto sum_ints_b = std::bind( &Controller::sum_ints, controller, _1 );
734  auto query_payload_b = std::bind( &Controller::query_payload, controller, _1 );
735  auto mean_ints_b = std::bind( &Controller::mean_ints, controller, _1 );
736 
737  // Add available command handlers in Controller class to CommandDispatcher manually
738  command_dispatcher.addCommandHandler( "help", help_b );
739  command_dispatcher.addCommandHandler( "exit", exit_b);
740  command_dispatcher.addCommandHandler( "sum_ints", sum_ints_b);
741  command_dispatcher.addCommandHandler( "query_payload", query_payload_b);
742  command_dispatcher.addCommandHandler( "mean_ints", mean_ints_b);
743 
744  // DEBUG - should generate warning on fail because "help" already exists in map
745  command_dispatcher.addCommandHandler( "help", help_b);
746 
747 
748  // array of test commands
758  string test_commands[] = {
759 
760  fail_command, // */
761 
762  help_command,
763  help_command_fail, // */
764 
765  sum_command,
769  sum_command_fail_2, // */
770 
771  mean_command,
773  mean_command_long, // */
774 
777 
779  exit_command // */
780 
781  };
782 
783 
784  // if set, send all test commands to command dispatcher
785  if (TEST_ALL) {
786 
787  for (auto& com : test_commands) {
788 
789  //catch and handle each exception
790  try {
791  command_dispatcher.dispatchCommand(com);
792  }
793  catch (const char* excpt) {
794  exceptionPrinter(excpt);
795  }
796  }
797  }
798 
799 
800  // command line interface for testing
801  string command;
802  while( ! g_done ) {
803  cout << "\n\n\n";
804  cout << "COMMANDS: {\"command\":\"exit\", \"payload\":{\"reason\":\"User requested exit.\"}}\n";
805  cout << "\tenter command : ";
806  getline(cin, command);
807  try {
808  command_dispatcher.dispatchCommand(command);
809  }
810  catch (const char* excpt) {
811  exceptionPrinter(excpt);
812  }
813  }
814 
815  std::cout << "COMMAND DISPATCHER: ENDED" << std::endl;
816  return 0;
817 }
controller class of functions to "dispatch" from Command Dispatcher
Definition: main.cpp:220
auto fail_command
bool query_payload(rapidjson::Value &payload)
command handler for query_payload
auto sum_command_fail_2
void exceptionPrinter(const char *excpt)
EXCEPTION HANDLER function.
bool VERBOSE
turn on verbose messages
auto sum_command_long
STL namespace.
bool sum_ints(rapidjson::Value &payload)
command handler for sum_ints
auto mean_command_long
bool help(rapidjson::Value &payload)
command handler for help
auto sum_command_floats
virtual ~CommandDispatcher()
auto exit_command
auto exit_command_fail
bool mean_ints(rapidjson::Value &payload)
command handler for mean_ints
auto sum_command
bool TEST_ALL
test all functionality before prompting user
bool dispatchCommand(std::string command_json)
initial receiver of command
bool exit(rapidjson::Value &payload)
command handler for exit
auto mean_command
auto help_command
std::function< bool(rapidjson::Value &)> CommandHandler
std::function< bool(rapidjson::Value &)> CommandHandler
Definition: main.cpp:546
auto mean_command_fraction
int main()
auto query_payload_command_2
bool addCommandHandler(std::string command, CommandHandler handler)
adds a command and handler pair to the map
auto help_command_fail
auto query_payload_command
static auto summation(vector< T > collection) -> T
summation function for sum_ints() and mean_ints()
auto sum_command_fail_1
bool g_done