From fc9fb7937c3dee96dddcdbab5c6e858c8bf8ad65 Mon Sep 17 00:00:00 2001 From: "Peter D. Barnes, Jr" Date: Mon, 24 Oct 2022 23:12:38 -0700 Subject: [PATCH] core: command-line: refactor and modernize --- src/core/model/command-line.cc | 167 ++++++++++++++------------------- src/core/model/command-line.h | 32 ++++--- 2 files changed, 92 insertions(+), 107 deletions(-) diff --git a/src/core/model/command-line.cc b/src/core/model/command-line.cc index cc77b4072..2a7d5d288 100644 --- a/src/core/model/command-line.cc +++ b/src/core/model/command-line.cc @@ -165,14 +165,6 @@ CommandLine::Clear() { NS_LOG_FUNCTION(this); - for (auto i : m_options) - { - delete i; - } - for (auto i : m_nonOptions) - { - delete i; - } m_options.clear(); m_nonOptions.clear(); m_NNonOptions = 0; @@ -284,7 +276,7 @@ CommandLine::HandleNonOption(const std::string& value) // Add an unspecified non-option as a string NS_LOG_LOGIC("adding StringItem, NOCount:" << m_nonOptionCount << ", NOSize:" << m_nonOptions.size()); - StringItem* item = new StringItem; + auto item = std::make_shared(); item->m_name = "extra-non-option-argument"; item->m_help = "Extra non-option argument encountered."; item->m_value = value; @@ -321,56 +313,43 @@ CommandLine::PrintHelp(std::ostream& os) const os << m_shortName << (m_options.size() ? " [Program Options]" : "") << (nonOptions.size() ? " [Program Arguments]" : "") << " [General Arguments]" << std::endl; - if (m_usage.length()) + if (!m_usage.empty()) { os << std::endl; os << m_usage << std::endl; } std::size_t width = 0; - for (auto it : m_options) - { - width = std::max(width, it->m_name.size()); - } - for (auto it : nonOptions) - { - width = std::max(width, it->m_name.size()); - } + auto max_width = [&width](const std::shared_ptr item) { + width = std::max(width, item->m_name.size()); + }; + std::for_each(m_options.begin(), m_options.end(), max_width); + std::for_each(nonOptions.begin(), nonOptions.end(), max_width); width += 3; // room for ": " between option and help + auto optionsHelp = [&os, width](const std::string& head, bool option, const Items& items) { + os << "\n" << head << "\n"; + for (const auto& item : items) + { + os << " " << (option ? "--" : " ") << std::left << std::setw(width) + << (item->m_name + ":") << std::right << item->m_help; + + if (item->HasDefault()) + { + os << " [" << item->GetDefault() << "]"; + } + os << "\n"; + } + }; + if (!m_options.empty()) { - os << std::endl; - os << "Program Options:" << std::endl; - for (auto i : m_options) - { - os << " --" << std::left << std::setw(width) << (i->m_name + ":") << std::right - << i->m_help; - - if (i->HasDefault()) - { - os << " [" << i->GetDefault() << "]"; - } - os << std::endl; - } + optionsHelp("Program Options:", true, m_options); } if (!nonOptions.empty()) { - width += 2; // account for "--" added above - os << std::endl; - os << "Program Arguments:" << std::endl; - for (auto i : nonOptions) - { - os << " " << std::left << std::setw(width) << (i->m_name + ":") << std::right - << i->m_help; - - if (i->HasDefault()) - { - os << " [" << i->GetDefault() << "]"; - } - os << std::endl; - } + optionsHelp("Program Arguments:", false, nonOptions); } os << std::endl; @@ -433,14 +412,14 @@ CommandLine::PrintDoxygenUsage() const << "$ ./ns3 run \"" << m_shortName << (m_options.size() ? " [Program Options]" : "") << (nonOptions.size() ? " [Program Arguments]" : "") << "\"\n"; - if (m_usage.length()) + if (!m_usage.empty()) { os << Encode(m_usage) << "\n"; } - auto listOptions = [&os](Items items, std::string pre) { - os << "
\n"; - for (const auto i : items) + auto listOptions = [&os](const std::string& head, const Items& items, std::string pre) { + os << "\n
\n

" << head << "

\n"; + for (const auto& i : items) { os << "
" << pre << i->m_name << "
\n" << "
" << Encode(i->m_help); @@ -456,16 +435,12 @@ CommandLine::PrintDoxygenUsage() const if (!m_options.empty()) { - os << std::endl; - os << "

Program Options

\n"; - listOptions(m_options, "\\c --"); + listOptions("Program Options", m_options, "\\c --"); } if (!nonOptions.empty()) { - os << std::endl; - os << "

Program Arguments

\n"; - listOptions(nonOptions, "\\c "); + listOptions("Program Arguments", nonOptions, "\\c "); } os << "*/" << std::endl; @@ -632,7 +607,7 @@ CommandLine::PrintGroups(std::ostream& os) const } } -void +bool CommandLine::HandleArgument(const std::string& name, const std::string& value) const { NS_LOG_FUNCTION(this << name << value); @@ -682,33 +657,36 @@ CommandLine::HandleArgument(const std::string& name, const std::string& value) c PrintAttributes(std::cout, value); std::exit(0); } - else - { - for (auto i : m_options) + + auto errorExit = [this, name, value]() { + std::cerr << "Invalid command-line argument: --" << name; + if (value != "") { - if (i->m_name == name) - { - if (!i->Parse(value)) - { - std::cerr << "Invalid argument value: " << name << "=" << value << std::endl; - PrintHelp(std::cerr); - std::exit(1); - } - else - { - return; - } - } + std::cerr << "=" << value; } - } - // Global or ConfigPath options - if (!Config::SetGlobalFailSafe(name, StringValue(value)) && - !Config::SetDefaultFailSafe(name, StringValue(value))) - { - std::cerr << "Invalid command-line arguments: --" << name << "=" << value << std::endl; - PrintHelp(std::cerr); + std::cerr << std::endl; + this->PrintHelp(std::cerr); std::exit(1); + }; + + auto item = std::find_if(m_options.begin(), m_options.end(), [name](std::shared_ptr it) { + return it->m_name == name; + }); + if (item != m_options.end()) + { + if (!(*item)->Parse(value)) + { + errorExit(); + } + return true; } + + // Global or ConfigPath options + if (!HandleAttribute(name, value)) + { + errorExit(); + } + return true; } bool @@ -739,7 +717,7 @@ CommandLine::AddValue(const std::string& name, { NS_LOG_FUNCTION(this << &name << &help << &callback); - CallbackItem* item = new CallbackItem(); + auto item = std::make_shared(); item->m_name = name; item->m_help = help; item->m_callback = callback; @@ -783,7 +761,7 @@ CommandLine::GetExtraNonOption(std::size_t i) const if (m_nonOptions.size() >= i + m_NNonOptions) { - auto ip = dynamic_cast(m_nonOptions[i + m_NNonOptions]); + auto ip = std::dynamic_pointer_cast(m_nonOptions[i + m_NNonOptions]); if (ip != nullptr) { value = ip->m_value; @@ -809,13 +787,8 @@ CommandLine::GetNExtraNonOptions() const bool CommandLine::HandleAttribute(const std::string& name, const std::string& value) { - bool success = true; - if (!Config::SetGlobalFailSafe(name, StringValue(value)) && - !Config::SetDefaultFailSafe(name, StringValue(value))) - { - success = false; - } - return success; + return Config::SetGlobalFailSafe(name, StringValue(value)) || + Config::SetDefaultFailSafe(name, StringValue(value)); } bool @@ -856,21 +829,23 @@ template <> bool CommandLineHelper::UserItemParse(const std::string& value, bool& val) { - std::string src = value; - std::transform(src.begin(), src.end(), src.begin(), [](char c) { - return static_cast(std::tolower(c)); - }); - if (src.length() == 0) + // No new value, so just toggle it + if (value.empty()) { val = !val; return true; } - else if ((src == "true") || (src == "t")) + + std::string src = value; + std::transform(src.begin(), src.end(), src.begin(), [](char c) { + return static_cast(std::tolower(c)); + }); + if (src == "true" || src == "t") { val = true; return true; } - else if ((src == "false") || (src == "f")) + else if (src == "false" || src == "f") { val = false; return true; @@ -898,7 +873,7 @@ bool CommandLineHelper::UserItemParse(const std::string& value, uint8_t& val) { uint8_t oldVal = val; - long newVal; + int newVal; try { diff --git a/src/core/model/command-line.h b/src/core/model/command-line.h index f387cad8f..88418c3d9 100644 --- a/src/core/model/command-line.h +++ b/src/core/model/command-line.h @@ -23,6 +23,7 @@ #include "nstime.h" #include "type-id.h" +#include // shared_ptr #include #include #include @@ -517,8 +518,9 @@ class CommandLine * * \param [in] name The argument name * \param [in] value The command line value + * \returns \c true if the argument was handled successfully */ - void HandleArgument(const std::string& name, const std::string& value) const; + bool HandleArgument(const std::string& name, const std::string& value) const; /** * Callback function to handle attributes. * @@ -583,14 +585,22 @@ class CommandLine */ void PrintDoxygenUsage() const; - typedef std::vector Items; /**< Argument list container */ - Items m_options; /**< The list of option arguments */ - Items m_nonOptions; /**< The list of non-option arguments */ - std::size_t m_NNonOptions; /**< The expected number of non-option arguments */ - std::size_t m_nonOptionCount; /**< The number of actual non-option arguments seen so far. */ - std::string m_usage; /**< The Usage string */ - std::string - m_shortName; /**< The source file name (without `.cc`), as would be given to `ns3 run` */ + /** Argument list container */ + using Items = std::vector>; + + /** The list of option arguments */ + Items m_options; + /** The list of non-option arguments */ + Items m_nonOptions; + + /** The expected number of non-option arguments */ + std::size_t m_NNonOptions; + /** The number of actual non-option arguments seen so far. */ + std::size_t m_nonOptionCount; + /** The Usage string */ + std::string m_usage; + /** The source file name (without `.cc`), as would be given to `ns3 run` */ + std::string m_shortName; }; // class CommandLine @@ -667,7 +677,7 @@ template void CommandLine::AddValue(const std::string& name, const std::string& help, T& value) { - UserItem* item = new UserItem(); + auto item = std::make_shared>(); item->m_name = name; item->m_help = help; item->m_valuePtr = &value; @@ -683,7 +693,7 @@ template void CommandLine::AddNonOption(const std::string& name, const std::string& help, T& value) { - UserItem* item = new UserItem(); + auto item = std::make_shared>(); item->m_name = name; item->m_help = help; item->m_valuePtr = &value;