diff --git a/design_log/Step_5.md b/design_log/Step_5.md index 064289f..058d1f3 100644 --- a/design_log/Step_5.md +++ b/design_log/Step_5.md @@ -393,6 +393,10 @@ Since I have not had prior exposure to several concepts needed for this task, I * [Brainstorming (Appendix)](Step_5_Appendix.md#brainstorming) * [Research (Appendix)](Step_5_Appendix.md#research) +### Fix for bind issue + +* [Bind issue (Appendix)](Step_5_Appendix.md#bind-issue) + ### Environment The general environment remains the same as the previous steps. diff --git a/design_log/Step_5_Appendix.md b/design_log/Step_5_Appendix.md index 8237931..fd05264 100644 --- a/design_log/Step_5_Appendix.md +++ b/design_log/Step_5_Appendix.md @@ -283,15 +283,76 @@ Deciding to manually add the functions in main() // It looks like C++ is not able to implement a programmatic enumberation of // a class's public member functions, so I need to do it manually in main(). - + + +
+ +Bind issue +--- + +### Hint + +Received this email: + +> Armaan, +> +> Hint on the dispatcher. Bind is required and not static questions. + +This was not wholely unexpected. Using static member functions of `Controller` class produced results expected during testing, but did not make use of `std::bind` which was included as a hint in one of the files' headers. It seems logical that some other test conditions which I have not encountered produce unexpected results. + +### Issue fix + +Problem code: + +```c++ + // Add available command handlers in Controller class to CommandDispatcher manually + /// + /// \note needs static functions + /// + /// \todo maybe use std::bind instead to enable using non-static functions. + /// + command_dispatcher.addCommandHandler( "help", controller.help); + command_dispatcher.addCommandHandler( "exit", controller.exit); + command_dispatcher.addCommandHandler( "sum_ints", controller.sum_ints); + command_dispatcher.addCommandHandler( "query_payload", controller.query_payload); + command_dispatcher.addCommandHandler( "mean_ints", controller.mean_ints); +``` + +Fixed code: + +```c++ + // bind controller functions to function objects to facilitate map addition + // + // std::placeholders::_1 refers to first (and only) argument for Controller member + // functions of type rapidjson::Value + using namespace placeholders; + + auto help_b = std::bind( &Controller::help, controller, _1 ); + auto exit_b = std::bind( &Controller::exit, controller, _1 ); + auto sum_ints_b = std::bind( &Controller::sum_ints, controller, _1 ); + auto query_payload_b = std::bind( &Controller::query_payload, controller, _1 ); + auto mean_ints_b = std::bind( &Controller::mean_ints, controller, _1 ); + + // Add available command handlers in Controller class to CommandDispatcher manually + command_dispatcher.addCommandHandler( "help", help_b ); + command_dispatcher.addCommandHandler( "exit", exit_b); + command_dispatcher.addCommandHandler( "sum_ints", sum_ints_b); + command_dispatcher.addCommandHandler( "query_payload", query_payload_b); + command_dispatcher.addCommandHandler( "mean_ints", mean_ints_b); +``` + +This allows all Controller member functions to be non-static. + + +
Reference --- -### std::function << know this +### std::function -### std::bind << need to look up +### std::bind * Binds parameters to functions * Guaranteed to make a copy of argument passed in. @@ -337,9 +398,9 @@ shell:user$ ./bind.out source: https://www.youtube.com/watch?v=JtUZmkvroKg -### std::placeholders << need to look up +### std::placeholders -### std::map << know this +### std::map key / value pair @@ -369,7 +430,7 @@ source: https://www.youtube.com/watch?v=6iyzPed7FrM Note: class template does NOT infer parameter types. Function template does. -### std::make_pair << need to look up +### std::make_pair
diff --git a/docs/Step_5/html/index.html b/docs/Step_5/html/index.html index 7b6d97f..a98a2ee 100644 --- a/docs/Step_5/html/index.html +++ b/docs/Step_5/html/index.html @@ -65,28 +65,28 @@

To duplicate

    -
  1. Rapidjson headers need to be placed in the /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.
  2. +
  3. Rapidjson headers need to be placed in the /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 the current rapidjson/include/rapidjson directory.
-
    +
    1. 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.
    -
      +
      1. Enure that the bool value for TEST_ALL on line 37 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.
      -
        +
        1. In the /src/Step_5/build/ directory run make. Executable will build as dispatcher.
        -
          +
          1. To run executable, run ./dispatcher.
          -
            +
            1. If the final command (exit_command) on line 804 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.
            -
              +
              1. At the user prompt, JSON may be entered for parsing.

              Files

              @@ -228,6 +228,12 @@

              Research

            1. Brainstorming (Appendix)
            2. Research (Appendix)
            3. + +

              Fix for bind issue

              + +

              Environment

              The general environment remains the same as the previous steps.

              CMAKE

              diff --git a/docs/Step_5/html/main_8cpp.html b/docs/Step_5/html/main_8cpp.html index 49beecb..b7f8176 100644 --- a/docs/Step_5/html/main_8cpp.html +++ b/docs/Step_5/html/main_8cpp.html @@ -81,6 +81,8 @@ Include dependency graph for main.cpp:
+ +

Go to the source code of this file.

@@ -164,7 +166,7 @@

-

Definition at line 544 of file main.cpp.

+

Definition at line 546 of file main.cpp.

@@ -205,7 +207,7 @@

Warning
NOT thread-safe; must be called within a thread-safe scope
-

Definition at line 52 of file main.cpp.

+

Definition at line 54 of file main.cpp.

@@ -234,7 +236,7 @@

Warning
NOT thread-safe; must be called within a thread-safe scope
-

Definition at line 68 of file main.cpp.

+

Definition at line 70 of file main.cpp.

@@ -252,13 +254,11 @@

-
Note
needs static functions
-
Todo:
maybe use std::bind instead to enable using non-static functions.
Note
exit_command commented out to allow fallthrough to user prompt. If uncommented, exit_command will successfully exit program prior to user prompt. Ok to uncomment if desired.
The "//
-

Definition at line 750 of file main.cpp.

+

Definition at line 753 of file main.cpp.

@@ -277,7 +277,7 @@

Definition at line 39 of file main.cpp.

+

Definition at line 41 of file main.cpp.

@@ -293,7 +293,7 @@

Initial value:
= R"(
{
"command":"exit",
"payload": {
"reason":"Exiting program on user request."
}
}
)"
-

