Parsing C++ command line arguments with optional parameters using optionparser.h - c++

All the examples in the optionparser.h (Lean Mean C++ Option Parser) file look to not use any parameters to options. And I readily admit I'm not a brilliant C++ programmer. I just wand to have some options that take a numeric parameter.
Something like [--help --count_to 5 --jump 3 --noecho].
Here is a clip from the file on usage:
enum optionIndex { UNKNOWN, HELP, PLUS, SKIP1, SKIP2};
const option::Descriptor usage[] =
{
{UNKNOWN, 0, "", "",option::Arg::None, "USAGE: MyPgm [options]\n\n" "Options:" },
{HELP, 0,"h", "help",option::Arg::None, " --help -h \tPrint usage and exit." },
{PLUS, 0,"p","plus",option::Arg::None, " --plus, -p \tIncrement count." },
{SKIP1, 0,"","nodcs",option::Arg::None, " --nodcs, \tMy Option1" },
{SKIP2, 0,"","noacs",option::Arg::None, " --noacs, \tMy Option2" },
};
int main(int argc, char* argv[])
{
argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present
option::Stats stats(usage, argc, argv);
option::Option options[stats.options_max], buffer[stats.buffer_max];
option::Parser parse(usage, argc, argv, options, buffer);
if (parse.error())
return 1;
if (options[HELP] || argc == 0) {
option::printUsage(std::cout, usage);
return 0;
}
std::cout << "--plus count: " <<
options[PLUS].count() << "\n";
for (option::Option* opt = options[UNKNOWN]; opt; opt = opt->next())
std::cout << "Unknown option: " << opt->name << "\n";
for (int i = 0; i < parse.nonOptionsCount(); ++i)
std::cout << "Non-option #" << i << ": " << parse.nonOption(i) << "\n";
}
I see that option::Arg can be NONE or Optional, but I am at a loss exactly
how to reference an optional argument. I am also certain that this code can do much more than I am asking it to do at this time.

Related

GNU argp "too few arguments"

My program is supposed to take two required arguments, and three optional arguments, like follows
ATE <input file> <output file> [--threads] [--bass] [--treble]
(note, I haven't figured out how to take <required> arguments yet, so input and output file is defined in the code as -i input_file and -o output_file)
I'm using the GNU library argp to parse the command line arguments, my file is based off the third example.
I run my program using the following command
$ ./ATE -i input_file.pcm -o output_file.pcm
Too few arguments!
Usage: ATE [OPTION...]
-p AMOUNT_OF_THREADS -b BASS_INTENSITY -t TREBLE_INTENSITY
input_file.pcm output_file.pcm
Try `ATE --help' or `ATE --usage' for more information.
threads: 2, bass: 4, treble: 4
opening file input.pcm
RUNNING!
done, saving to out.pcm
When running my program, I get "too few arguments", even though argp succesfully parsed the input and output option, as you can see in the output.
Printing out the number of arguments in parse_opt, cout << state->arg_num << endl; gives me 0's at every call.
The code is a little long, but it's completely self-contained so you can compile it to see for yourself.
commands.cpp
using namespace std;
#include <stdlib.h>
#include <argp.h>
#include <iostream>
#include <string>
#include <errno.h>
struct arguments {
string input_file;
string output_file;
int threads;
int bass;
int treble;
};
static char doc[] = "Parallequaliser - a multithreaded equaliser application written in c++";
static char args_doc[] = "-p AMOUNT_OF_THREADS -b BASS_INTENSITY -t TREBLE_INTENSITY input_file.pcm output_file.pcm";
static struct argp_option options[] = {
{"input_file", 'i', "IN_FILE", 0, "an input file in pcm format"},
{"output_file", 'o', "OUT_FILE", 0, "an output file in pcm format"},
{"threads", 'p', "AMOUNT_OF_THREADS", OPTION_ARG_OPTIONAL, "amount of threads, min 2"},
{"bass", 'b', "BASS_INTENSITY", OPTION_ARG_OPTIONAL, "bass intensity, from 0 to 7"},
{"treble", 't', "TREBLE_INTENSITY", OPTION_ARG_OPTIONAL, "treble intensity, from 0 to 7"},
{0}
};
static error_t parse_opt (int key, char *arg, struct argp_state *state) {
struct arguments *arguments = (struct arguments *) state->input;
switch (key) {
case 'p':
if (arg == NULL) {
arguments->threads = 4;
} else {
arguments->threads = strtol(arg, NULL, 10);
}
break;
case 'b':
if (arg == NULL) {
arguments->bass = 4;
} else {
arguments->bass = strtol(arg, NULL, 10);
}
break;
case 't':
if (arg == NULL) {
arguments->treble = 4;
} else {
arguments->treble = strtol(arg, NULL, 10);
}
break;
case 'i':
if (arg == NULL) {
cout << "You forgot to specify the input file using the -i input_file.pcm option" << endl;
} else {
arguments->input_file = (string) arg;
}
break;
case 'o':
if (arg == NULL) {
cout << "You forgot to specify the out file using the -i output_file.pcm option" << endl;
} else {
arguments->output_file = (string) arg;
}
break;
case ARGP_KEY_ARG:
cout << "Key arg... " << key << endl;
if (state->arg_num > 5){
cout << "Too many arguments!" << endl;
argp_usage(state);
}
break;
case ARGP_KEY_END:
if (state->arg_num < 2){
cout << "Too few arguments!" << endl;
argp_usage(state);
}
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = { options, parse_opt, args_doc, doc };
int main (int argc, char **argv) {
struct arguments arguments;
arguments.threads = 2;
arguments.bass = 4;
arguments.treble = 4;
argp_parse(&argp, argc, argv, ARGP_NO_EXIT, 0, &arguments);
cout << "threads: " << arguments.threads << ", bass: " << arguments.bass << ", treble: " << arguments.treble << endl;
cout << "opening file " << arguments.input_file << endl;
cout << "RUNNING!" << endl;
cout << "done, saving to " << arguments.output_file << endl;
return 0;
}
The options don't count as "arguments" for the context of the argp parser.
When running ./ATE -i input_file.pcm -o output_file.pcm, you have "too few arguments" because you reach ARGP_KEY_END, the end of the arguments, with no arguments left. arg_num represents the "stand-alone" arguments : the number of ARGP_KEY_ARG arguments that have been processed. You don't have any.
To make sure you have the two required arguments as you initialy wanted, check that you don't reach ARGP_KEY_END without having seen two arguments (like you are already doing : the too few arguments would mean you don't have your two filenames). The case ARGP_KEY_ARG is where you get the values of the arguments.

C++ equivalent of C fgets

I am looking to find a C++ fstream equivalent function of C fgets. I tried with get function of fstream but did not get what I wanted. The get function does not extract the delim character whereas the fgets function used to extract it. So, I wrote a code to insert this delim character from my code itself. But it is giving strange behaviour. Please see my sample code below;
#include <stdio.h>
#include <fstream>
#include <iostream>
int main(int argc, char **argv)
{
char str[256];
int len = 10;
std::cout << "Using C fgets function" << std::endl;
FILE * file = fopen("C:\\cpp\\write.txt", "r");
if(file == NULL){
std::cout << " Error opening file" << std::endl;
}
int count = 0;
while(!feof(file)){
char *result = fgets(str, len, file);
std::cout << result << std::endl ;
count++;
}
std::cout << "\nCount = " << count << std::endl;
fclose(file);
std::fstream fp("C:\\cpp\\write.txt", std::ios_base::in);
int iter_count = 0;
while(!fp.eof() && iter_count < 10){
fp.get(str, len,'\n');
int count = fp.gcount();
std::cout << "\nCurrent Count = " << count << std::endl;
if(count == 0){
//only new line character encountered
//adding newline character
str[1] = '\0';
str[0] = '\n';
fp.ignore(1, '\n');
//std::cout << fp.get(); //ignore new line character from stream
}
else if(count != (len -1) ){
//adding newline character
str[count + 1] = '\0';
str[count ] = '\n';
//std::cout << fp.get(); //ignore new line character from stream
fp.ignore(1, '\n');
//std::cout << "Adding new line \n";
}
std::cout << str << std::endl;
std::cout << " Stream State : Good: " << fp.good() << " Fail: " << fp.fail() << std::endl;
iter_count++;
}
std::cout << "\nCount = " << iter_count << std::endl;
fp.close();
return 0;
}
The txt file that I am using is write.txt with following content:
This is a new lines.
Now writing second
line
DONE
If you observe my program, I am using fgets function first and then using the get function on same file. In case of get function, the stream state goes bad.
Can anyone please point me out what is going wrong here?
UPDATED: I am now posting a simplest code which does not work at my end. If I dont care about the delim character for now and just read the entire file 10 characters at a time using getline:
void read_file_getline_no_insert(){
char str[256];
int len =10;
std::cout << "\nREAD_GETLINE_NO_INSERT FUNCITON\n" << std::endl;
std::fstream fp("C:\\cpp\\write.txt", std::ios_base::in);
int iter_count = 0;
while(!fp.eof() && iter_count < 10){
fp.getline(str, len,'\n');
int count = fp.gcount();
std::cout << "\nCurrent Count = " << count << std::endl;
std::cout << str << std::endl;
std::cout << " Stream State : Good: " << fp.good() << " Fail: " << fp.fail() << std::endl;
iter_count++;
}
std::cout << "\nCount = " << iter_count << std::endl;
fp.close();
}
int main(int argc, char **argv)
{
read_file_getline_no_insert();
return 0;
}
If wee see the output of above code:
READ_GETLINE_NO_INSERT FUNCITON
Current Count = 9
This is a
Stream State : Good: 0 Fail: 1
Current Count = 0
Stream State : Good: 0 Fail: 1
You would see that the state of stream goes Bad and the fail bit is set. I am unable to understand this behavior.
Rgds
Sapan
std::getline() will read a string from a stream, until it encounters a delimiter (newline by default).
Unlike fgets(), std::getline() discards the delimiter. But, also unlike fgets(), it will read the whole line (available memory permitting) since it works with a std::string rather than a char *. That makes it somewhat easier to use in practice.
All types derived from std::istream (which is the base class for all input streams) also have a member function called getline() which works a little more like fgets() - accepting a char * and a buffer size. It still discards the delimiter though.
The C++-specific options are overloaded functions (i.e. available in more than one version) so you need to read documentation to decide which one is appropriate to your needs.

Cast element of character array to integer, and print using cout

I have something in my opinion unexpected happening here.
int main(int argc, char* argv[]) {
cout << "argv[1] : " << argv[1] << endl;
cout << "(int)argv[1] : " << (int)argv[1] << endl;
}
When I call this:
$ ./a.out 1
The output is:
argv[1] : 1
(int)argv[1] : -1074470344
I would expect
argv[1] : 1
(int)argv[1] : 49
Since the ASCII code for '1' is 49.
What is happening here?
Remember that the type of argv is char* argv[], so argv[1] is not a single char, but a C-style string.
To print the first character, use argv[1][0].
std::cout << "(int)argv[1][0] : " << (int)argv[1][0] << std::endl;
argv[1] is not a char, it's a char *.
(int)argv[1][0] may be what you want, if you guarantee the argument will be only one character.
cout << "(int)argv[1][0] : " << (int)argv[1][0] << endl;
and you will get:
argv[1] : 1
(int)argv[1][0] : 49
NOTICE
If your argument is a string like "11", you will get a strange result such as:
argv[1] : 11
(int)argv[1][0] : 49
cout << "(int)argv[1] : " << (int)argv[1] << endl;
You are trying to cast argv[1] which is a char pointer, i.e. "C string". It could also contain "11", not just '1'.
You would need to "cast" the first letter, but I think it is not a good idea either way.
Furthermore, you have not checked against the argc whether you actually supplied the argument on the command line. That is another mistake unless you can make sure somehow it is never "misused".
Yet another mistake is not returning an integer, e.g. zero, at the end of the function since it should, otherwise you will get a warning from your compiler.
You could write this:
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
if (argc == 1)
return 1;
cout << "argv[1] : " << argv[1] << endl;
cout << "(int)argv[1][0] : " << (int)argv[1][0] << endl;
^^^ ^^^
return 0;
}
That being said, you probably want to use static_cast for this in a C++ program rather than low-level C-type cast.

Print help for both normal and positional args with boost::program_options

When you use Boost library program_options it is very easy to print help for your program:
boost::program_options::variables_map options;
boost::program_options::options_description optionsDesc;
boost::program_options::positional_options_description positionalOptionsDesc;
//...
if(options.count("help"))
{
cerr << optionsDesc << endl;
}
But how do you add the options from positional_options_description to the help message? In the tutorial I can see the output of such set-up, at the end of the section:
http://www.boost.org/doc/libs/1_52_0/doc/html/program_options/tutorial.html#id2607297
The option input-file is printed in help and it is positional. But I can't see the code.
Is there an build-in way to print it, like with options_description or you have to do it manually? Apparently the << does not work for positional_options_description, the compilation error is:
error: cannot bind ‘std::ostream {aka std::basic_ostream<char>}’ lvalue to ‘std::basic_ostream<char>&&’
Notice that streaming description only prints out the options. It does not print the name of the program or the actual description of what the program does. You should manually print any positional parameter you have as part of the output message:
Instead of
if (vm.count("help")) {
cout << "Usage: options_description [options]\n";
cout << desc;
return 0;
}
You could easily say
if (vm.count("help")) {
cout << "Usage: " << argv[0] << " [options] <description of positional 1> <description of positional 2> ...\n";
cout << desc;
return 0;
}
Have a look at boost::program_options::positional_options_description.name_for_position(i)
The error message is something unrelated, I forget what eactly something to do with cpp11
This is what I do for automatically printing positional options:
void printUsage(const std::string &argv0)
{
std::ostream &os = std::cout;
os << "Usage:" << std::endl;
// print only basename of argv[0]
boost::filesystem::path p(argv0);
os << " " << p.filename().string();
os << " [options]";
std::string last = "";
int rep = 0;
for(int i = 0; i < positional_options_description_.max_total_count(); i++)
{
const std::string &n = positional_options_description_.name_for_position(i);
if(n == last)
{
if(!rep) os << " ...";
if(rep++ > 1000) break;
}
else
{
os << " " << n;
last = n;
rep = 0;
}
}
os << std::endl << std::endl;
os << options_description_ << std::endl;
}
The logic for checking repeated argument names is needed only if you have repeated options that can repeat an infinite number of times, i.e. with count equal to -1, otherwise you can simplify this a little bit, e.g. replace the if... else if ... with os << " " << n;.
With current (1.68) version of boost, there is no way of telling if an option description is positional or not, so there is nothing to do to improve the help, for example, excluding positional options from being printed.

How do I use getopt_long to parse multiple arguments?

#include <iostream>
#include <getopt.h>
#define no_argument 0
#define required_argument 1
#define optional_argument 2
int main(int argc, char * argv[])
{
std::cout << "Hello" << std::endl;
const struct option longopts[] =
{
{"version", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"stuff", required_argument, 0, 's'},
{0,0,0,0},
};
int index;
int iarg=0;
//turn off getopt error message
opterr=1;
while(iarg != -1)
{
iarg = getopt_long(argc, argv, "s:vh", longopts, &index);
switch (iarg)
{
case 'h':
std::cout << "You hit help" << std::endl;
break;
case 'v':
std::cout << "You hit version" << std::endl;
break;
case 's':
std::cout << "You hit stuff" << std::endl;
if(optarg)
std::cout << "Your argument(s): " << optarg << std::endl;
break;
}
}
std::cout << "GoodBye!" << std::endl;
return 0;
}
Desired output:
./a.out --stuff someArg1 someArg2
Hello
You hit stuff
Your agument(s): someArg1 someArg2
GoodBye!
getopt returns -1 when all option args have been processed. The --stuff is recognized as an option that takes an argument, in this case someArg1. The someArg2 arg does not start with - or --, so it is not an option. By default, this will be permuted to the end of argv. After getopt returns -1, all non-option args will be in argv from optind to argc-1:
while (iarg != -1) {
iarg = getopt_long(argc, argv, "s:vh", longopts, &index);
// ...
}
for (int i = optind; i < argc; i++) {
cout << "non-option arg: " << argv[i] << std::endl;
}
If you add a single - to the start of optstring, getopt will return 1 (not '1') and point optarg to the non-option parameter:
while (iarg != -1) {
iarg = getopt_long(argc, argv, "-s:vh", longopts, &index);
switch (iarg)
{
// ...
case 1:
std::cout << "You hit a non-option arg:" << optarg << std::endl;
break;
}
}
In the line ./a.out --stuff someArg1 someArg2 the shell interprets three arguments to a.out. You want the shell to interpret "someArg1 someArg2" as one argument - so put the words in quotes:
./a.out --stuff "someArg1 someArg2"
I'm working on windows, so I had to compile getopt and getopt_long from this excellent source
I modified getopt_long.c (below) to accommodate two input arguments. I didn't bother with the more general case of multiple arguments since that would require more (and cleaner) rework than I had time/need for. The second argument is placed in another global, "optarg2".
If you don't need to compile getopt from source, Frank's answer above is more elegant.
extern char * optarg2
.
.
.
int getopt_long(nargc, nargv, options, long_options, index)
{
.
.
.
if (long_options[match].has_arg == required_argument ||
long_options[match].has_arg == optional_argument ||
long_options[match].has_arg == two_req_arguments) {
if (has_equal)
optarg = has_equal;
else
optarg = nargv[optind++];
if (long_options[match].has_arg == two_req_arguments) {
optarg2 = nargv[optind++];
}
}
if ((long_options[match].has_arg == required_argument ||
long_options[match].has_arg == two_req_arguments)
&& (optarg == NULL)) {
/*
* Missing argument, leading :
* indicates no error should be generated
*/
if ((opterr) && (*options != ':'))
(void)fprintf(stderr,
"%s: option requires an argument -- %s\n",
__progname(nargv[0]), current_argv);
return (BADARG);
}
if ((long_options[match].has_arg == two_req_arguments)
&& (optarg2 == NULL)) {
/*
* Missing 2nd argument, leading :
* indicates no error should be generated
*/
if ((opterr) && (*options != ':'))
(void)fprintf(stderr,
"%s: option requires 2nd argument -- %s\n",
__progname(nargv[0]), current_argv);
return (BADARG);
}
You'll also need to add a define in getopt.h for "two_required_args" or "multiple_args" as you see fit.
edit: I'm bad at markdown
optarg points to "someArg1" and argv[optind] is "someArg2" if it exists and is not an option. You can simply use it and then consume it by incrementing optind.
case 's':
std::cout << "You hit stuff" << std::endl;
if (optind < argc && argv[optind][0] != '-') {
std::cout << "Your argument(s): " << optarg << argv[optind] << std::endl;
optind++;
} else {
printusage();
}
break;
Note this can work for an arbitrary number of arguments:
case 's':
std::cout << "You hit stuff." << std::endl;
std::cout << "Your arguments:" std::endl << optarg << std::endl;
while (optind < argc && argv[optind][0] != '-') {
std::cout << argv[optind] << std::endl;
optind++;
}
break;
c++ getopt-long command-line-arguments