Page 1 of 1

Map and global variable erased on constructor creation

Posted: Fri Mar 27, 2020 9:53 am
by leofabri
I'm working on a project that wasn't initially developed by me. I'm trying to make it work with esp-idf v4 (instead of v3.0).

Every time the constructor of AppCommandApp() is initialized, the entire content of the global MyCommandApp seems to be getting erased and the map of commands is incomplete.

This is the code (I tried to shorten it, sorry if it's still so long):

app_command.h
  1. #ifndef COMMAND_H
  2. #define COMMAND_H
  3.  
  4. #include <string>
  5. #include <map>
  6. #include "app.h"
  7. // Other libs... (removed for clarity)
  8.  
  9. class AppCommand;
  10. class AppCommandMap;
  11.  
  12. struct CompareCharPtr
  13. {
  14.   bool operator()(std::string a, std::string b);
  15. };
  16.  
  17. // Here is the map I want to work with. This is supposed to store a list of issuable commands.
  18. typedef std::map<std::string, AppCommand *, CompareCharPtr> commandmap_t;
  19.  
  20. // Define a map class for commands. I guess I could place this directly into AppCommand
  21. class AppCommandMap
  22. {
  23. public:
  24.   AppCommand *FindUniquePrefix(std::string key);
  25.   AppCommand *FindCommandOnMap(std::string key);
  26.  
  27. public:
  28.   commandmap_t m_map;
  29. };
  30.  
  31. class AppCommand : public ExternalRamAllocated
  32. {
  33. public:
  34.   void InitCommandMap();
  35.  
  36. public:
  37.   AppCommand();
  38.   AppCommand(const char *name, const char *title,
  39.              void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *),
  40.              const char *usage, int min, int max, bool secure,
  41.              int (*validate)(AppWriter *, AppCommand *, int, const char *const *, bool));
  42.   virtual ~AppCommand();
  43.  
  44. public:
  45.   AppCommand *RegisterCommand(const char *name, const char *title,
  46.                               void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *) = NULL,
  47.                               const char *usage = "", int min = 0, int max = 0, bool secure = true,
  48.                               int (*validate)(AppWriter *, AppCommand *, int, const char *const *, bool) = NULL);
  49.   bool UnregisterCommand(const char *name = NULL);
  50.  
  51.   AppCommand *FindCommand(const char *name);
  52.   bool IsSecure() { return m_secure; }
  53.  
  54.   // this should be set to protected:
  55. public:
  56.   const char *m_name;
  57.   const char *m_title;
  58.   void (*m_execute)(int, AppWriter *, AppCommand *, int, const char *const *);
  59.   int (*m_validate)(AppWriter *, AppCommand *, int, const char *const *, bool);
  60.   const char *m_usage_template;
  61.   int m_min;
  62.   int m_max;
  63.   bool m_secure;
  64.   AppCommand *m_parent;
  65.   AppCommandMap m_children; // This is the member of the command map
  66. };
  67.  
  68. class AppCommandApp : protected AppCommand
  69. {
  70. public:
  71.   AppCommandApp();
  72.   virtual ~AppCommandApp();
  73.  
  74. public:
  75.   void InitCommandMap();
  76.  
  77. public:
  78.   AppCommand *RegisterCommand(const char *name, const char *title,
  79.                               void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *) = NULL,
  80.                               const char *usage = "", int min = 0, int max = 0, bool secure = true);
  81.   bool UnregisterCommand(const char *name);
  82.   AppCommand *FindCommand(const char *name);
  83.  
  84. private:
  85.   AppCommand m_root;
  86. };
  87.  
  88. extern AppCommandApp MyCommandApp;
  89.  
  90. #endif //#ifndef _COMMAND_H_
And