Definition at line 107 of file main.cpp.

+

Definition at line 109 of file main.cpp.

@@ -309,7 +309,7 @@

Initial value:
= R"(
{
"command":"exit",
"payload": {
"booga":"gooba"
}
}
)"
-

Definition at line 116 of file main.cpp.

+

Definition at line 118 of file main.cpp.

@@ -325,7 +325,7 @@

Initial value:
= R"(
{
"command":"fail",
"payload": {
"Does not":"really matter what is in here."
}
}
)"
-

Definition at line 79 of file main.cpp.

+

Definition at line 81 of file main.cpp.

@@ -341,7 +341,7 @@

-

Definition at line 33 of file main.cpp.

+

Definition at line 35 of file main.cpp.

@@ -357,7 +357,7 @@

Initial value:
= R"(
{
"command":"help",
"payload": {
"usage":"Enter json command in 'command':'<command>','payload': { // json payload of arguments }"
}
}
)"
-

Definition at line 89 of file main.cpp.

+

Definition at line 91 of file main.cpp.

@@ -373,7 +373,7 @@

Initial value:
= R"(
{
"command":"help",
"payload": {
"reason":"Not usage."
}
}
)"
-

Definition at line 98 of file main.cpp.

+

Definition at line 100 of file main.cpp.

@@ -389,7 +389,7 @@

Initial value:
= R"(
{
"command": "mean_ints",
"payload": {
"addends": [1, 2, 3, 4, 5]
}
}
)"
-

Definition at line 171 of file main.cpp.

+

Definition at line 173 of file main.cpp.

@@ -405,7 +405,7 @@

Initial value:
= R"(
{
"command": "mean_ints",
"payload": {
"addends": [1, 2, 2]
}
}
)"
-

Definition at line 180 of file main.cpp.

+

Definition at line 182 of file main.cpp.

@@ -421,7 +421,7 @@

Initial value:
= R"(
{
"command": "mean_ints",
"payload": {
"addends": [1, 2, 3, 4, 5, 100, 999999999, -1, 0, -999]
}
}
)"
-

