core: command-line: refactor and modernize

This commit is contained in:
Peter D. Barnes, Jr
2022-10-24 23:12:38 -07:00
committed by Gabriel Ferreira
parent 95428c2882
commit fc9fb7937c
2 changed files with 92 additions and 107 deletions

View File

@@ -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<StringItem>();
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> 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
<< "<code>$ ./ns3 run \"" << m_shortName << (m_options.size() ? " [Program Options]" : "")
<< (nonOptions.size() ? " [Program Arguments]" : "") << "\"</code>\n";
if (m_usage.length())
if (!m_usage.empty())
{
os << Encode(m_usage) << "\n";
}
auto listOptions = [&os](Items items, std::string pre) {
os << "<dl>\n";
for (const auto i : items)
auto listOptions = [&os](const std::string& head, const Items& items, std::string pre) {
os << "\n<dl>\n<h3>" << head << "</h3>\n";
for (const auto& i : items)
{
os << " <dt>" << pre << i->m_name << " </dt>\n"
<< " <dd>" << Encode(i->m_help);
@@ -456,16 +435,12 @@ CommandLine::PrintDoxygenUsage() const
if (!m_options.empty())
{
os << std::endl;
os << "<h3>Program Options</h3>\n";
listOptions(m_options, "\\c --");
listOptions("Program Options", m_options, "\\c --");
}
if (!nonOptions.empty())
{
os << std::endl;
os << "<h3>Program Arguments</h3>\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<Item> 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<CallbackItem>();
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<StringItem*>(m_nonOptions[i + m_NNonOptions]);
auto ip = std::dynamic_pointer_cast<StringItem>(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<bool>(const std::string& value, bool& val)
{
std::string src = value;
std::transform(src.begin(), src.end(), src.begin(), [](char c) {
return static_cast<char>(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<char>(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<uint8_t>(const std::string& value, uint8_t& val)
{
uint8_t oldVal = val;
long newVal;
int newVal;
try
{

View File

@@ -23,6 +23,7 @@
#include "nstime.h"
#include "type-id.h"
#include <memory> // shared_ptr
#include <sstream>
#include <string>
#include <vector>
@@ -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<Item*> 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<std::shared_ptr<Item>>;
/** 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 <typename T>
void
CommandLine::AddValue(const std::string& name, const std::string& help, T& value)
{
UserItem<T>* item = new UserItem<T>();
auto item = std::make_shared<UserItem<T>>();
item->m_name = name;
item->m_help = help;
item->m_valuePtr = &value;
@@ -683,7 +693,7 @@ template <typename T>
void
CommandLine::AddNonOption(const std::string& name, const std::string& help, T& value)
{
UserItem<T>* item = new UserItem<T>();
auto item = std::make_shared<UserItem<T>>();
item->m_name = name;
item->m_help = help;
item->m_valuePtr = &value;