Map and global variable erased on constructor creation
Posted: Fri Mar 27, 2020 9:53 am
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
And
app_command.cpp
usage_example_file.cpp
Some help would be more than appreciated.
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
- #ifndef COMMAND_H
- #define COMMAND_H
- #include <string>
- #include <map>
- #include "app.h"
- // Other libs... (removed for clarity)
- class AppCommand;
- class AppCommandMap;
- struct CompareCharPtr
- {
- bool operator()(std::string a, std::string b);
- };
- // Here is the map I want to work with. This is supposed to store a list of issuable commands.
- typedef std::map<std::string, AppCommand *, CompareCharPtr> commandmap_t;
- // Define a map class for commands. I guess I could place this directly into AppCommand
- class AppCommandMap
- {
- public:
- AppCommand *FindUniquePrefix(std::string key);
- AppCommand *FindCommandOnMap(std::string key);
- public:
- commandmap_t m_map;
- };
- class AppCommand : public ExternalRamAllocated
- {
- public:
- void InitCommandMap();
- public:
- AppCommand();
- AppCommand(const char *name, const char *title,
- void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *),
- const char *usage, int min, int max, bool secure,
- int (*validate)(AppWriter *, AppCommand *, int, const char *const *, bool));
- virtual ~AppCommand();
- public:
- AppCommand *RegisterCommand(const char *name, const char *title,
- void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *) = NULL,
- const char *usage = "", int min = 0, int max = 0, bool secure = true,
- int (*validate)(AppWriter *, AppCommand *, int, const char *const *, bool) = NULL);
- bool UnregisterCommand(const char *name = NULL);
- AppCommand *FindCommand(const char *name);
- bool IsSecure() { return m_secure; }
- // this should be set to protected:
- public:
- const char *m_name;
- const char *m_title;
- void (*m_execute)(int, AppWriter *, AppCommand *, int, const char *const *);
- int (*m_validate)(AppWriter *, AppCommand *, int, const char *const *, bool);
- const char *m_usage_template;
- int m_min;
- int m_max;
- bool m_secure;
- AppCommand *m_parent;
- AppCommandMap m_children; // This is the member of the command map
- };
- class AppCommandApp : protected AppCommand
- {
- public:
- AppCommandApp();
- virtual ~AppCommandApp();
- public:
- void InitCommandMap();
- public:
- AppCommand *RegisterCommand(const char *name, const char *title,
- void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *) = NULL,
- const char *usage = "", int min = 0, int max = 0, bool secure = true);
- bool UnregisterCommand(const char *name);
- AppCommand *FindCommand(const char *name);
- private:
- AppCommand m_root;
- };
- extern AppCommandApp MyCommandApp;
- #endif //#ifndef _COMMAND_H_
app_command.cpp
- static const char *TAG = "command";
- #include "app_command.h"
- #include "freertos/FreeRTOS.h"
- // Other libs...
- // MyCommandApp is my global variable, that should store the map and other data
- // and make it accessible throughout the application.
- AppCommandApp MyCommandApp __attribute__((init_priority(1000)));
- bool CompareCharPtr::operator()(std::string a, std::string b)
- {
- return a.compare(b) < 0;
- }
- bool firstLoad = true;
- // InitCommandMap empties the map the first time. It should be executed a single time.
- // It's very kludgy, maybe it should be done differently.
- // If the map is not emptied, I get errors when I'm trying to insert values inside of it
- void AppCommand::InitCommandMap()
- {
- if (firstLoad && m_children.m_map.empty())
- {
- m_children.m_map.clear();
- firstLoad = false;
- ESP_LOGI(TAG, "m_map cleared");
- return;
- }
- }
- AppCommand *AppCommandMap::FindUniquePrefix(std::string key)
- {
- AppCommand *found = NULL;
- for (commandmap_t::iterator it = m_map.begin(); it != m_map.end(); ++it)
- {
- if (key.compare(it->first) == 0)
- {
- if (it->first.length())
- {
- return it->second;
- }
- if (found)
- {
- return NULL;
- }
- else
- {
- found = it->second;
- }
- }
- }
- return found;
- }
- AppCommand *AppCommandMap::FindCommandOnMap(std::string key)
- {
- commandmap_t::iterator it = m_map.find(key);
- if (it == m_map.end())
- {
- return NULL;
- }
- else
- return it->second;
- }
- AppCommand::AppCommand()
- {
- m_parent = NULL;
- }
- AppCommand::AppCommand(const char *name, const char *title, void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *),
- const char *usage, int min, int max, bool secure,
- int (*validate)(AppWriter *, AppCommand *, int, const char *const *, bool))
- {
- m_name = name;
- m_title = title;
- m_execute = execute;
- m_usage_template = usage;
- m_min = min;
- m_max = max;
- m_parent = NULL;
- m_secure = secure;
- m_validate = validate;
- }
- AppCommand::~AppCommand()
- {
- for (auto it = m_children.m_map.begin(); it != m_children.m_map.end(); ++it)
- {
- AppCommand *cmd = it->second;
- delete cmd;
- }
- m_children.m_map.clear();
- }
- AppCommand *AppCommand::RegisterCommand(const char *name, const char *title, void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *),
- const char *usage, int min, int max, bool secure,
- int (*validate)(AppWriter *, AppCommand *, int, const char *const *, bool))
- {
- std::string name_s(name);
- // Protect against duplicate registrations
- AppCommand *cmd = FindCommand(name);
- if (cmd == NULL)
- {
- cmd = new AppCommand(name, title, execute, usage, min, max, secure, validate);
- m_children.m_map.insert(commandmap_t::value_type(name, cmd));
- // m_children.m_map.insert does not work if the map is not initialized as empty.
- // This problem didn't seem to occur with esp-idf v3.
- cmd->m_parent = this;
- }
- return cmd;
- }
- bool AppCommand::UnregisterCommand(const char *name)
- {
- if (name == NULL)
- {
- // Unregister this command
- return m_parent->UnregisterCommand(m_name);
- }
- // Unregister the specified child command
- auto pos = m_children.m_map.find(name);
- if (pos == m_children.m_map.end())
- {
- return false;
- }
- else
- {
- AppCommand *cmd = pos->second;
- m_children.m_map.erase(pos);
- delete cmd;
- return true;
- }
- }
- AppCommand *AppCommand::FindCommand(const char *name)
- {
- return m_children.FindCommandOnMap(name);
- }
- AppCommandApp::AppCommandApp()
- {
- ESP_LOGI(TAG, "Initialising COMMAND (1000)");
- // What is happening here? m_root.m_children.m_map is empty although in the console,
- // I could see that there was some data into the map before reading "Initialising COMMAND (1000)"
- //
- // FIXME: When this constructor gets called, the entire content of m_root is gone!
- // I suspect that the entire MyCommandApp variable is therefore empty.
- //
- m_root.RegisterCommand("help", "Ask for help", help, "", 0, 0, false);
- m_root.RegisterCommand("exit", "End console session", Exit, "", 0, 0, false);
- AppCommand *cmd_log = MyCommandApp.RegisterCommand("log", "LOG framework");
- cmd_log->RegisterCommand("file", "Start logging to specified file", log_file, "<vfspath>", 0, 1);
- // The 4 commands above do get registered. But all of the previous ones, are gone (see the final example below).
- }
- AppCommandApp::~AppCommandApp()
- {
- }
- void AppCommandApp::InitCommandMap()
- {
- return m_root.InitCommandMap();
- }
- AppCommand *AppCommandApp::RegisterCommand(const char *name, const char *title, void (*execute)(int, AppWriter *, AppCommand *, int, const char *const *),
- const char *usage, int min, int max, bool secure)
- {
- InitCommandMap(); // Still think this shouldn't be done this way
- AppCommand *ret = m_root.RegisterCommand(name, title, execute, usage, min, max, secure);
- // The following horrible lines were added to verify the presence of commands.
- // I want to make sure that the data is inside of the map as it should be. And it is.
- // But it's incomplete right after AppCommandApp initializes.
- printf("\n---S---\n");
- int i = 0;
- for (const auto &x : m_root.Map())
- {
- 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);
- i++;
- }
- printf("\n---E---\n");
- return ret;
- }
- AppCommand *AppCommandApp::FindCommand(const char *name)
- {
- return m_root.FindCommand(name);
- }
- char **AppCommandApp::Complete(AppWriter *writer, int argc, const char *const *argv)
- {
- return m_root.Complete(writer, argc, argv);
- }
- // Register some commands
- SenseCommand *cmd_notify = MyCommandApp.RegisterCommand("notify", "NOTIFICATION framework");
- cmd_notify->RegisterCommand("status", "Show notification status", notify_status);
- SenseCommand *cmd_notifyraise = cmd_notify->RegisterCommand("raise", "NOTIFICATION raise framework");
- cmd_notifyraise->RegisterCommand("text", "Raise a textual notification", notify_raise, "<type><subtype><message>", 3, 3);
- // Those commands are there until AppCommandApp inits. They disappear afterward
Some help would be more than appreciated.