Definition at line 189 of file main.cpp.

+

Definition at line 191 of file main.cpp.

@@ -437,7 +437,7 @@

Initial value:
= R"(
{
"command": "query_payload",
"payload": {
"hello": "world",
"t": true ,
"f": false,
"n": null,
"i": 123,
"pi": 3.1416,
"a": [1, 2, 3, 4]
}
}
)"
-

Definition at line 198 of file main.cpp.

+

Definition at line 200 of file main.cpp.

@@ -453,7 +453,7 @@

Initial value:
= R"(
{
"command": "sum_ints",
"payload": {
"addends": [1, 2, 3]
}
}
)"
-

Definition at line 125 of file main.cpp.

+

Definition at line 127 of file main.cpp.

@@ -469,7 +469,7 @@

Initial value:
= R"(
{
"command": "sum_ints",
"payload": {
"addends": ["apples", "oranges", "turtles"]
}
}
)"
-

Definition at line 152 of file main.cpp.

+

Definition at line 154 of file main.cpp.

@@ -485,7 +485,7 @@

Initial value:
= R"(
{
"command": "sum_ints",
"payload": {
"well":"formed",
"json":"test"
}
}
)"
-

Definition at line 161 of file main.cpp.

+

Definition at line 163 of file main.cpp.

@@ -501,7 +501,7 @@

Initial value:
= R"(
{
"command": "sum_ints",
"payload": {
"addends": [1, 2, 3, 4.5, 5.0, 100.1, 999999999.1, -1.1, 0, -999.1]
}
}
)"
-

Definition at line 143 of file main.cpp.

+

Definition at line 145 of file main.cpp.

@@ -517,7 +517,7 @@

Initial value:
= R"(
{
"command": "sum_ints",
"payload": {
"addends": [1, 2, 3, 4, 5, 100, 999999999, -1, 0, -999]
}
}
)"
-

Definition at line 134 of file main.cpp.

+

Definition at line 136 of file main.cpp.

@@ -535,7 +535,7 @@

Definition at line 37 of file main.cpp.

+

Definition at line 39 of file main.cpp.

@@ -553,7 +553,7 @@

Definition at line 38 of file main.cpp.

+

Definition at line 40 of file main.cpp.

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

Go to the source code of this file.

@@ -160,7 +162,7 @@

-

Definition at line 535 of file main_no_debug.cpp.

+

Definition at line 537 of file main_no_debug.cpp.

@@ -190,7 +192,7 @@

Warning
NOT thread-safe; must be called within a thread-safe scope
-

Definition at line 50 of file main_no_debug.cpp.

+

Definition at line 52 of file main_no_debug.cpp.

@@ -208,13 +210,11 @@

-
Note
needs static functions
-
Todo:
maybe use std::bind instead to enable using non-static functions.
Note
exit_command commented out to allow fallthrough to user prompt. If uncommented, exit_command will successfully exit program prior to user prompt. Ok to uncomment if desired.
The "//
-

Definition at line 716 of file main_no_debug.cpp.

+

Definition at line 718 of file main_no_debug.cpp.

@@ -231,7 +231,7 @@

Initial value:
= R"(
{
"command":"exit",
"payload": {
"reason":"Exiting program on user request."
}
}
)"
-

Definition at line 89 of file main_no_debug.cpp.

+

Definition at line 91 of file main_no_debug.cpp.

@@ -247,7 +247,7 @@

Initial value:
= R"(
{
"command":"exit",
"payload": {
"booga":"gooba"
}
}
)"
-

Definition at line 98 of file main_no_debug.cpp.

+

Definition at line 100 of file main_no_debug.cpp.

@@ -263,7 +263,7 @@

Initial value:
= R"(
{
"command":"fail",
"payload": {
"Does not":"really matter what is in here."
}
}
)"
-

Definition at line 61 of file main_no_debug.cpp.

+

Definition at line 63 of file main_no_debug.cpp.

@@ -279,7 +279,7 @@

-

Definition at line 33 of file main_no_debug.cpp.

+

Definition at line 35 of file main_no_debug.cpp.

@@ -295,7 +295,7 @@

