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