option=value
Просто включите argparse.hpp, и все готово.
# include < argparse/argparse.hpp >
Чтобы начать анализ аргументов командной строки, создайте ArgumentParser
.
argparse::ArgumentParser program ( " program_name " );
ПРИМЕЧАНИЕ. В ArgumentParser
есть необязательный второй аргумент, который указывает версию программы. Пример: argparse::ArgumentParser program("libfoo", "1.9.0");
ПРИМЕЧАНИЕ. У ArgumentParser
есть необязательные третий и четвертый аргументы, которые управляют аргументами по умолчанию. Пример: argparse::ArgumentParser program("libfoo", "1.9.0", default_arguments::help, false);
См. Аргументы по умолчанию ниже.
Чтобы добавить новый аргумент, просто вызовите .add_argument(...)
. Вы можете предоставить переменный список имен аргументов, которые вы хотите сгруппировать, например -v
и --verbose
program.add_argument( " foo " );
program.add_argument( " -v " , " --verbose " ); // parameter packing
Argparse поддерживает различные типы аргументов, включая позиционные, необязательные и составные аргументы. Ниже вы можете увидеть, как настроить каждый из этих типов:
Вот пример позиционного аргумента :
# include < argparse/argparse.hpp >
int main ( int argc, char *argv[]) {
argparse::ArgumentParser program ( " program_name " );
program. add_argument ( " square " )
. help ( " display the square of a given integer " )
. scan < ' i ' , int >();
try {
program. parse_args (argc, argv);
}
catch ( const std:: exception & err) {
std::cerr << err. what () << std::endl;
std::cerr << program;
return 1 ;
}
auto input = program. get < int >( " square " );
std::cout << (input * input) << std::endl;
return 0 ;
}
И запускаем код:
foo@bar:/home/dev/ $ ./main 15
225
Вот что происходит:
add_argument()
используется для указания того, какие параметры командной строки программа готова принять. В данном случае я назвал его квадратным, чтобы оно соответствовало его функции..scan
для преобразования пользовательского ввода в целое число.parser.get<T>(key)
. Теперь давайте посмотрим на необязательные аргументы . Необязательные аргументы начинаются с -
или --
, например --verbose
или -a
. Необязательные аргументы могут быть размещены в любом месте входной последовательности.
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:: exception & err) {
std::cerr << err. what () << std::endl;
std::cerr << program;
std::exit ( 1 );
}
if (program[ " --verbose " ] == true ) {
std::cout << " Verbosity enabled " << std::endl;
}
foo@bar:/home/dev/ $ ./main --verbose
Verbosity enabled
Вот что происходит:
--verbose
не возникает ошибок. Обратите внимание, что при использовании .default_value(false)
, если необязательный аргумент не используется, его значение автоматически устанавливается равным false..implicit_value(true)
пользователь указывает, что этот параметр является скорее флагом, чем чем-то, требующим значения. Когда пользователь предоставляет параметр --verbose, для него устанавливается значение true. При определении аргументов флага вы можете использовать сокращенную запись flag()
, которая аналогична default_value(false).implicit_value(true)
.
argparse::ArgumentParser program ( " test " );
program.add_argument( " --verbose " )
.help( " increase output verbosity " )
.flag();
try {
program. parse_args (argc, argv);
}
catch ( const std:: exception & err) {
std::cerr << err. what () << std::endl;
std::cerr << program;
std::exit ( 1 );
}
if (program[ " --verbose " ] == true ) {
std::cout << " Verbosity enabled " << std::endl;
}
Существуют сценарии, в которых необходимо сделать необязательный аргумент обязательным . Как обсуждалось выше, необязательные аргументы начинаются с -
или --
. Вы можете сделать эти типы аргументов обязательными следующим образом:
program.add_argument( " -o " , " --output " )
.required()
.help( " specify the output file. " );
Если пользователь не предоставляет значение для этого параметра, генерируется исключение.
В качестве альтернативы вы можете указать значение по умолчанию, например:
program.add_argument( " -o " , " --output " )
.default_value(std::string( " - " ))
.required()
.help( " specify the output file. " );
Если вам требуется присутствие необязательного аргумента, но у вас нет для него подходящего значения по умолчанию, вы можете объединить тестирование и доступ к аргументу следующим образом:
if ( auto fn = program.present( " -o " )) {
do_something_with (*fn);
}
Подобно get
, present
метод также принимает аргумент шаблона. Но вместо возврата T
, parser.present<T>(key)
возвращает std::optional<T>
, поэтому, когда пользователь не предоставляет значение этому параметру, возвращаемое значение сравнивается с std::nullopt
.
Если вы хотите узнать, предоставил ли пользователь значение для аргумента, имеющего .default_value
, проверьте, есть ли аргумент .is_used()
.
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:: exception & 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
Возможно, вы захотите разрешить повторение необязательного аргумента и собрать все значения в одном месте.
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:: exception & 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"}
Обратите внимание, что .default_value
дается явный параметр шаблона, соответствующий типу, который вы хотите .get
.
Распространенным шаблоном является повторение аргумента для указания большего значения.
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
Создайте взаимоисключающую группу, используя program.add_mutually_exclusive_group(required = false)
. `argparse`` будет следить за тем, чтобы в командной строке присутствовал только один из аргументов взаимоисключающей группы:
auto &group = program.add_mutually_exclusive_group();
group.add_argument( " --first " );
group.add_argument( " --second " );
при следующем использовании выдаст ошибку:
foo@bar:/home/dev/ $ ./main --first 1 --second 2
Argument '--second VAR' not allowed with '--first VAR'
Функция add_mutually_exclusive_group()
также принимает required
аргумент, указывающий, что требуется хотя бы один из взаимоисключающих аргументов:
auto &group = program.add_mutually_exclusive_group( true );
group.add_argument( " --first " );
group.add_argument( " --second " );
при следующем использовании выдаст ошибку:
foo@bar:/home/dev/ $ ./main
One of the arguments '--first VAR' or '--second VAR' is required
Можно привязать аргументы к переменной, сохраняющей их значение, в качестве альтернативы явному вызову program.get<T>(arg_name)
или program[arg_name]
В настоящее время это реализовано для переменных типа bool
(это также неявно вызывает flag()
), int
, double
, std::string
, std::vector<std::string>
и std::vector<int>
. Если аргумент не указан в командной строке, в переменную устанавливается значение по умолчанию (если оно установлено).
bool flagvar = false ;
program.add_argument( " --flagvar " ).store_into(flagvar);
int intvar = 0 ;
program.add_argument( " --intvar " ).store_into(intvar);
double doublevar = 0 ;
program.add_argument( " --doublevar " ).store_into(doublevar);
std::string strvar;
program.add_argument( " --strvar " ).store_into(strvar);
std::vector<std::string> strvar_repeated;
program.add_argument( " --strvar-repeated " ).append().store_into(strvar_repeated);
std::vector<std::string> strvar_multi_valued;
program.add_argument( " --strvar-multi-valued " ).nargs( 2 ).store_into(strvar_multi_valued);
std::vector< int > intvar_repeated;
program.add_argument( " --intvar-repeated " ).append().store_into(intvar_repeated);
std::vector< int > intvar_multi_valued;
program.add_argument( " --intvar-multi-valued " ).nargs( 2 ).store_into(intvar_multi_valued);
Необязательные аргументы начинаются с -
. Может ли argparse
обрабатывать отрицательные числа? Ответ – да!
argparse::ArgumentParser program;
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:: exception & err) {
std::cerr << err. what () << std::endl;
std::cerr << program;
std::exit ( 1 );
}
// Some code to print arguments
foo@bar:/home/dev/ $ ./main -5 -1.1 -3.1415 -3.1e2 -4.51329E3
integer : -5
floats : -1.1 -3.1415 -310 -4513.29
Как вы можете видеть здесь, argparse
поддерживает отрицательные целые числа, отрицательные числа с плавающей запятой и экспоненциальную запись.
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:: exception & 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;
}
foo@bar:/home/dev/ $ ./main 4
16
foo@bar:/home/dev/ $ ./main 4 --verbose
The square of 4 is 16
foo@bar:/home/dev/ $ ./main --verbose 4
The square of 4 is 16
std::cout << program
печатает справочное сообщение, включая использование программы и информацию об аргументах, зарегистрированных с помощью ArgumentParser
. Для предыдущего примера вот справочное сообщение по умолчанию:
foo@bar:/home/dev/$ ./main --help
Usage: main [-h] [--verbose] square
Positional arguments:
square display the square of a given number
Optional arguments:
-h, --help shows help message and exits
-v, --version prints version information and exits
--verbose
Вы также можете получить справочное сообщение в виде строки с помощью program.help().str()
.
ArgumentParser::add_description
добавит текст перед подробной информацией об аргументе. ArgumentParser::add_epilog
добавит текст после всех остальных выводов справки.
# 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;
}
Usage: main [-h] [--member ALIAS] [--verbose] THING
Forward a thing to the next member.
Positional arguments:
THING Thing to use.
Optional arguments:
-h, --help shows help message and exits
-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.
Объекты ArgumentParser обычно связывают один аргумент командной строки с одним действием, которое необходимо выполнить. .nargs
связывает различное количество аргументов командной строки с одним действием. При использовании nargs(N)
N аргументов из командной строки будут собраны в список.
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:: exception & 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"}
ArgumentParser.get<T>()
имеет специализацию для std::vector
и std::list
. Итак, следующий вариант .get<std::list>
также будет работать.
auto files = program.get<std::list<std::string>>( " --input_files " ); // {"config.yml", "System.xml"}
Используя .scan
, можно быстро создать список желаемых типов значений из аргументов командной строки. Вот пример:
argparse::ArgumentParser program ( " main " );
program.add_argument( " --query_point " )
.help( " 3D query point " )
.nargs( 3 )
.default_value(std::vector< double >{ 0.0 , 0.0 , 0.0 })
.scan< ' g ' , double >();
try {
program. parse_args (argc, argv); // Example: ./main --query_point 3.5 4.7 9.2
}
catch ( const std:: exception & err) {
std::cerr << err. what () << std::endl;
std::cerr << program;
std::exit ( 1 );
}
auto query_point = program.get<std::vector< double >>( " --query_point " ); // {3.5, 4.7, 9.2}
Вы также можете создать список аргументов переменной длины с помощью .nargs
. Ниже приведены некоторые примеры.
program.add_argument( " --input_files " )
.nargs( 1 , 3 ); // This accepts 1 to 3 arguments.
Некоторые полезные шаблоны определены как «?», «*», «+» argparse в Python.
program.add_argument( " --input_files " )
.nargs(argparse::nargs_pattern::any); // "*" in Python. This accepts any number of arguments including 0.
program.add_argument( " --input_files " )
.nargs(argparse::nargs_pattern::at_least_one); // "+" in Python. This accepts one or more number of arguments.
program.add_argument( " --input_files " )
.nargs(argparse::nargs_pattern::optional); // "?" in Python. This accepts an argument optionally.
Составные аргументы — это необязательные аргументы, которые объединяются и предоставляются как один аргумент. Пример: ps -aux
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:: exception & 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}
// / Some code that prints parsed arguments
foo@bar:/home/dev/ $ ./main -ac 3.14 2.718
a = true
b = false
c = {3.14, 2.718}
foo@bar:/home/dev/ $ ./main -cb
a = false
b = true
c = {0.0, 0.0}
Вот что происходит:
-a
, -b
и -c
.-a
и -b
— аргументы переключения.-c
требует двух чисел с плавающей запятой из командной строки.-abc
или -bac
или -cab
. Это работает только с короткими односимвольными именами аргументов.-a
и -b
становятся истинными.-c
..default_value
Для входных данных пользователи могут выразить примитивный тип значения.
Метод .scan<Shape, T>
пытается преобразовать входящий std::string
в T
следуя спецификатору преобразования Shape
. В случае ошибок выдается исключение std::invalid_argument
или std::range_error
.
program.add_argument( " -x " )
.scan< ' d ' , int >();
program.add_argument( " scale " )
.scan< ' g ' , double >();
Shape
определяет, как «выглядят» входные данные, а аргумент шаблона типа определяет возвращаемое значение предопределенного действия. Допустимыми типами являются числа с плавающей запятой (т. е. float, double, long double) и целочисленные (т. е. знаковый char, short, int, long, long long).
Грамматика соответствует std::from_chars
, но не дублирует ее. Например, шестнадцатеричные числа могут начинаться с 0x
или 0X
, а числа с ведущим нулем могут обрабатываться как восьмеричные значения.
Форма | интерпретация |
---|---|
«а» или «А» | шестнадцатеричная с плавающей запятой |
«е» или «е» | научная запись (с плавающей запятой) |
«ф» или «ф» | фиксированное обозначение (с плавающей запятой) |
«г» или «г» | общая форма (фиксированная или научная) |
'д' | десятичный |
'я' | Грамматика std::from_chars с базой == 10 |
'о' | восьмеричный (без знака) |
'ты' | десятичный (без знака) |
«х» или «х» | шестнадцатеричный (без знака) |
argparse
предоставляет предопределенные аргументы и действия для -h
/ --help
и -v
/ --version
. По умолчанию эти действия завершают работу программы после отображения справки или сообщения о версии соответственно. Этот выход не вызывает деструкторы, пропуская очистку захваченных ресурсов.
Эти аргументы по умолчанию можно отключить во время создания ArgumentParser
, чтобы вы могли обрабатывать эти аргументы по-своему. (Обратите внимание, что при выборе аргументов по умолчанию необходимо указать имя и версию программы.)
argparse::ArgumentParser program ( " test " , " 1.0 " , default_arguments::none);
program.add_argument( " -h " , " --help " )
.action([=]( const std::string& s) {
std::cout << help (). str ();
})
.default_value( false )
.help( " shows help message " )
.implicit_value( true )
.nargs( 0 );
Приведенный выше фрагмент кода выводит справочное сообщение и продолжает работу. Он не поддерживает аргумент --version
.
По умолчанию для включенных аргументов используется default_arguments::all
. Аргументы по умолчанию не будут добавлены с помощью default_arguments::none
. default_arguments::help
и default_arguments::version
индивидуально добавят --help
и --version
.
Аргументы по умолчанию можно использовать при отключении выхода по умолчанию с этими аргументами. Этот четвертый аргумент ArgumentParser
( exit_on_default_arguments
) представляет собой логический флаг со значением true по умолчанию. Следующий вызов сохранит --help
и --version
, но не завершится при использовании этих аргументов.
argparse::ArgumentParser program ( " test " , " 1.0 " , default_arguments::all, false )
argparse
поддерживает сбор «оставшихся» аргументов в конце команды, например, для использования в компиляторе:
foo@bar:/home/dev/ $ compiler file1 file2 file3
Чтобы включить это, просто создайте аргумент и отметьте его как remaining
. Здесь собраны все оставшиеся аргументы, переданные в argparse.
argparse::ArgumentParser program ( " compiler " );
program.add_argument( " files " )
.remaining();
try {
program. parse_args (argc, argv);
}
catch ( const std:: exception & 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;
}
Если аргументы не указаны:
foo@bar:/home/dev/ $ ./compiler
No files provided
и когда указано несколько аргументов:
foo@bar:/home/dev/ $ ./compiler foo.txt bar.txt baz.txt
3 files provided
foo.txt
bar.txt
baz.txt
Процесс сбора оставшихся аргументов хорошо работает и с необязательными аргументами: