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