C++ accepting command line argument with "-" symbol - c++

I am new to c++ and trying to read command line arguments specified as below.
./helloworld -i input_file -o outputfile -s flag3 -t flag4
I tried hardcoding the flags by index as below
int main(int argc, char *argv[]) {
// argv[1] corresponds to -i
// argv[2] corresponds to input_file
// argv[3] corresponds to -o
// argv[4] corresponds to outputfile
// argv[5] corresponds to -s
// argv[6] corresponds to flag3
// argv[7] corresponds to -t
// argv[8] corresponds to flag4
}
Then i realized the order can be changed so I can't use hardcoded index, I used a
unordered_map<string, string> to put the -i, -o, -s, -t as keys and inputfile, outputfile, flag3, flag4 as values.
This is working fine, but I was wondering is there any better way to do the same.

Oh my gosh. Okay, you can do this manually, and I'll show you some code. But please look at getopt(). It already helps you out quite a bit, but it takes a little to get used to.
But here's how you could code it manually:
int index = 1;
while (index < argc) {
string cmnd = argv[index++];
if (cmnd == "-i") {
if (index >= argc) {
usage(); // This should provide help on calling your program.
exit(1);
}
inputFileName = argv[index++];
}
else if (cmnd == "-whatever") {
// Continue to process all your other options the same way
}
}
Now, this isn't how anyone does this. We use some version of getopt(). There's another one I like called getopt_long, I believe. You'll want to dig something up like that. Then I put my own wrapper around all of that so I can do some really cool things.
If you want to see the wrapper I use: https://github.com/jplflyer/ShowLib.git and look at the OptionHandler.h and .cpp. It's pretty cool. I think there's an example of how to use it somewhere.
But you need to know how it works under the hood, so for your first programs, maybe do it manually like I've shown you.

You can use a 3rdparty library to parsing commandline arguments.
For example: https://github.com/mirror/tclap

Related

Extracting a function body by name with BASH and regex

I have some automatically generated code from MATLAB coder. I would like to make a script to find my entries out of large file. I've successfully plowed my way through regex with BASH to get the main function main\( *([^)]+?)\), and then the body with /\{([^}]+)\}/; however, I'm having a terrible time glueing those together. All I need is the function names contained in main().
I realize that this could be a terrible exercise, but the automatically generated code gives me simple functions that looks like:
int main(int argc, const char * const argv[])
{
(void)argc;
(void)argv;
/* Initialize the application. You do not need to do this more than one time. */
RT_initialize();
/* Invoke the entry-point functions. You can call entry-point functions multiple times. */
main_RT();
/* Terminate the application. You do not need to do this more than one time. */
RT_terminate();
return 0;
}
I would like to extract the that function and body, but my regex is poorer than I recalled.
Any guidance would be greatly appreciated.
A simple way to fairly reliably extract the entire function body is to run the code through a formatter first:
indent -kr < mymain.c | sed -n 's/^int main(/,/^}/p'
cflow can give you a function call graph. eg:
cflow -d2 mymain.c
Due to some restrictions to being on BSD, the resulting BASH function follows to get the function body from a C source for a function by name. This was only tested with the well-formatted C code from MATLAB's Coder.
function getFunctionInC(){
TMPFILEIDENT="/tmp/indent.$$.tmp" #temp file
indent "$1" $TMPFILEIDENT
cat $TMPFILEIDENT | awk '
BEGIN { state = 0; last = ""; }
$0 ~ /^'$2'\(/ { print last; state = 1; }
{ if (state == 1) print; }
$0 ~ /^}/ { if (state) state = 2; }
{ last = $0; }
'
}
The formatting is terrible on the outputs, but I can easily pull the function names to dynamically create defines. Thanks to everyone who read the question.

Using bool values in a command-line program. Can't find a way to define the flags and check if they are in the parameters

I tried to look for my problem all over stackoverflow, and found nothing similar. I am working an an assignment that requires me to go through each argument, and if it is a text file, to output the textfile's length for each argument given.
The main part of the function I have no trouble with. The only problem I have is that we have to have certain flag (denoted as '-c'), which if the flag was in the argument, it would change the behavior of the main program. For instance, '-c' would just output the contents of the textfiles instead of printing out it's length.
I understand that they way to go by this using boolean values, seeing if the flag is in the argument or not. However, no matter what method I try, my compiler keeps coming up with this mysterious expected unqualified id.
My code begins with
int main(int argc, char *argv[]){
for ( i = 1; 1 < argc; i++) // iterating through each textfiles
}
I want the program to see if argv[i] is the flag that I defined, but whatever method I try to implement the flag, I always get this error.
bool isflag (string -c)
or
bool -c;
-c = true;
if (isflag){
...
}
And none of these work. I assume it has something to do with the dash character. I'm just really in a hunch and I have no idea what to do to solve this.
You seem to be having a bit of confusion between the name of a variable, and the value stored in that variable. Here's a fragment of C++ code showing how you can check strings against command line arguments, and set boolean flags if they match. It assumes "-c" will be the first command line argument, if it's present at all.
int main(int argc, char *argv[])
{
std::string argument = "-c"; /* the string you're looking for */
bool flag = false; /* Was the string found? */
int filename_start_position = 1; /* Where do the filenames start in the argument list? */
/* check first argument to see if it's the -c option */
if (argument == argv[1]) /* note, using == for string comparison only works if at least one of the arguments is a std::string */
{
flag = true; /* -c option found, set the flag */
filename_start_position = 2; /*no need to process argv[1] further */
}
/* Process remaining arguments */
for(int i = filename_start_position; i < argc; i++)
{
if (flag)
{
/* print contents of file argv[i] */
}
else
{
/* calculate length of file argv[i] */
}
}
}