Initial value:
= R"(
{
"command":"help",
"payload": {
"usage":"Enter json command in 'command':'<command>','payload': { // json payload of arguments }"
}
}
)"
-

Definition at line 71 of file main_no_debug.cpp.

+

Definition at line 73 of file main_no_debug.cpp.

@@ -311,7 +311,7 @@

Initial value:
= R"(
{
"command":"help",
"payload": {
"reason":"Not usage."
}
}
)"
-

Definition at line 80 of file main_no_debug.cpp.

+

Definition at line 82 of file main_no_debug.cpp.

@@ -327,7 +327,7 @@

Initial value:
= R"(
{
"command": "mean_ints",
"payload": {
"addends": [1, 2, 3, 4, 5]
}
}
)"
-

Definition at line 153 of file main_no_debug.cpp.

+

Definition at line 155 of file main_no_debug.cpp.

@@ -343,7 +343,7 @@

Initial value:
= R"(
{
"command": "mean_ints",
"payload": {
"addends": [1, 2, 2]
}
}
)"
-

Definition at line 162 of file main_no_debug.cpp.

+

Definition at line 164 of file main_no_debug.cpp.

@@ -359,7 +359,7 @@

Initial value:
= R"(
{
"command": "mean_ints",
"payload": {
"addends": [1, 2, 3, 4, 5, 100, 999999999, -1, 0, -999]
}
}
)"
-

Definition at line 171 of file main_no_debug.cpp.

+

Definition at line 173 of file main_no_debug.cpp.

@@ -375,7 +375,7 @@

Initial value:
= R"(
{
"command": "query_payload",
"payload": {
"well": "formed",
"json": "test",
"a": [1,2,3,4],
"number1": 1,
"number2": 34.7,
"b": [
"something":"more",
"here":"too"
]
}
}
)"
-

Definition at line 182 of file main_no_debug.cpp.

+

Definition at line 184 of file main_no_debug.cpp.

@@ -391,7 +391,7 @@

Initial value:
= R"(
{
"command": "query_payload",
"payload": {
"hello": "world",
"t": true ,
"f": false,
"n": null,
"i": 123,
"pi": 3.1416,
"a": [1, 2, 3, 4]
}
}
)"
-

Definition at line 199 of file main_no_debug.cpp.

+

Definition at line 201 of file main_no_debug.cpp.

@@ -407,7 +407,7 @@

Initial value:
= R"(
{
"command": "sum_ints",
"payload": {
"addends": [1, 2, 3]
}
}
)"
-

Definition at line 107 of file main_no_debug.cpp.

+

Definition at line 109 of file main_no_debug.cpp.

@@ -423,7 +423,7 @@

Initial value:
= R"(
{
"command": "sum_ints",
"payload": {
"addends": ["apples", "oranges", "turtles"]
}
}
)"
-

Definition at line 134 of file main_no_debug.cpp.

+

Definition at line 136 of file main_no_debug.cpp.

@@ -439,7 +439,7 @@

Initial value:
= R"(
{
"command": "sum_ints",
"payload": {
"well":"formed",
"json":"test"
}
}
)"
-

Definition at line 143 of file main_no_debug.cpp.

+

Definition at line 145 of file main_no_debug.cpp.

@@ -455,7 +455,7 @@

Initial value:
= R"(
{
"command": "sum_ints",
"payload": {
"addends": [1, 2, 3, 4.5, 5.0, 100.1, 999999999.1, -1.1, 0, -999.1]
}
}
)"
-

Definition at line 125 of file main_no_debug.cpp.

+

Definition at line 127 of file main_no_debug.cpp.

@@ -471,7 +471,7 @@

Initial value:
= R"(
{
"command": "sum_ints",
"payload": {
"addends": [1, 2, 3, 4, 5, 100, 999999999, -1, 0, -999]
}
}
)"
-

Definition at line 116 of file main_no_debug.cpp.

+

Definition at line 118 of file main_no_debug.cpp.

@@ -489,7 +489,7 @@

Definition at line 37 of file main_no_debug.cpp.

+

Definition at line 39 of file main_no_debug.cpp.

@@ -507,7 +507,7 @@

Definition at line 38 of file main_no_debug.cpp.

+

Definition at line 40 of file main_no_debug.cpp.

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