Return operands as a separate list from options
This commit is contained in:
parent
5619ec178c
commit
087353c487
49
README.md
49
README.md
|
@ -38,56 +38,11 @@ This implementation strives to follow (in decreasing precedence) the guidelines
|
|||
* `start`: Iterator to the initial argument.
|
||||
* `end`: Past-the-end iterator to signal the argument list’s end.
|
||||
|
||||
**Return value:** Dictionary containing, for each option present on the command line, a key-value pair with the option’s name as the key and the list of option-arguments associated to that option, following the order of the command line, as the value. If an option is has no associated option-arguments (i.e. is a flag), the value in the dictionary is an empty list. Operands are grouped in a special item using `options::operands` as its key and also follow the order of the command line.
|
||||
**Return value:** A pair with, first: a dictionary containing for each option present on the command line a key-value pair, with the option’s name as the key, and the list of option-arguments associated to that option, following the order of the command line, as the value. If an option has no associated option-arguments (i.e. is a flag), the value in the dictionary is an empty list; second: a list in which all operands are grouped following the order of the command line.
|
||||
|
||||
### Example
|
||||
|
||||
This simple program prints all options that are passed to it on the command line.
|
||||
|
||||
```cpp
|
||||
#include "options.hpp"
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
auto opts = options::parse(argv + 1, argv + argc);
|
||||
|
||||
for (const auto& [name, args] : opts)
|
||||
{
|
||||
if (name == options::operands)
|
||||
{
|
||||
std::cerr << "Operands: ";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << '"' << name << "\": ";
|
||||
}
|
||||
|
||||
if (args.empty())
|
||||
{
|
||||
std::cerr << "(no arguments)";
|
||||
}
|
||||
else
|
||||
{
|
||||
for (
|
||||
auto value_it = std::cbegin(args);
|
||||
value_it != std::cend(args);
|
||||
++value_it
|
||||
)
|
||||
{
|
||||
std::cerr << '"' << *value_it << '"';
|
||||
|
||||
if (std::next(value_it) != std::cend(args))
|
||||
{
|
||||
std::cerr << ", ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << '\n';
|
||||
}
|
||||
}
|
||||
```
|
||||
[This program](main.cpp) prints all options and operands that are passed to it on the command line.
|
||||
|
||||
## Other libraries
|
||||
|
||||
|
|
58
main.cpp
58
main.cpp
|
@ -1,42 +1,38 @@
|
|||
#include "options.hpp"
|
||||
#include <iostream>
|
||||
|
||||
template<typename Iterator>
|
||||
void print_list(std::ostream& out, Iterator start, Iterator end)
|
||||
{
|
||||
if (start == end)
|
||||
{
|
||||
out << "<empty>";
|
||||
return;
|
||||
}
|
||||
|
||||
for (; start != end; ++start)
|
||||
{
|
||||
out << '"' << *start << '"';
|
||||
|
||||
if (std::next(start) != end)
|
||||
{
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
auto opts = options::parse(argv + 1, argv + argc);
|
||||
auto [opts, operands] = options::parse(argv + 1, argv + argc);
|
||||
|
||||
std::cerr << "Operands: ";
|
||||
print_list(std::cerr, std::cbegin(operands), std::cend(operands));
|
||||
std::cerr << '\n';
|
||||
|
||||
for (const auto& [name, args] : opts)
|
||||
{
|
||||
if (name == options::operands)
|
||||
{
|
||||
std::cerr << "Operands: ";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << '"' << name << "\": ";
|
||||
}
|
||||
|
||||
if (args.empty())
|
||||
{
|
||||
std::cerr << "(no arguments)";
|
||||
}
|
||||
else
|
||||
{
|
||||
for (
|
||||
auto value_it = std::cbegin(args);
|
||||
value_it != std::cend(args);
|
||||
++value_it
|
||||
)
|
||||
{
|
||||
std::cerr << '"' << *value_it << '"';
|
||||
|
||||
if (std::next(value_it) != std::cend(args))
|
||||
{
|
||||
std::cerr << ", ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "“" << name << "”: ";
|
||||
print_list(std::cerr, std::cbegin(args), std::cend(args));
|
||||
std::cerr << '\n';
|
||||
}
|
||||
}
|
||||
|
|
47
options.hpp
47
options.hpp
|
@ -44,12 +44,6 @@ using Value = std::string;
|
|||
using Values = std::vector<Value>;
|
||||
using Dictionary = std::map<Key, Values>;
|
||||
|
||||
/**
|
||||
* @var Dictionary key under which operands (i.e. arguments that are neither
|
||||
* option nor options-arguments) are stored.
|
||||
*/
|
||||
constexpr auto operands = "";
|
||||
|
||||
/**
|
||||
* Parse a list of command line arguments into a set of program options.
|
||||
*
|
||||
|
@ -61,28 +55,26 @@ constexpr auto operands = "";
|
|||
*
|
||||
* @param start Iterator to the initial argument.
|
||||
* @param end Past-the-end iterator to signal the argument list’s end.
|
||||
* @return Dictionary containing, for each option present on the command line,
|
||||
* a key-value pair with the option’s name as the key and the list of
|
||||
* option-arguments associated to that option, following the order of the
|
||||
* command line, as the value. If an option is has no associated
|
||||
* @return A pair with, first: a dictionary containing for each option present
|
||||
* on the command line a key-value pair, with the option’s name as the key, and
|
||||
* the list of option-arguments associated to that option, following the order
|
||||
* of the command line, as the value. If an option has no associated
|
||||
* option-arguments (i.e. is a flag), the value in the dictionary is an empty
|
||||
* list. Operands are grouped in a special item using `options::operands` as
|
||||
* its key and also follow the order of the command line.
|
||||
* list; second: a list in which all operands are grouped following the order
|
||||
* of the command line.
|
||||
*/
|
||||
template<typename Iterator>
|
||||
auto parse(Iterator start, Iterator end)
|
||||
{
|
||||
Dictionary opts;
|
||||
Values operands;
|
||||
|
||||
// Signals whether a “--” argument was encountered, which implies that
|
||||
// all further arguments are to be treated as operands
|
||||
bool operands_only = false;
|
||||
|
||||
// Dictionary item into which the next value should be pushed
|
||||
auto [item, _] = opts.emplace(operands, Values{});
|
||||
|
||||
// Dictionary item into which all operands are pushed
|
||||
const auto operands_item = item;
|
||||
// Array into which the next value should be pushed
|
||||
Values* value_collector = &operands;
|
||||
|
||||
for (; start != end; ++start)
|
||||
{
|
||||
|
@ -95,7 +87,7 @@ auto parse(Iterator start, Iterator end)
|
|||
if (current[2] == '\0')
|
||||
{
|
||||
operands_only = true;
|
||||
item = operands_item;
|
||||
value_collector = &operands;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -106,15 +98,15 @@ auto parse(Iterator start, Iterator end)
|
|||
const char* key_end = current + 2;
|
||||
while (*key_end != '\0' && *key_end != '=') ++key_end;
|
||||
|
||||
std::tie(item, _) = opts.emplace(
|
||||
auto [item, _] = opts.emplace(
|
||||
std::string(current + 2, key_end - current - 2),
|
||||
Values{}
|
||||
);
|
||||
Values{});
|
||||
value_collector = &item->second;
|
||||
|
||||
if (*key_end == '=')
|
||||
{
|
||||
item->second.emplace_back(key_end + 1);
|
||||
item = operands_item;
|
||||
value_collector->emplace_back(key_end + 1);
|
||||
value_collector = &operands;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +119,8 @@ auto parse(Iterator start, Iterator end)
|
|||
|
||||
while (*letter != '\0')
|
||||
{
|
||||
std::tie(item, _) = opts.emplace(Key{*letter}, Values{});
|
||||
auto [item, _] = opts.emplace(Key{*letter}, Values{});
|
||||
value_collector = &item->second;
|
||||
++letter;
|
||||
}
|
||||
}
|
||||
|
@ -136,12 +129,12 @@ auto parse(Iterator start, Iterator end)
|
|||
else
|
||||
{
|
||||
// Either an option-argument or an operand
|
||||
item->second.emplace_back(current);
|
||||
item = operands_item;
|
||||
value_collector->emplace_back(current);
|
||||
value_collector = &operands;
|
||||
}
|
||||
}
|
||||
|
||||
return opts;
|
||||
return std::make_pair(opts, operands);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
88
tests.cpp
88
tests.cpp
|
@ -6,35 +6,42 @@ int main()
|
|||
/* Skeleton.
|
||||
|
||||
{
|
||||
std::array args{…};
|
||||
auto opts = options::parse(std::cbegin(args), std::cend(args));
|
||||
std::array l{…};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((oper == options::Values{…}));
|
||||
assert((opts == options::Dictionary{
|
||||
{options::operands, {…}},
|
||||
{"option", {"value", …}},
|
||||
…
|
||||
}));
|
||||
}
|
||||
*/
|
||||
|
||||
/* Empty. */
|
||||
{
|
||||
std::array<const char*, 0> l{};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((oper == options::Values{}));
|
||||
assert((opts == options::Dictionary{}));
|
||||
}
|
||||
|
||||
/* Operands. */
|
||||
{
|
||||
std::array args{"all", "these", "are", "operands"};
|
||||
auto opts = options::parse(std::cbegin(args), std::cend(args));
|
||||
std::array l{"all", "these", "are", "operands"};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((opts == options::Dictionary{
|
||||
{options::operands, {
|
||||
"all", "these", "are", "operands"
|
||||
}}
|
||||
}));
|
||||
assert((oper == options::Values{"all", "these", "are", "operands"}));
|
||||
assert((opts == options::Dictionary{}));
|
||||
}
|
||||
|
||||
/* Short Unix options. */
|
||||
{
|
||||
std::array args{"-a", "-b", "-c"};
|
||||
auto opts = options::parse(std::cbegin(args), std::cend(args));
|
||||
std::array l{"-a", "-b", "-c"};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((oper == options::Values{}));
|
||||
assert((opts == options::Dictionary{
|
||||
{options::operands, {}},
|
||||
{"a", {}},
|
||||
{"b", {}},
|
||||
{"c", {}}
|
||||
|
@ -43,11 +50,11 @@ int main()
|
|||
|
||||
/* Short Unix options with arguments. */
|
||||
{
|
||||
std::array args{"-v", "value", "not-a-value", "-w", "-v", "other"};
|
||||
auto opts = options::parse(std::cbegin(args), std::cend(args));
|
||||
std::array l{"-v", "value", "not-a-value", "-w", "-v", "other"};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((oper == options::Values{"not-a-value"}));
|
||||
assert((opts == options::Dictionary{
|
||||
{options::operands, {"not-a-value"}},
|
||||
{"v", {"value", "other"}},
|
||||
{"w", {}}
|
||||
}));
|
||||
|
@ -55,11 +62,11 @@ int main()
|
|||
|
||||
/* Short Unix options shorthand. */
|
||||
{
|
||||
std::array args{"-abcdef", "value", "not-a-value"};
|
||||
auto opts = options::parse(std::cbegin(args), std::cend(args));
|
||||
std::array l{"-abcdef", "value", "not-a-value"};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((oper == options::Values{"not-a-value"}));
|
||||
assert((opts == options::Dictionary{
|
||||
{options::operands, {"not-a-value"}},
|
||||
{"a", {}},
|
||||
{"b", {}},
|
||||
{"c", {}},
|
||||
|
@ -71,35 +78,46 @@ int main()
|
|||
|
||||
/* Long GNU options. */
|
||||
{
|
||||
std::array args{"--long", "--option"};
|
||||
auto opts = options::parse(std::cbegin(args), std::cend(args));
|
||||
std::array l{"--long", "--option"};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((oper == options::Values{}));
|
||||
assert((opts == options::Dictionary{
|
||||
{options::operands, {}},
|
||||
{"long", {}},
|
||||
{"option", {}}
|
||||
}));
|
||||
}
|
||||
|
||||
/* Long GNU options with arguments. */
|
||||
/* Long GNU options with arguments separated by whitespace. */
|
||||
{
|
||||
std::array args{"--value", "v", "--value", "-v", "value", "--value"};
|
||||
auto opts = options::parse(std::cbegin(args), std::cend(args));
|
||||
std::array l{"--value", "v", "--value", "-v", "value", "--value"};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((oper == options::Values{}));
|
||||
assert((opts == options::Dictionary{
|
||||
{options::operands, {}},
|
||||
{"v", {"value"}},
|
||||
{"value", {"v"}}
|
||||
}));
|
||||
}
|
||||
|
||||
/* Long GNU options with arguments separated by equals sign. */
|
||||
{
|
||||
std::array l{"--value=-2", "--value=-1", "--value=0"};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((oper == options::Values{}));
|
||||
assert((opts == options::Dictionary{
|
||||
{"value", {"-2", "-1", "0"}}
|
||||
}));
|
||||
}
|
||||
|
||||
/* Single dash as option-argument. */
|
||||
{
|
||||
std::array args{"--output", "-", "--input", "-", "-"};
|
||||
auto opts = options::parse(std::cbegin(args), std::cend(args));
|
||||
std::array l{"--output", "-", "--input", "-", "-"};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((oper == options::Values{"-"}));
|
||||
assert((opts == options::Dictionary{
|
||||
{options::operands, {"-"}},
|
||||
{"output", {"-"}},
|
||||
{"input", {"-"}}
|
||||
}));
|
||||
|
@ -107,11 +125,11 @@ int main()
|
|||
|
||||
/* Long, short and shorthand options mixed. */
|
||||
{
|
||||
std::array args{"-abc", "content", "--long", "-short", "tree", "out"};
|
||||
auto opts = options::parse(std::cbegin(args), std::cend(args));
|
||||
std::array l{"-abc", "content", "--long", "-short", "tree", "out"};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((oper == options::Values{"out"}));
|
||||
assert((opts == options::Dictionary{
|
||||
{options::operands, {"out"}},
|
||||
{"a", {}},
|
||||
{"b", {}},
|
||||
{"c", {"content"}},
|
||||
|
@ -126,11 +144,11 @@ int main()
|
|||
|
||||
/* Operand separator. */
|
||||
{
|
||||
std::array args{"--option", "--", "--not-option"};
|
||||
auto opts = options::parse(std::cbegin(args), std::cend(args));
|
||||
std::array l{"--option", "--", "--not-option"};
|
||||
auto [opts, oper] = options::parse(std::cbegin(l), std::cend(l));
|
||||
|
||||
assert((oper == options::Values{"--not-option"}));
|
||||
assert((opts == options::Dictionary{
|
||||
{options::operands, {"--not-option"}},
|
||||
{"option", {}}
|
||||
}));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue