Merge pull request #206 from p-ranav/feature/metavar_help_and_usage

METAVAR, Improved help, Samples (Based on #187)
This commit is contained in:
Pranav 2022-09-21 21:16:37 -05:00 committed by GitHub
commit ce0b491571
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 709 additions and 67 deletions

View File

@ -28,6 +28,9 @@ target_include_directories(argparse INTERFACE
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>) $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>)
if(ARGPARSE_BUILD_SAMPLES)
add_subdirectory(samples)
endif()
if(ARGPARSE_BUILD_TESTS) if(ARGPARSE_BUILD_TESTS)
add_subdirectory(test) add_subdirectory(test)

View File

@ -142,7 +142,7 @@ catch (const std::runtime_error& err) {
} }
if (program["--verbose"] == true) { if (program["--verbose"] == true) {
std::cout << "Verbosity enabled" << std::endl; std::cout << "Verbosity enabled" << std::endl;
} }
``` ```
@ -338,15 +338,15 @@ The square of 4 is 16
``` ```
foo@bar:/home/dev/$ ./main --help foo@bar:/home/dev/$ ./main --help
Usage: main [options] square Usage: main [-h] [--verbose] square
Positional arguments: Positional arguments:
square display the square of a given number square display the square of a given number
Optional arguments: Optional arguments:
-h --help shows help message and exits [default: false] -h, --help shows help message and exits
-v --version prints version information and exits [default: false] -v, --version prints version information and exits
--verbose [default: false] --verbose
``` ```
You may also get the help message in string via `program.help().str()`. You may also get the help message in string via `program.help().str()`.
@ -357,30 +357,36 @@ You may also get the help message in string via `program.help().str()`.
information. `ArgumentParser::add_epilog` will add text after all other help output. information. `ArgumentParser::add_epilog` will add text after all other help output.
```cpp ```cpp
argparse::ArgumentParser program("main"); #include <argparse/argparse.hpp>
program.add_argument("thing") int main(int argc, char *argv[]) {
.help("Thing to use."); argparse::ArgumentParser program("main");
program.add_description("Forward a thing to the next member."); program.add_argument("thing").help("Thing to use.").metavar("THING");
program.add_epilog("Possible things include betingalw, chiz, and res."); program.add_argument("--member").help("The alias for the member to pass to.").metavar("ALIAS");
program.add_argument("--verbose").default_value(false).implicit_value(true);
program.parse_args(argc, argv); program.add_description("Forward a thing to the next member.");
program.add_epilog("Possible things include betingalw, chiz, and res.");
std::cout << program << std::endl; program.parse_args(argc, argv);
std::cout << program << std::endl;
}
``` ```
```console ```console
foo@bar:/home/dev/$ ./main --help Usage: main [-h] [--member ALIAS] [--verbose] THING
Usage: main thing
Forward a thing to the next member. Forward a thing to the next member.
Positional arguments: Positional arguments:
thing Thing to use. THING Thing to use.
Optional arguments: Optional arguments:
-h --help shows help message and exits [default: false] -h, --help shows help message and exits
-v --version prints version information and exits [default: false] -v, --version prints version information and exits
--member ALIAS The alias for the member to pass to.
--verbose
Possible things include betingalw, chiz, and res. Possible things include betingalw, chiz, and res.
``` ```
@ -710,12 +716,14 @@ int main(int argc, char *argv[]) {
// git add subparser // git add subparser
argparse::ArgumentParser add_command("add"); argparse::ArgumentParser add_command("add");
add_command.add_description("Add file contents to the index");
add_command.add_argument("files") add_command.add_argument("files")
.help("Files to add content from. Fileglobs (e.g. *.c) can be given to add all matching files.") .help("Files to add content from. Fileglobs (e.g. *.c) can be given to add all matching files.")
.remaining(); .remaining();
// git commit subparser // git commit subparser
argparse::ArgumentParser commit_command("commit"); argparse::ArgumentParser commit_command("commit");
commit_command.add_description("Record changes to the repository");
commit_command.add_argument("-a", "--all") commit_command.add_argument("-a", "--all")
.help("Tell the command to automatically stage files that have been modified and deleted.") .help("Tell the command to automatically stage files that have been modified and deleted.")
.default_value(false) .default_value(false)
@ -726,6 +734,7 @@ int main(int argc, char *argv[]) {
// git cat-file subparser // git cat-file subparser
argparse::ArgumentParser catfile_command("cat-file"); argparse::ArgumentParser catfile_command("cat-file");
catfile_command.add_description("Provide content or type and size information for repository objects");
catfile_command.add_argument("-t") catfile_command.add_argument("-t")
.help("Instead of the content, show the object type identified by <object>."); .help("Instead of the content, show the object type identified by <object>.");
@ -734,7 +743,9 @@ int main(int argc, char *argv[]) {
// git submodule subparser // git submodule subparser
argparse::ArgumentParser submodule_command("submodule"); argparse::ArgumentParser submodule_command("submodule");
submodule_command.add_description("Initialize, update or inspect submodules");
argparse::ArgumentParser submodule_update_command("update"); argparse::ArgumentParser submodule_update_command("update");
submodule_update_command.add_description("Update the registered submodules to match what the superproject expects");
submodule_update_command.add_argument("--init") submodule_update_command.add_argument("--init")
.default_value(false) .default_value(false)
.implicit_value(true); .implicit_value(true);
@ -763,41 +774,52 @@ int main(int argc, char *argv[]) {
```console ```console
foo@bar:/home/dev/$ ./git --help foo@bar:/home/dev/$ ./git --help
Usage: git [options] <command> [<args>] Usage: git [-h] {add,cat-file,commit,submodule}
Optional arguments: Optional arguments:
-h --help shows help message and exits [default: false] -h, --help shows help message and exits
-v --version prints version information and exits [default: false] -v, --version prints version information and exits
Subcommands: Subcommands:
add Add file contents to the index add Add file contents to the index
cat-file Provide content or type and size information for repository objects cat-file Provide content or type and size information for repository objects
commit Record changes to the repository commit Record changes to the repository
submodule Initialize, update or inspect submodules submodule Initialize, update or inspect submodules
foo@bar:/home/dev/$ ./git add --help foo@bar:/home/dev/$ ./git add --help
Usage: git add [options] files Usage: add [-h] files
Add file contents to the index Add file contents to the index
Positional arguments: Positional arguments:
files Files to add content from. Fileglobs (e.g. *.c) can be given to add all matching files. files Files to add content from. Fileglobs (e.g. *.c) can be given to add all matching files.
Optional arguments: Optional arguments:
-h --help shows help message and exits [default: false] -h, --help shows help message and exits
-v --version prints version information and exits [default: false] -v, --version prints version information and exits
foo@bar:/home/dev/$ ./git commit --help
Usage: commit [-h] [--all] [--message VAR]
Record changes to the repository
Optional arguments:
-h, --help shows help message and exits
-v, --version prints version information and exits
-a, --all Tell the command to automatically stage files that have been modified and deleted.
-m, --message Use the given <msg> as the commit message.
foo@bar:/home/dev/$ ./git submodule --help foo@bar:/home/dev/$ ./git submodule --help
Usage: git submodule [options] <command> [<args>] Usage: submodule [-h] {update}
Initialize, update or inspect submodules Initialize, update or inspect submodules
Optional arguments: Optional arguments:
-h --help shows help message and exits [default: false] -h, --help shows help message and exits
-v --version prints version information and exits [default: false] -v, --version prints version information and exits
Subcommands: Subcommands:
update Update the registered submodules to match what the superproject expects update Update the registered submodules to match what the superproject expects
``` ```
When a help message is requested from a subparser, only the help for that particular parser will be printed. The help message will not include parent parser or sibling parser messages. When a help message is requested from a subparser, only the help for that particular parser will be printed. The help message will not include parent parser or sibling parser messages.

View File

@ -1 +1 @@
clang-format -i include/argparse/*.hpp test/*.cpp clang-format -i include/argparse/*.hpp test/*.cpp samples/*.cpp

View File

@ -36,6 +36,7 @@ SOFTWARE.
#include <charconv> #include <charconv>
#include <cstdlib> #include <cstdlib>
#include <functional> #include <functional>
#include <iomanip>
#include <iostream> #include <iostream>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
@ -329,6 +330,21 @@ template <class T> struct parse_number<T, chars_format::fixed> {
} }
}; };
template <typename StrIt>
std::string join(StrIt first, StrIt last, const std::string &separator) {
if (first == last) {
return "";
}
std::stringstream value;
value << *first;
++first;
while (first != last) {
value << separator << *first;
++first;
}
return value.str();
}
} // namespace details } // namespace details
enum class nargs_pattern { optional, any, at_least_one }; enum class nargs_pattern { optional, any, at_least_one };
@ -379,6 +395,11 @@ public:
return *this; return *this;
} }
Argument &metavar(std::string metavar) {
m_metavar = std::move(metavar);
return *this;
}
template <typename T> Argument &default_value(T &&value) { template <typename T> Argument &default_value(T &&value) {
m_default_value_repr = details::repr(value); m_default_value_repr = details::repr(value);
m_default_value = std::forward<T>(value); m_default_value = std::forward<T>(value);
@ -573,29 +594,90 @@ public:
} }
} }
std::string get_inline_usage() const {
std::stringstream usage;
// Find the longest variant to show in the usage string
std::string longest_name = m_names[0];
for (const auto &s : m_names) {
if (s.size() > longest_name.size()) {
longest_name = s;
}
}
if (!m_is_required) {
usage << "[";
}
usage << longest_name;
const std::string metavar = !m_metavar.empty() ? m_metavar : "VAR";
if (m_num_args_range.get_max() > 0) {
usage << " " << metavar;
if (m_num_args_range.get_max() > 1) {
usage << "...";
}
}
if (!m_is_required) {
usage << "]";
}
return usage.str();
}
std::size_t get_arguments_length() const { std::size_t get_arguments_length() const {
return std::accumulate(std::begin(m_names), std::end(m_names),
std::size_t(0), [](const auto &sum, const auto &s) { std::size_t names_size = std::accumulate(
return sum + s.size() + std::begin(m_names), std::end(m_names), std::size_t(0),
1; // +1 for space between names [](const auto &sum, const auto &s) { return sum + s.size(); });
});
if (is_positional(m_names.front(), m_prefix_chars)) {
// A set metavar means this replaces the names
if (!m_metavar.empty()) {
// Indent and metavar
return 2 + m_metavar.size();
}
// Indent and space-separated
return 2 + names_size + (m_names.size() - 1);
}
// Is an option - include both names _and_ metavar
// size = text + (", " between names)
std::size_t size = names_size + 2 * (m_names.size() - 1);
if (!m_metavar.empty() && m_num_args_range == NArgsRange{1, 1}) {
size += m_metavar.size() + 1;
}
return size + 2; // indent
} }
friend std::ostream &operator<<(std::ostream &stream, friend std::ostream &operator<<(std::ostream &stream,
const Argument &argument) { const Argument &argument) {
std::stringstream name_stream; std::stringstream name_stream;
std::copy(std::begin(argument.m_names), std::end(argument.m_names), name_stream << " "; // indent
std::ostream_iterator<std::string>(name_stream, " ")); if (argument.is_positional(argument.m_names.front(),
stream << name_stream.str() << "\t" << argument.m_help; argument.m_prefix_chars)) {
if (argument.m_default_value.has_value()) { if (!argument.m_metavar.empty()) {
if (!argument.m_help.empty()) { name_stream << argument.m_metavar;
stream << " "; } else {
name_stream << details::join(argument.m_names.begin(),
argument.m_names.end(), " ");
} }
} else {
name_stream << details::join(argument.m_names.begin(),
argument.m_names.end(), ", ");
// If we have a metavar, and one narg - print the metavar
if (!argument.m_metavar.empty() &&
argument.m_num_args_range == NArgsRange{1, 1}) {
name_stream << " " << argument.m_metavar;
}
}
stream << name_stream.str() << "\t" << argument.m_help;
// print nargs spec
if (!argument.m_help.empty()) {
stream << " ";
}
stream << argument.m_num_args_range;
if (argument.m_default_value.has_value() &&
argument.m_num_args_range != NArgsRange{0, 0}) {
stream << "[default: " << argument.m_default_value_repr << "]"; stream << "[default: " << argument.m_default_value_repr << "]";
} else if (argument.m_is_required) { } else if (argument.m_is_required) {
if (!argument.m_help.empty()) {
stream << " ";
}
stream << "[required]"; stream << "[required]";
} }
stream << "\n"; stream << "\n";
@ -647,6 +729,29 @@ private:
std::size_t get_min() const { return m_min; } std::size_t get_min() const { return m_min; }
std::size_t get_max() const { return m_max; } std::size_t get_max() const { return m_max; }
// Print help message
friend auto operator<<(std::ostream &stream, const NArgsRange &range)
-> std::ostream & {
if (range.m_min == range.m_max) {
if (range.m_min != 0 && range.m_min != 1) {
stream << "[nargs: " << range.m_min << "] ";
}
} else {
if (range.m_max == std::numeric_limits<std::size_t>::max()) {
stream << "[nargs: " << range.m_min << " or more] ";
} else {
stream << "[nargs=" << range.m_min << ".." << range.m_max << "] ";
}
}
return stream;
}
bool operator==(const NArgsRange &rhs) const {
return rhs.m_min == m_min && rhs.m_max == m_max;
}
bool operator!=(const NArgsRange &rhs) const { return !(*this == rhs); }
}; };
void throw_nargs_range_validation_error() const { void throw_nargs_range_validation_error() const {
@ -913,6 +1018,7 @@ private:
std::vector<std::string> m_names; std::vector<std::string> m_names;
std::string_view m_used_name; std::string_view m_used_name;
std::string m_help; std::string m_help;
std::string m_metavar;
std::any m_default_value; std::any m_default_value;
std::string m_default_value_repr; std::string m_default_value_repr;
std::any m_implicit_value; std::any m_implicit_value;
@ -1094,8 +1200,8 @@ public:
* @throws std::runtime_error in case of any invalid argument * @throws std::runtime_error in case of any invalid argument
*/ */
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays) // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays)
void parse_known_args(int argc, const char *const argv[]) { auto parse_known_args(int argc, const char *const argv[]) {
parse_known_args({argv, argv + argc}); return parse_known_args({argv, argv + argc});
} }
/* Getter for options with default values. /* Getter for options with default values.
@ -1171,17 +1277,10 @@ public:
friend auto operator<<(std::ostream &stream, const ArgumentParser &parser) friend auto operator<<(std::ostream &stream, const ArgumentParser &parser)
-> std::ostream & { -> std::ostream & {
stream.setf(std::ios_base::left); stream.setf(std::ios_base::left);
stream << "Usage: " << parser.m_parser_path << " [options] ";
for (const auto &argument : parser.m_positional_arguments) {
stream << argument.m_names.front() << " ";
}
if (!parser.m_subparser_map.empty()) { auto longest_arg_length = parser.get_length_of_longest_argument();
stream << (parser.m_positional_arguments.empty() ? "" : " ")
<< "<command> [<args>]"; stream << parser.usage() << "\n\n";
}
std::size_t longest_arg_length = parser.get_length_of_longest_argument();
stream << "\n\n";
if (!parser.m_description.empty()) { if (!parser.m_description.empty()) {
stream << parser.m_description << "\n\n"; stream << parser.m_description << "\n\n";
@ -1212,8 +1311,10 @@ public:
: "\n") : "\n")
<< "Subcommands:\n"; << "Subcommands:\n";
for (const auto &[command, subparser] : parser.m_subparser_map) { for (const auto &[command, subparser] : parser.m_subparser_map) {
stream.width(static_cast<std::streamsize>(longest_arg_length)); stream << std::setw(2) << " ";
stream << command << "\t" << subparser->get().m_description << "\n"; stream << std::setw(static_cast<int>(longest_arg_length - 2))
<< command;
stream << " " << subparser->get().m_description << "\n";
} }
} }
@ -1232,6 +1333,48 @@ public:
return out; return out;
} }
// Format usage part of help only
auto usage() const -> std::string {
std::stringstream stream;
stream << "Usage: " << this->m_program_name;
// Add any options inline here
for (const auto &argument : this->m_optional_arguments) {
if (argument.m_names[0] == "-v") {
continue;
} else if (argument.m_names[0] == "-h") {
stream << " [-h]";
} else {
stream << " " << argument.get_inline_usage();
}
}
// Put positional arguments after the optionals
for (const auto &argument : this->m_positional_arguments) {
if (!argument.m_metavar.empty()) {
stream << " " << argument.m_metavar;
} else {
stream << " " << argument.m_names.front();
}
}
// Put subcommands after positional arguments
if (!m_subparser_map.empty()) {
stream << " {";
std::size_t i{0};
for (const auto &[command, unused] : m_subparser_map) {
if (i == 0) {
stream << command;
} else {
stream << "," << command;
}
++i;
}
stream << "}";
}
return stream.str();
}
// Printing the one and only help message // Printing the one and only help message
// I've stuck with a simple message format, nothing fancy. // I've stuck with a simple message format, nothing fancy.
[[deprecated("Use cout << program; instead. See also help().")]] std::string [[deprecated("Use cout << program; instead. See also help().")]] std::string

46
samples/CMakeLists.txt Normal file
View File

@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.6)
project(argparse_samples)
if(MSVC)
# Force to always compile with W4
if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]")
string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
# Update if necessary
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic -Wsign-conversion -Wshadow -Wconversion")
endif()
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
# Disable deprecation for windows
if (WIN32)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif()
function(add_sample NAME)
ADD_EXECUTABLE(ARGPARSE_SAMPLE_${NAME} ${NAME}.cpp)
INCLUDE_DIRECTORIES("../include" ".")
TARGET_LINK_LIBRARIES(ARGPARSE_SAMPLE_${NAME} PRIVATE argparse::argparse)
set_target_properties(ARGPARSE_SAMPLE_${NAME} PROPERTIES OUTPUT_NAME ${NAME})
endfunction()
add_sample(positional_argument)
add_sample(optional_flag_argument)
add_sample(required_optional_argument)
add_sample(is_used)
add_sample(joining_repeated_optional_arguments)
add_sample(repeating_argument_to_increase_value)
add_sample(negative_numbers)
add_sample(description_epilog_metavar)
add_sample(list_of_arguments)
add_sample(compound_arguments)
add_sample(gathering_remaining_arguments)
add_sample(subcommands)
add_sample(parse_known_args)
add_sample(custom_prefix_characters)
add_sample(custom_assignment_characters)

View File

@ -0,0 +1,36 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("test");
program.add_argument("-a").default_value(false).implicit_value(true);
program.add_argument("-b").default_value(false).implicit_value(true);
program.add_argument("-c")
.nargs(2)
.default_value(std::vector<float>{0.0f, 0.0f})
.scan<'g', float>();
try {
program.parse_args(argc, argv); // Example: ./main -abc 1.95 2.47
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
auto a = program.get<bool>("-a"); // true
auto b = program.get<bool>("-b"); // true
auto c = program.get<std::vector<float>>("-c"); // {1.95, 2.47}
std::cout << "a: " << std::boolalpha << a << "\n";
std::cout << "b: " << b << "\n";
if (!c.empty()) {
std::cout << "c: ";
for (auto &v : c) {
std::cout << v << " ";
}
std::cout << std::endl;
}
}

View File

@ -0,0 +1,27 @@
#include <argparse/argparse.hpp>
#include <cassert>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("test");
program.set_prefix_chars("-+/");
program.set_assign_chars("=:");
program.add_argument("--foo");
program.add_argument("/B");
try {
program.parse_args(argc, argv);
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
if (program.is_used("--foo")) {
std::cout << "--foo : " << program.get("--foo") << "\n";
}
if (program.is_used("/B")) {
std::cout << "/B : " << program.get("/B") << "\n";
}
}

View File

@ -0,0 +1,31 @@
#include <argparse/argparse.hpp>
#include <cassert>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("test");
program.set_prefix_chars("-+/");
program.add_argument("+f");
program.add_argument("--bar");
program.add_argument("/foo");
try {
program.parse_args(argc, argv);
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
if (program.is_used("+f")) {
std::cout << "+f : " << program.get("+f") << "\n";
}
if (program.is_used("--bar")) {
std::cout << "--bar : " << program.get("--bar") << "\n";
}
if (program.is_used("/foo")) {
std::cout << "/foo : " << program.get("/foo") << "\n";
}
}

View File

@ -0,0 +1,17 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("main");
program.add_argument("thing").help("Thing to use.").metavar("THING");
program.add_argument("--member")
.help("The alias for the member to pass to.")
.metavar("ALIAS");
program.add_argument("--verbose").default_value(false).implicit_value(true);
program.add_description("Forward a thing to the next member.");
program.add_epilog("Possible things include betingalw, chiz, and res.");
program.parse_args(argc, argv);
std::cout << program << std::endl;
}

View File

@ -0,0 +1,24 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("compiler");
program.add_argument("files").remaining();
try {
program.parse_args(argc, argv);
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
try {
auto files = program.get<std::vector<std::string>>("files");
std::cout << files.size() << " files provided" << std::endl;
for (auto &file : files)
std::cout << file << std::endl;
} catch (std::logic_error &e) {
std::cout << "No files provided" << std::endl;
}
}

26
samples/is_used.cpp Normal file
View File

@ -0,0 +1,26 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("test");
program.add_argument("--color")
.default_value(std::string{
"orange"}) // might otherwise be type const char* leading to an error
// when trying program.get<std::string>
.help("specify the cat's fur color");
try {
program.parse_args(argc, argv); // Example: ./main --color orange
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
auto color = program.get<std::string>("--color"); // "orange"
auto explicit_color =
program.is_used("--color"); // true, user provided orange
std::cout << "Color: " << color << "\n";
std::cout << "Argument was explicitly provided by user? " << std::boolalpha
<< explicit_color << "\n";
}

View File

@ -0,0 +1,28 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("test");
program.add_argument("--color")
.default_value<std::vector<std::string>>({"orange"})
.append()
.help("specify the cat's fur color");
try {
program.parse_args(
argc, argv); // Example: ./main --color red --color green --color blue
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
auto colors = program.get<std::vector<std::string>>(
"--color"); // {"red", "green", "blue"}
std::cout << "Colors: ";
for (const auto &c : colors) {
std::cout << c << " ";
}
std::cout << "\n";
}

View File

@ -0,0 +1,30 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("main");
program.add_argument("--input_files")
.help("The list of input files")
.nargs(2);
try {
program.parse_args(
argc, argv); // Example: ./main --input_files config.yml System.xml
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
auto files = program.get<std::vector<std::string>>(
"--input_files"); // {"config.yml", "System.xml"}
if (!files.empty()) {
std::cout << "Files: ";
for (auto &file : files) {
std::cout << file << " ";
}
std::cout << std::endl;
}
}

View File

@ -0,0 +1,32 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("test");
program.add_argument("integer").help("Input number").scan<'i', int>();
program.add_argument("floats")
.help("Vector of floats")
.nargs(4)
.scan<'g', float>();
try {
program.parse_args(argc, argv);
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
if (program.is_used("integer")) {
std::cout << "Integer : " << program.get<int>("integer") << "\n";
}
if (program.is_used("floats")) {
std::cout << "Floats : ";
for (const auto &f : program.get<std::vector<float>>("floats")) {
std::cout << f << " ";
}
std::cout << std::endl;
}
}

View File

@ -0,0 +1,22 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("test");
program.add_argument("--verbose")
.help("increase output verbosity")
.default_value(false)
.implicit_value(true);
try {
program.parse_args(argc, argv);
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
if (program["--verbose"] == true) {
std::cout << "Verbosity enabled" << std::endl;
}
}

View File

@ -0,0 +1,26 @@
#include <argparse/argparse.hpp>
#include <cassert>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("test");
program.add_argument("--foo").implicit_value(true).default_value(false);
program.add_argument("bar");
auto unknown_args = program.parse_known_args(argc, argv);
if (program.is_used("--foo")) {
std::cout << "--foo : " << program.get<bool>("--foo") << "\n";
}
if (program.is_used("bar")) {
std::cout << "bar : " << program.get<std::string>("bar") << "\n";
}
if (!unknown_args.empty()) {
std::cout << "Unknown args : ";
for (const auto &u : unknown_args) {
std::cout << u << " ";
}
std::cout << std::endl;
}
}

View File

@ -0,0 +1,28 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("main");
program.add_argument("square")
.help("display the square of a given number")
.scan<'i', int>();
program.add_argument("--verbose").default_value(false).implicit_value(true);
try {
program.parse_args(argc, argv);
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
int input = program.get<int>("square");
if (program["--verbose"] == true) {
std::cout << "The square of " << input << " is " << (input * input)
<< std::endl;
} else {
std::cout << (input * input) << std::endl;
}
}

View File

@ -0,0 +1,17 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("test");
int verbosity = 0;
program.add_argument("-V", "--verbose")
.action([&](const auto &) { ++verbosity; })
.append()
.default_value(false)
.implicit_value(true)
.nargs(0);
program.parse_args(argc, argv); // Example: ./main -VVVV
std::cout << "verbose level: " << verbosity << std::endl; // verbose level: 4
}

View File

@ -0,0 +1,19 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("test");
program.add_argument("-o", "--output")
.required()
.help("specify the output file.");
try {
program.parse_args(argc, argv);
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
std::cout << "Output written to " << program.get("-o") << "\n";
}

65
samples/subcommands.cpp Normal file
View File

@ -0,0 +1,65 @@
#include <argparse/argparse.hpp>
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("git");
// git add subparser
argparse::ArgumentParser add_command("add");
add_command.add_description("Add file contents to the index");
add_command.add_argument("files")
.help("Files to add content from. Fileglobs (e.g. *.c) can be given to "
"add all matching files.")
.remaining();
// git commit subparser
argparse::ArgumentParser commit_command("commit");
commit_command.add_description("Record changes to the repository");
commit_command.add_argument("-a", "--all")
.help("Tell the command to automatically stage files that have been "
"modified and deleted.")
.default_value(false)
.implicit_value(true);
commit_command.add_argument("-m", "--message")
.help("Use the given <msg> as the commit message.");
// git cat-file subparser
argparse::ArgumentParser catfile_command("cat-file");
catfile_command.add_description(
"Provide content or type and size information for repository objects");
catfile_command.add_argument("-t").help(
"Instead of the content, show the object type identified by <object>.");
catfile_command.add_argument("-p").help(
"Pretty-print the contents of <object> based on its type.");
// git submodule subparser
argparse::ArgumentParser submodule_command("submodule");
submodule_command.add_description("Initialize, update or inspect submodules");
argparse::ArgumentParser submodule_update_command("update");
submodule_update_command.add_description(
"Update the registered submodules to match what the superproject "
"expects");
submodule_update_command.add_argument("--init")
.default_value(false)
.implicit_value(true);
submodule_update_command.add_argument("--recursive")
.default_value(false)
.implicit_value(true);
submodule_command.add_subparser(submodule_update_command);
program.add_subparser(add_command);
program.add_subparser(commit_command);
program.add_subparser(catfile_command);
program.add_subparser(submodule_command);
try {
program.parse_args(argc, argv);
} catch (const std::runtime_error &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
}
// Use arguments
}

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.6) cmake_minimum_required(VERSION 3.6)
project(argparse) project(argparse_tests)
if(MSVC) if(MSVC)
# Force to always compile with W4 # Force to always compile with W4
@ -54,10 +54,10 @@ file(GLOB ARGPARSE_TEST_SOURCES
set_source_files_properties(main.cpp set_source_files_properties(main.cpp
PROPERTIES PROPERTIES
COMPILE_DEFINITIONS DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) COMPILE_DEFINITIONS DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN)
ADD_EXECUTABLE(ARGPARSE ${ARGPARSE_TEST_SOURCES}) ADD_EXECUTABLE(ARGPARSE_TESTS ${ARGPARSE_TEST_SOURCES})
INCLUDE_DIRECTORIES("../include" ".") INCLUDE_DIRECTORIES("../include" ".")
set_target_properties(ARGPARSE PROPERTIES OUTPUT_NAME tests) set_target_properties(ARGPARSE_TESTS PROPERTIES OUTPUT_NAME tests)
set_property(TARGET ARGPARSE PROPERTY CXX_STANDARD 17) set_property(TARGET ARGPARSE_TESTS PROPERTY CXX_STANDARD 17)
# Set ${PROJECT_NAME} as the startup project # Set ${PROJECT_NAME} as the startup project
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ARGPARSE) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ARGPARSE_TESTS)