Move parse result to an immutable object
This is far from ideal, but it's the first step in moving the parse result to an immutable object so that the parser can be reused.
This commit is contained in:
parent
d8458a8c1a
commit
8010e06952
6
CHANGELOG.md
Normal file
6
CHANGELOG.md
Normal file
@ -0,0 +1,6 @@
|
||||
# 2.0
|
||||
|
||||
## Changed
|
||||
|
||||
* `Options::parse` returns a ParseResult rather than storing the parse
|
||||
result internally.
|
||||
@ -34,6 +34,7 @@ THE SOFTWARE.
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
@ -893,41 +894,16 @@ namespace cxxopts
|
||||
std::vector<HelpOptionDetails> options;
|
||||
};
|
||||
|
||||
class Options
|
||||
class ParseResult
|
||||
{
|
||||
public:
|
||||
|
||||
Options(std::string program, std::string help_string = "")
|
||||
: m_program(std::move(program))
|
||||
, m_help_string(toLocalString(std::move(help_string)))
|
||||
, m_positional_help("positional parameters")
|
||||
, m_next_positional(m_positional.end())
|
||||
{
|
||||
}
|
||||
|
||||
Options&
|
||||
positional_help(std::string help_text)
|
||||
{
|
||||
m_positional_help = std::move(help_text);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
parse(int& argc, char**& argv);
|
||||
|
||||
OptionAdder
|
||||
add_options(std::string group = "");
|
||||
|
||||
void
|
||||
add_option
|
||||
(
|
||||
const std::string& group,
|
||||
const std::string& s,
|
||||
const std::string& l,
|
||||
std::string desc,
|
||||
std::shared_ptr<const Value> value,
|
||||
std::string arg_help
|
||||
);
|
||||
template <typename Iterator>
|
||||
ParseResult(
|
||||
Iterator,
|
||||
Iterator,
|
||||
std::vector<std::string>,
|
||||
int&, char**&);
|
||||
|
||||
int
|
||||
count(const std::string& o) const
|
||||
@ -954,6 +930,77 @@ namespace cxxopts
|
||||
return *iter->second;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void
|
||||
parse(int& argc, char**& argv);
|
||||
|
||||
void
|
||||
add_to_option(const std::string& option, const std::string& arg);
|
||||
|
||||
bool
|
||||
consume_positional(std::string a);
|
||||
|
||||
void
|
||||
parse_option
|
||||
(
|
||||
std::shared_ptr<OptionDetails> value,
|
||||
const std::string& name,
|
||||
const std::string& arg = ""
|
||||
);
|
||||
|
||||
void
|
||||
checked_parse_arg
|
||||
(
|
||||
int argc,
|
||||
char* argv[],
|
||||
int& current,
|
||||
std::shared_ptr<OptionDetails> value,
|
||||
const std::string& name
|
||||
);
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<OptionDetails>> m_options;
|
||||
std::vector<std::string> m_positional;
|
||||
std::vector<std::string>::iterator m_next_positional;
|
||||
std::unordered_set<std::string> m_positional_set;
|
||||
};
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
|
||||
Options(std::string program, std::string help_string = "")
|
||||
: m_program(std::move(program))
|
||||
, m_help_string(toLocalString(std::move(help_string)))
|
||||
, m_positional_help("positional parameters")
|
||||
, m_next_positional(m_positional.end())
|
||||
{
|
||||
}
|
||||
|
||||
Options&
|
||||
positional_help(std::string help_text)
|
||||
{
|
||||
m_positional_help = std::move(help_text);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ParseResult
|
||||
parse(int& argc, char**& argv);
|
||||
|
||||
OptionAdder
|
||||
add_options(std::string group = "");
|
||||
|
||||
void
|
||||
add_option
|
||||
(
|
||||
const std::string& group,
|
||||
const std::string& s,
|
||||
const std::string& l,
|
||||
std::string desc,
|
||||
std::shared_ptr<const Value> value,
|
||||
std::string arg_help
|
||||
);
|
||||
|
||||
//parse positional arguments into the given option
|
||||
void
|
||||
parse_positional(std::string option);
|
||||
@ -979,30 +1026,6 @@ namespace cxxopts
|
||||
std::shared_ptr<OptionDetails> details
|
||||
);
|
||||
|
||||
bool
|
||||
consume_positional(std::string a);
|
||||
|
||||
void
|
||||
add_to_option(const std::string& option, const std::string& arg);
|
||||
|
||||
void
|
||||
parse_option
|
||||
(
|
||||
std::shared_ptr<OptionDetails> value,
|
||||
const std::string& name,
|
||||
const std::string& arg = ""
|
||||
);
|
||||
|
||||
void
|
||||
checked_parse_arg
|
||||
(
|
||||
int argc,
|
||||
char* argv[],
|
||||
int& current,
|
||||
std::shared_ptr<OptionDetails> value,
|
||||
const std::string& name
|
||||
);
|
||||
|
||||
String
|
||||
help_one_group(const std::string& group) const;
|
||||
|
||||
@ -1053,24 +1076,6 @@ namespace cxxopts
|
||||
std::string m_group;
|
||||
};
|
||||
|
||||
// A helper function for setting required arguments
|
||||
inline
|
||||
void
|
||||
check_required
|
||||
(
|
||||
const Options& options,
|
||||
const std::vector<std::string>& required
|
||||
)
|
||||
{
|
||||
for (auto& r : required)
|
||||
{
|
||||
if (options.count(r) == 0)
|
||||
{
|
||||
throw option_required_exception(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr int OPTION_LONGEST = 30;
|
||||
@ -1188,6 +1193,18 @@ namespace cxxopts
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
ParseResult::ParseResult(Iterator begin, Iterator end,
|
||||
std::vector<std::string> positional,
|
||||
int& argc, char**& argv
|
||||
)
|
||||
: m_options(begin, end)
|
||||
, m_positional(std::move(positional))
|
||||
, m_next_positional(m_positional.begin())
|
||||
{
|
||||
parse(argc, argv);
|
||||
}
|
||||
|
||||
inline
|
||||
OptionAdder
|
||||
Options::add_options(std::string group)
|
||||
@ -1255,7 +1272,7 @@ OptionAdder::operator()
|
||||
|
||||
inline
|
||||
void
|
||||
Options::parse_option
|
||||
ParseResult::parse_option
|
||||
(
|
||||
std::shared_ptr<OptionDetails> value,
|
||||
const std::string& /*name*/,
|
||||
@ -1267,7 +1284,7 @@ Options::parse_option
|
||||
|
||||
inline
|
||||
void
|
||||
Options::checked_parse_arg
|
||||
ParseResult::checked_parse_arg
|
||||
(
|
||||
int argc,
|
||||
char* argv[],
|
||||
@ -1303,7 +1320,7 @@ Options::checked_parse_arg
|
||||
|
||||
inline
|
||||
void
|
||||
Options::add_to_option(const std::string& option, const std::string& arg)
|
||||
ParseResult::add_to_option(const std::string& option, const std::string& arg)
|
||||
{
|
||||
auto iter = m_options.find(option);
|
||||
|
||||
@ -1317,7 +1334,7 @@ Options::add_to_option(const std::string& option, const std::string& arg)
|
||||
|
||||
inline
|
||||
bool
|
||||
Options::consume_positional(std::string a)
|
||||
ParseResult::consume_positional(std::string a)
|
||||
{
|
||||
while (m_next_positional != m_positional.end())
|
||||
{
|
||||
@ -1368,8 +1385,16 @@ Options::parse_positional(std::vector<std::string> options)
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
ParseResult
|
||||
Options::parse(int& argc, char**& argv)
|
||||
{
|
||||
ParseResult result(m_options.begin(), m_options.end(), m_positional, argc, argv);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
ParseResult::parse(int& argc, char**& argv)
|
||||
{
|
||||
int current = 1;
|
||||
|
||||
|
||||
@ -63,9 +63,9 @@ int main(int argc, char* argv[])
|
||||
|
||||
options.parse_positional({"input", "output", "positional"});
|
||||
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
if (options.count("help"))
|
||||
if (result.count("help"))
|
||||
{
|
||||
std::cout << options.help({"", "Group"}) << std::endl;
|
||||
exit(0);
|
||||
@ -73,18 +73,18 @@ int main(int argc, char* argv[])
|
||||
|
||||
if (apple)
|
||||
{
|
||||
std::cout << "Saw option ‘a’ " << options.count("a") << " times " <<
|
||||
std::cout << "Saw option ‘a’ " << result.count("a") << " times " <<
|
||||
std::endl;
|
||||
}
|
||||
|
||||
if (options.count("b"))
|
||||
if (result.count("b"))
|
||||
{
|
||||
std::cout << "Saw option ‘b’" << std::endl;
|
||||
}
|
||||
|
||||
if (options.count("f"))
|
||||
if (result.count("f"))
|
||||
{
|
||||
auto& ff = options["f"].as<std::vector<std::string>>();
|
||||
auto& ff = result["f"].as<std::vector<std::string>>();
|
||||
std::cout << "Files" << std::endl;
|
||||
for (const auto& f : ff)
|
||||
{
|
||||
@ -92,36 +92,36 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if (options.count("input"))
|
||||
if (result.count("input"))
|
||||
{
|
||||
std::cout << "Input = " << options["input"].as<std::string>()
|
||||
std::cout << "Input = " << result["input"].as<std::string>()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (options.count("output"))
|
||||
if (result.count("output"))
|
||||
{
|
||||
std::cout << "Output = " << options["output"].as<std::string>()
|
||||
std::cout << "Output = " << result["output"].as<std::string>()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (options.count("positional"))
|
||||
if (result.count("positional"))
|
||||
{
|
||||
std::cout << "Positional = {";
|
||||
auto& v = options["positional"].as<std::vector<std::string>>();
|
||||
auto& v = result["positional"].as<std::vector<std::string>>();
|
||||
for (const auto& s : v) {
|
||||
std::cout << s << ", ";
|
||||
}
|
||||
std::cout << "}" << std::endl;
|
||||
}
|
||||
|
||||
if (options.count("int"))
|
||||
if (result.count("int"))
|
||||
{
|
||||
std::cout << "int = " << options["int"].as<int>() << std::endl;
|
||||
std::cout << "int = " << result["int"].as<int>() << std::endl;
|
||||
}
|
||||
|
||||
if (options.count("float"))
|
||||
if (result.count("float"))
|
||||
{
|
||||
std::cout << "float = " << options["float"].as<float>() << std::endl;
|
||||
std::cout << "float = " << result["float"].as<float>() << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Arguments remain = " << argc << std::endl;
|
||||
|
||||
@ -71,17 +71,17 @@ TEST_CASE("Basic options", "[options]")
|
||||
char** actual_argv = argv.argv();
|
||||
auto argc = argv.argc();
|
||||
|
||||
options.parse(argc, actual_argv);
|
||||
auto result = options.parse(argc, actual_argv);
|
||||
|
||||
CHECK(options.count("long") == 1);
|
||||
CHECK(options.count("s") == 1);
|
||||
CHECK(options.count("value") == 1);
|
||||
CHECK(options.count("a") == 1);
|
||||
CHECK(options["value"].as<std::string>() == "value");
|
||||
CHECK(options["a"].as<std::string>() == "b");
|
||||
CHECK(options.count("6") == 1);
|
||||
CHECK(options.count("p") == 2);
|
||||
CHECK(options.count("space") == 2);
|
||||
CHECK(result.count("long") == 1);
|
||||
CHECK(result.count("s") == 1);
|
||||
CHECK(result.count("value") == 1);
|
||||
CHECK(result.count("a") == 1);
|
||||
CHECK(result["value"].as<std::string>() == "value");
|
||||
CHECK(result["a"].as<std::string>() == "b");
|
||||
CHECK(result.count("6") == 1);
|
||||
CHECK(result.count("p") == 2);
|
||||
CHECK(result.count("space") == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("Short options", "[options]")
|
||||
@ -96,36 +96,15 @@ TEST_CASE("Short options", "[options]")
|
||||
auto actual_argv = argv.argv();
|
||||
auto argc = argv.argc();
|
||||
|
||||
options.parse(argc, actual_argv);
|
||||
auto result = options.parse(argc, actual_argv);
|
||||
|
||||
CHECK(options.count("a") == 1);
|
||||
CHECK(options["a"].as<std::string>() == "value");
|
||||
CHECK(result.count("a") == 1);
|
||||
CHECK(result["a"].as<std::string>() == "value");
|
||||
|
||||
REQUIRE_THROWS_AS(options.add_options()("", "nothing option"),
|
||||
cxxopts::invalid_option_format_error);
|
||||
}
|
||||
|
||||
TEST_CASE("Required arguments", "[options]")
|
||||
{
|
||||
cxxopts::Options options("required", " - test required options");
|
||||
options.add_options()
|
||||
("one", "one option")
|
||||
("two", "second option")
|
||||
;
|
||||
|
||||
Argv argv({
|
||||
"required",
|
||||
"--one"
|
||||
});
|
||||
|
||||
auto aargv = argv.argv();
|
||||
auto argc = argv.argc();
|
||||
|
||||
options.parse(argc, aargv);
|
||||
REQUIRE_THROWS_AS(cxxopts::check_required(options, {"two"}),
|
||||
cxxopts::option_required_exception);
|
||||
}
|
||||
|
||||
TEST_CASE("No positional", "[positional]")
|
||||
{
|
||||
cxxopts::Options options("test_no_positional",
|
||||
@ -135,7 +114,7 @@ TEST_CASE("No positional", "[positional]")
|
||||
|
||||
char** argv = av.argv();
|
||||
auto argc = av.argc();
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(argc == 4);
|
||||
CHECK(strcmp(argv[1], "a") == 0);
|
||||
@ -158,7 +137,7 @@ TEST_CASE("All positional", "[positional]")
|
||||
|
||||
options.parse_positional("positional");
|
||||
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(argc == 1);
|
||||
REQUIRE(positional.size() == 3);
|
||||
@ -186,14 +165,14 @@ TEST_CASE("Some positional explicit", "[positional]")
|
||||
char** argv = av.argv();
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
CHECK(argc == 1);
|
||||
CHECK(options.count("output"));
|
||||
CHECK(options["input"].as<std::string>() == "b");
|
||||
CHECK(options["output"].as<std::string>() == "a");
|
||||
CHECK(result.count("output"));
|
||||
CHECK(result["input"].as<std::string>() == "b");
|
||||
CHECK(result["output"].as<std::string>() == "a");
|
||||
|
||||
auto& positional = options["positional"].as<std::vector<std::string>>();
|
||||
auto& positional = result["positional"].as<std::vector<std::string>>();
|
||||
|
||||
REQUIRE(positional.size() == 2);
|
||||
CHECK(positional[0] == "c");
|
||||
@ -234,10 +213,10 @@ TEST_CASE("Empty with implicit value", "[implicit]")
|
||||
char** argv = av.argv();
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(options.count("implicit") == 1);
|
||||
REQUIRE(options["implicit"].as<std::string>() == "");
|
||||
REQUIRE(result.count("implicit") == 1);
|
||||
REQUIRE(result["implicit"].as<std::string>() == "");
|
||||
}
|
||||
|
||||
TEST_CASE("Integers", "[options]")
|
||||
@ -252,11 +231,11 @@ TEST_CASE("Integers", "[options]")
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse_positional("positional");
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(options.count("positional") == 6);
|
||||
REQUIRE(result.count("positional") == 6);
|
||||
|
||||
auto& positional = options["positional"].as<std::vector<int>>();
|
||||
auto& positional = result["positional"].as<std::vector<int>>();
|
||||
CHECK(positional[0] == 5);
|
||||
CHECK(positional[1] == 6);
|
||||
CHECK(positional[2] == -6);
|
||||
@ -294,11 +273,11 @@ TEST_CASE("Integer bounds", "[integer]")
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse_positional("positional");
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(options.count("positional") == 5);
|
||||
REQUIRE(result.count("positional") == 5);
|
||||
|
||||
auto& positional = options["positional"].as<std::vector<int8_t>>();
|
||||
auto& positional = result["positional"].as<std::vector<int8_t>>();
|
||||
CHECK(positional[0] == 127);
|
||||
CHECK(positional[1] == -128);
|
||||
CHECK(positional[2] == 0x7f);
|
||||
@ -350,14 +329,14 @@ TEST_CASE("Floats", "[options]")
|
||||
auto argc = av.argc();
|
||||
|
||||
options.parse_positional("positional");
|
||||
options.parse(argc, argv);
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
REQUIRE(options.count("double") == 1);
|
||||
REQUIRE(options.count("positional") == 4);
|
||||
REQUIRE(result.count("double") == 1);
|
||||
REQUIRE(result.count("positional") == 4);
|
||||
|
||||
CHECK(options["double"].as<double>() == 0.5);
|
||||
CHECK(result["double"].as<double>() == 0.5);
|
||||
|
||||
auto& positional = options["positional"].as<std::vector<float>>();
|
||||
auto& positional = result["positional"].as<std::vector<float>>();
|
||||
CHECK(positional[0] == 4);
|
||||
CHECK(positional[1] == -4);
|
||||
CHECK(positional[2] == 1.5e6);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user