How do I create a --help option in a command-line program in c/c++?

I'm pretty sure it's simple. Is there a pre-defined header for create a help context in a command line program.
$ program --help
would provide a list of various help options.
The simplest way to do it in c++ is:
int main(int argc, char** argv)
{
if(argc == 2 && strcmp(argv[1], "--help")==0)
{..print help here..}
return 0;
}
For C++, you have Boost.Program_options
http://www.boost.org/doc/libs/1_55_0/doc/html/program_options.html
But you'll have to bring the whole boost library (which can be tedious, the first time you do it).
Something along these lines...
int main(int argc, char* argv[])
{
std::vector<std::string> cmdLineArgs(argv, argv+argc);
for(auto& arg : cmdLineArgs)
{
if(arg == "--help" || arg == "-help")
{
std::cout << "Helpful stuff here\n";
}
else if(arg == "whatever")
{
std::cout << "whatever?!\n";
}
}
}
Of course, there are libraries to handle cmd line arguments. But for simple stuff it's really not hard to do yourself.
I think you should have a look to this library : Getopt which is part of the GNU C Library. It allows you to parse -like parameters efficiently.
You could do it multiple ways depending on how you want to go about it.
You could use strcmp() and just parse argv[1]:
if(strcmp(argv[1],"--help") == 0)
or you can use getopt if you are running linux:
http://www.gnu.org/software/libc/manual/html_node/Getopt.html
getopt_long is your friend.
For one-character options, getopt will suffice.

Is there are a way to find out whether the input is streaming into the program from a file or not?

In Windows, there exists a console trick
someprogram.exe < input.txt
which makes the program to get the input from input.txt whenever there is a input request.
I want my program to behave differently when the input is read from another file. Is there are a way to do that? How?
I don't think so(not sure though), but here is an alternative (error checking omitted):
int main(int argc, char **argv)
{
std::istream * pstream = &std::cin;
std::ifstream fin;
if (argc > 1)
{
fin.open(argv[1]);
pstream = &fin;
}
// use pstream instead of cin
}
Then you pass the name of the file as a command line argument.
Yes, use the function isatty available on most platforms. Looks like it is now called _isatty in windows (not sure why).

Why can't QFile read from the "~" directory?

I've tried the following short example to find out about a bug in a bigger program I am working on. It looks like QFile doesn't support unix (or the shell's) notation for the home directory:
#include <QFile>
#include <QDebug>
int main()
{
QFile f("~/.vimrc");
if (f.open(QIODevice::ReadOnly))
{
qDebug() << f.readAll();
f.close();
}
else
{
qDebug() << f.error();
}
}
As soon as I replace the "~" with my real home directory path, it works. Is there an easy workaround - some setting to enable? Or do I have to go the "ugly" way and ask QDir for the home directory of the current user and prepend that manually to each path?
Addendum: It's clear that usually the shell performs the tilde expansion so programs would never see that. Still it is so convenient in unix shells that I hoped the Qt implementation for file access would have that expansion included.
You can just create a helper function to do this for you, something like:
QString morphFile(QString s) {
if ((s == "~") || (s.startsWith("~/"))) {
s.replace (0, 1, QDir::homePath());
}
return s;
}
:
QFile vimRc(morphFile("~/.vimrc"));
QFile homeDir(morphFile("~"));
A more complete solution, allowing for home directories of other users as well, may be:
QString morphFile(QString fspec) {
// Leave strings alone unless starting with tilde.
if (! fspec.startsWith("~")) return fspec;
// Special case for current user.
if ((fspec == "~") || (fspec.startsWith("~/"))) {
fspec.replace(0, 1, QDir::homePath());
return fspec;
}
// General case for any user. Get user name and length of it.
QString name (fspec);
name.replace(0, 1, ""); // Remove leading '~'.
int len = name.indexOf('/'); // Get name (up to first '/').
len = (len == -1)
? name.length()
: len - 1;
name = name.left(idx);
// Find that user in the password file, replace with home
// directory if found, then return it. You can also add a
// Windows-specific variant if needed.
struct passwd *pwent = getpwnam(name.toAscii().constData());
if (pwent != NULL)
fspec.replace(0, len+1, pwent->pw_dir);
return fspec;
}
Just one thing to keep in mind, the current solution is not portable to Windows (as per the comments in the code). I suspect this is okay for the immediate question since .vimrc indicates that's not the platform you're running on (it's _vimrc on Windows).
Tailoring the solution to that platform is possible, and indeed shows that the helper-function solution is a good fit since you'll only have to change one piece of code to add that.
It has nothing to do with not supporting UNIX; the expansion of tildes to the user's home directory is a substitution performed by the shell, so yes, you will have to manually replace them.
Please submit a suggestion to the Qt bugtracker.
https://bugreports.qt.io/
Take a look at the C library function glob, which will do tilde expansion (and possibly wildcard expansion and various other functions too).