app_command.cpp
  1. static const char *TAG = "command";
  2.  
  3. #include "app_command.h"
  4. #include "freertos/FreeRTOS.h"
  5. // Other libs...
  6.  
  7. // MyCommandApp is my global variable, that should store the map and other data
  8. // and make it accessible throughout the application.
  9. AppCommandApp MyCommandApp __attribute__((init_priority(1000)));
  10.  
  11. bool CompareCharPtr::operator()(std::string a, std::string b)
  12. {
  13.   return a.compare(b) < 0;
  14. }
  15.  
  16. bool firstLoad = true;
  17.  
  18.  
  19. // InitCommandMap empties the map the first time. It should be executed a single time.
  20. // It's very kludgy, maybe it should be done differently.
  21. // If the map is not emptied, I get errors when I'm trying to insert values inside of it
  22. void AppCommand::InitCommandMap()
  23. {
  24.   if (firstLoad && m_children.m_map.empty())
  25.   {
  26.     m_children.m_map.clear();
  27.     firstLoad = false;
  28.     ESP_LOGI(TAG, "m_map cleared");
  29.     return;
  30.   }
  31. }
  32.  
  33. AppCommand *AppCommandMap::FindUniquePrefix(std::string key)
  34. {
  35.   AppCommand *found = NULL;
  36.   for (commandmap_t::iterator it = m_map.begin(); it != m_map.end(); ++it)
  37.   {
  38.     if (key.compare(it->first) == 0)
  39.     {
  40.       if (it->first.length())
  41.       {
  42.         return it->second;
  43.       }
  44.       if (found)
  45.       {
  46.         return NULL;
  47.       }
  48.       else
  49.       {
  50.         found = it->second;
  51.       }
  52.     }
  53.   }
  54.   return found;
  55. }
  56.  
  57. AppCommand *AppCommandMap::FindCommandOnMap(std::string key)
  58. {
  59.   commandmap_t::iterator it = m_map.find(key);
  60.   if (it == m_map.end())
  61.   {
  62.     return NULL;
  63.   }
  64.   else
  65.     return it->second;
  66. }
  67.  
  68. AppCommand::AppCommand()
  69. {
  70.   m_parent = NULL;
  71. }
  72.  
  73. AppCommand::AppCommand(const char *name, const char *title, void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *),
  74.                        const char *usage, int min, int max, bool secure,
  75.                        int (*validate)(AppWriter *, AppCommand *, int, const char *const *, bool))
  76. {
  77.   m_name = name;
  78.   m_title = title;
  79.   m_execute = execute;
  80.   m_usage_template = usage;
  81.   m_min = min;
  82.   m_max = max;
  83.   m_parent = NULL;
  84.   m_secure = secure;
  85.   m_validate = validate;
  86. }
  87.  
  88. AppCommand::~AppCommand()
  89. {
  90.   for (auto it = m_children.m_map.begin(); it != m_children.m_map.end(); ++it)
  91.   {
  92.     AppCommand *cmd = it->second;
  93.     delete cmd;
  94.   }
  95.   m_children.m_map.clear();
  96. }
  97.  
  98. AppCommand *AppCommand::RegisterCommand(const char *name, const char *title, void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *),
  99.                                         const char *usage, int min, int max, bool secure,
  100.                                         int (*validate)(AppWriter *, AppCommand *, int, const char *const *, bool))
  101. {
  102.   std::string name_s(name);
  103.   // Protect against duplicate registrations
  104.   AppCommand *cmd = FindCommand(name);
  105.   if (cmd == NULL)
  106.   {
  107.     cmd = new AppCommand(name, title, execute, usage, min, max, secure, validate);
  108.     m_children.m_map.insert(commandmap_t::value_type(name, cmd));
  109.     // m_children.m_map.insert does not work if the map is not initialized as empty.
  110.     // This problem didn't seem to occur with esp-idf v3.
  111.  
  112.     cmd->m_parent = this;
  113.   }
  114.   return cmd;
  115. }
  116.  
  117. bool AppCommand::UnregisterCommand(const char *name)
  118. {
  119.   if (name == NULL)
  120.   {
  121.     // Unregister this command
  122.     return m_parent->UnregisterCommand(m_name);
  123.   }
  124.  
  125.   // Unregister the specified child command
  126.   auto pos = m_children.m_map.find(name);
  127.   if (pos == m_children.m_map.end())
  128.   {
  129.     return false;
  130.   }
  131.   else
  132.   {
  133.     AppCommand *cmd = pos->second;
  134.     m_children.m_map.erase(pos);
  135.     delete cmd;
  136.     return true;
  137.   }
  138. }
  139.  
  140. AppCommand *AppCommand::FindCommand(const char *name)
  141. {
  142.   return m_children.FindCommandOnMap(name);
  143. }
  144.  
  145. AppCommandApp::AppCommandApp()
  146. {
  147.   ESP_LOGI(TAG, "Initialising COMMAND (1000)");
  148.  
  149.   // What is happening here? m_root.m_children.m_map is empty although in the console,
  150.   // I could see that there was some data into the map before reading "Initialising COMMAND (1000)"
  151.  
  152.   //
  153.   // FIXME: When this constructor gets called, the entire content of m_root is gone!
  154.   // I suspect that the entire MyCommandApp variable is therefore empty.
  155.   //
  156.  
  157.   m_root.RegisterCommand("help", "Ask for help", help, "", 0, 0, false);
  158.   m_root.RegisterCommand("exit", "End console session", Exit, "", 0, 0, false);
  159.   AppCommand *cmd_log = MyCommandApp.RegisterCommand("log", "LOG framework");
  160.   cmd_log->RegisterCommand("file", "Start logging to specified file", log_file, "<vfspath>", 0, 1);
  161.   // The 4 commands above do get registered. But all of the previous ones, are gone (see the final example below).
  162.  
  163. }
  164.  
  165. AppCommandApp::~AppCommandApp()
  166. {
  167. }
  168.  
  169. void AppCommandApp::InitCommandMap()
  170. {
  171.   return m_root.InitCommandMap();
  172. }
  173.  
  174. AppCommand *AppCommandApp::RegisterCommand(const char *name, const char *title, void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *),
  175.                                            const char *usage, int min, int max, bool secure)
  176. {
  177.   InitCommandMap(); // Still think this shouldn't be done this way
  178.   AppCommand *ret = m_root.RegisterCommand(name, title, execute, usage, min, max, secure);
  179.  
  180.  
  181.   // The following horrible lines were added to verify the presence of commands.
  182.   // I want to make sure that the data is inside of the map as it should be. And it is.
  183.   // But it's incomplete right after AppCommandApp initializes.
  184.  
  185.   printf("\n---S---\n");
  186.   int i = 0;
  187.   for (const auto &x : m_root.Map())
  188.   {
  189.     ESP_LOGD(TAG, "%d key is: %s \n- title: %s \n- name: %s\n", i, x.first.c_str(), x.second->m_title, x.second->m_name);
  190.     i++;
  191.   }
  192.   printf("\n---E---\n");
  193.  
  194.   return ret;
  195. }
  196.  
  197. AppCommand *AppCommandApp::FindCommand(const char *name)
  198. {
  199.   return m_root.FindCommand(name);
  200. }
  201.  
  202. char **AppCommandApp::Complete(AppWriter *writer, int argc, const char *const *argv)
  203. {
  204.   return m_root.Complete(writer, argc, argv);
  205. }
usage_example_file.cpp
  1.   // Register some commands
  2.   SenseCommand *cmd_notify = MyCommandApp.RegisterCommand("notify", "NOTIFICATION framework");
  3.   cmd_notify->RegisterCommand("status", "Show notification status", notify_status);
  4.   SenseCommand *cmd_notifyraise = cmd_notify->RegisterCommand("raise", "NOTIFICATION raise framework");
  5.   cmd_notifyraise->RegisterCommand("text", "Raise a textual notification", notify_raise, "<type><subtype><message>", 3, 3);
  6.  
  7.   // Those commands are there until AppCommandApp inits. They disappear afterward

Some help would be more than appreciated.