option=value
Basta incluir argparse.hpp e pronto.
# include < argparse/argparse.hpp >
Para começar a analisar argumentos de linha de comando, crie um ArgumentParser
.
argparse::ArgumentParser program ( " program_name " );
NOTA: Existe um segundo argumento opcional para o ArgumentParser
que é a versão do programa. Exemplo: argparse::ArgumentParser program("libfoo", "1.9.0");
NOTA: Existem terceiro e quarto argumentos opcionais para o ArgumentParser
que controlam os argumentos padrão. Exemplo: argparse::ArgumentParser program("libfoo", "1.9.0", default_arguments::help, false);
Consulte Argumentos padrão abaixo.
Para adicionar um novo argumento, basta chamar .add_argument(...)
. Você pode fornecer uma lista variada de nomes de argumentos que deseja agrupar, por exemplo, -v
e --verbose
program.add_argument( " foo " );
program.add_argument( " -v " , " --verbose " ); // parameter packing
Argparse oferece suporte a uma variedade de tipos de argumentos, incluindo argumentos posicionais, opcionais e compostos. Abaixo você confere como configurar cada um desses tipos:
Aqui está um exemplo de argumento posicional :
# 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 ;
}
E executando o código:
foo@bar:/home/dev/ $ ./main 15
225
Aqui está o que está acontecendo:
add_argument()
é usado para especificar quais opções de linha de comando o programa está disposto a aceitar. Neste caso, chamei-o de quadrado para que esteja alinhado com sua função..scan
para converter a entrada do usuário em um número inteiro.parser.get<T>(key)
. Agora, vamos dar uma olhada nos argumentos opcionais . Argumentos opcionais começam com -
ou --
, por exemplo, --verbose
ou -a
. Argumentos opcionais podem ser colocados em qualquer lugar na sequência de entrada.
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
Aqui está o que está acontecendo:
--verbose
. Observe que ao usar .default_value(false)
, se o argumento opcional não for usado, seu valor será automaticamente definido como falso..implicit_value(true)
, o usuário especifica que esta opção é mais um sinalizador do que algo que requer um valor. Quando o usuário fornece a opção --verbose, seu valor é definido como verdadeiro. Ao definir argumentos de flag, você pode usar a abreviatura flag()
que é igual 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;
}
Existem cenários em que você gostaria de tornar obrigatório um argumento opcional. Conforme discutido acima, os argumentos opcionais começam com -
ou --
. Você pode tornar esses tipos de argumentos obrigatórios da seguinte forma:
program.add_argument( " -o " , " --output " )
.required()
.help( " specify the output file. " );
Se o usuário não fornecer um valor para este parâmetro, uma exceção será lançada.
Como alternativa, você pode fornecer um valor padrão como este:
program.add_argument( " -o " , " --output " )
.default_value(std::string( " - " ))
.required()
.help( " specify the output file. " );
Se você precisar que um argumento opcional esteja presente, mas não tiver um bom valor padrão para ele, poderá combinar o teste e o acesso ao argumento da seguinte maneira:
if ( auto fn = program.present( " -o " )) {
do_something_with (*fn);
}
Semelhante a get
, o método present
também aceita um argumento de modelo. Mas em vez de retornar T
, parser.present<T>(key)
retorna std::optional<T>
, de modo que quando o usuário não fornece um valor para este parâmetro, o valor de retorno é comparado igual a std::nullopt
.
Se você quiser saber se o usuário forneceu um valor para um argumento que possui um .default_value
, verifique se o argumento .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
Você pode permitir que um argumento opcional seja repetido e reunir todos os valores em um só lugar.
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"}
Observe que .default_value
recebe um parâmetro de modelo explícito para corresponder ao tipo que você deseja .get
.
Um padrão comum é repetir um argumento para indicar um valor maior.
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
Crie um grupo mutuamente exclusivo usando program.add_mutually_exclusive_group(required = false)
. `argparse`` garantirá que apenas um dos argumentos do grupo mutuamente exclusivo esteja presente na linha de comando:
auto &group = program.add_mutually_exclusive_group();
group.add_argument( " --first " );
group.add_argument( " --second " );
com o seguinte uso produzirá um erro:
foo@bar:/home/dev/ $ ./main --first 1 --second 2
Argument '--second VAR' not allowed with '--first VAR'
A função add_mutually_exclusive_group()
também aceita um argumento required
, para indicar que pelo menos um dos argumentos mutuamente exclusivos é necessário:
auto &group = program.add_mutually_exclusive_group( true );
group.add_argument( " --first " );
group.add_argument( " --second " );
com o seguinte uso produzirá um erro:
foo@bar:/home/dev/ $ ./main
One of the arguments '--first VAR' or '--second VAR' is required
É possível vincular argumentos a uma variável armazenando seu valor, como alternativa à chamada explícita de program.get<T>(arg_name)
ou program[arg_name]
Atualmente, isso é implementado para variáveis do tipo bool
(isso também chama implicitamente flag()
), int
, double
, std::string
, std::vector<std::string>
e std::vector<int>
. Se o argumento não for especificado na linha de comando, o valor padrão (se definido) será definido na variável.
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);
Argumentos opcionais começam com -
. argparse
pode lidar com números negativos? A resposta é sim!
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
Como você pode ver aqui, argparse
suporta números inteiros negativos, números flutuantes negativos e notação científica.
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
imprime uma mensagem de ajuda, incluindo o uso do programa e informações sobre os argumentos registrados com o ArgumentParser
. Para o exemplo anterior, aqui está a mensagem de ajuda padrão:
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
Você também pode receber a mensagem de ajuda em string via program.help().str()
.
ArgumentParser::add_description
adicionará texto antes das informações detalhadas do argumento. ArgumentParser::add_epilog
adicionará texto após todas as outras saídas de ajuda.
# 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.
Os objetos ArgumentParser geralmente associam um único argumento de linha de comando a uma única ação a ser executada. O .nargs
associa um número diferente de argumentos de linha de comando a uma única ação. Ao usar nargs(N)
, N argumentos da linha de comando serão reunidos em uma lista.
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>()
possui especializações para std::vector
e std::list
. Portanto, a seguinte variante, .get<std::list>
, também funcionará.
auto files = program.get<std::list<std::string>>( " --input_files " ); // {"config.yml", "System.xml"}
Usando .scan
, pode-se construir rapidamente uma lista de tipos de valores desejados a partir de argumentos de linha de comando. Aqui está um exemplo:
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}
Você também pode criar uma lista de argumentos de comprimento variável com o .nargs
. Abaixo estão alguns exemplos.
program.add_argument( " --input_files " )
.nargs( 1 , 3 ); // This accepts 1 to 3 arguments.
Alguns padrões úteis são definidos como "?", "*", "+" de argparse em 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.
Argumentos compostos são argumentos opcionais combinados e fornecidos como um único argumento. Exemplo: 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}
Aqui está o que está acontecendo:
-a
, -b
e -c
.-a
e -b
são argumentos de alternância.-c
requer 2 números de ponto flutuante na linha de comando.-abc
ou -bac
ou -cab
. Isso funciona apenas com nomes curtos de argumentos de um único caractere.-a
e -b
tornam-se verdadeiros.-c
..default_value
Para entradas, os usuários podem expressar um tipo primitivo para o valor.
O método .scan<Shape, T>
tenta converter o std::string
de entrada em T
seguindo o especificador de conversão Shape
. Uma exceção std::invalid_argument
ou std::range_error
é lançada para erros.
program.add_argument( " -x " )
.scan< ' d ' , int >();
program.add_argument( " scale " )
.scan< ' g ' , double >();
Shape
especifica a "aparência" da entrada e o argumento do modelo de tipo especifica o valor de retorno da ação predefinida. Os tipos aceitáveis são ponto flutuante (ou seja, float, double, long double) e integral (ou seja, char assinado, short, int, long, long long).
A gramática segue std::from_chars
, mas não a duplica exatamente. Por exemplo, os números hexadecimais podem começar com 0x
ou 0X
e os números com zero à esquerda podem ser tratados como valores octais.
Forma | interpretação |
---|---|
'um' ou 'A' | ponto flutuante hexadecimal |
'e' ou 'E' | notação científica (ponto flutuante) |
'f' ou 'F' | notação fixa (ponto flutuante) |
'g' ou 'G' | forma geral (fixa ou científica) |
'd' | decimal |
'eu' | gramática std::from_chars com base == 10 |
'o' | octal (sem sinal) |
'você' | decimal (sem sinal) |
'x' ou 'X' | hexadecimal (sem sinal) |
argparse
fornece argumentos e ações predefinidos para -h
/ --help
e -v
/ --version
. Por padrão, essas ações sairão do programa após exibir uma mensagem de ajuda ou de versão, respectivamente. Esta saída não chama destruidores, ignorando a limpeza dos recursos obtidos.
Esses argumentos padrão podem ser desabilitados durante a criação ArgumentParser
para que você possa lidar com esses argumentos à sua maneira. (Observe que o nome e a versão do programa devem ser incluídos ao escolher os argumentos padrão.)
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 );
O trecho de código acima gera uma mensagem de ajuda e continua em execução. Não suporta um argumento --version
.
O padrão é default_arguments::all
para argumentos incluídos. Nenhum argumento padrão será adicionado com default_arguments::none
. default_arguments::help
e default_arguments::version
adicionarão individualmente --help
e --version
.
Os argumentos padrão podem ser usados ao desabilitar a saída padrão com esses argumentos. Este quarto argumento para ArgumentParser
( exit_on_default_arguments
) é um sinalizador bool com um valor verdadeiro padrão. A chamada a seguir manterá --help
e --version
, mas não será encerrada quando esses argumentos forem usados.
argparse::ArgumentParser program ( " test " , " 1.0 " , default_arguments::all, false )
argparse
suporta a coleta de argumentos "restantes" no final do comando, por exemplo, para uso em um compilador:
foo@bar:/home/dev/ $ compiler file1 file2 file3
Para habilitar isso, basta criar um argumento e marcá-lo como remaining
. Todos os argumentos restantes passados para argparse estão reunidos aqui.
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;
}
Quando nenhum argumento é fornecido:
foo@bar:/home/dev/ $ ./compiler
No files provided
e quando vários argumentos são fornecidos:
foo@bar:/home/dev/ $ ./compiler foo.txt bar.txt baz.txt
3 files provided
foo.txt
bar.txt
baz.txt
O processo de reunir os argumentos restantes também funciona bem com argumentos opcionais: