How to parse command line parameters.

A simple tutorial on command line parameters, as some people may not know how it is done.

-sec "What's the point?"
The point is that with command-line parameters you have no need of a human being to invoke your program -- this way, you can have, for example, a calculator with a GUI. The user opens the GUI and types in some commands. The GUI's executable invokes the calculator, for example, like this: calc add 5 5, calc.exe returns 10. Then the GUI can display "10" in a text box. "But you can just use a separate function" I hear you say. Well yes, in this case, you could just have an "add" function. But what if your user is using a text-only display or has a very old computer? They won't be able to use the program you spent two months designing. In the case of a calculator, you could say "But who cares? There are numerous calculators". True; but for something more complex it would be better to have two executables.

Another advantage is that people don't need your source code to include your file with their project. It would certainly be easier; but unnecessary.

Yet another is that it enables you to write in two different programming languages without the need for embedding. In C++ it is possible to embed languages like ASM or Python (I'm not sure how to embed Python, but there's a asm("ASMCommand"); command), but personally I think it better to create a seperate file, as with ASM it would take too long to type out a large piece of code into the asm() function -- you'd have to put \n\ at the end of every line which would be difficult to read; and also because you can't be sure the person reading your code even knows ASM. It may confuse them. So for readability and practicality it would be better to have two executables. Assembler's excellent execution time would not be so evident in a calculator, but as I say, for large projects it's definately worth having it.

There are more that I won't go into right now.

-sec "So how do you do it?"

It is very easy to use command line parameters once you know how. First of all you must declare main() with the following parameters: (int argc, char* argv[]).
Why argc and argv[]? Well really, you could name them anything -- argc and argv[] are simply naming conventions. But they do have meaning -- argument count and argument vector.

--subsec "Argument Count:"

argc contains the amount of arguments, separated by spaces, that were entered when the program was invoked. Let's say I have written a program to move file at arbitrary location x to location y. Let's say I have compiled and linked it as movefile (.exe on windows). If, for example, I opened up a terminal, and typed "movefile <filename> <frompath> <topath>"; then argc would be 4.

--subsec "Argument Vector:"

argv contains the actual arguments themselves. In argv[0] will always be the command used to invoke the program (thanks, Disch). If, for example, movefile was stored in C:\MyPrograms\ or /home/myprograms, and the current working directory (the folder your terminal is in, e.g. "C:\Users\Name>" or "name@workstation:~/home/name$" you would have to type in "C:\MyPrograms\movefile myfile mypath myotherpath" or "/home/myprograms/movefile myfile mypath myotherpath". That is what argv[0] will contain. argv[1] would be "myfile", etc.

-sec Switches:
Switches are sent to your programs as arguments themselves, but there are certain advantages to using a switch over having plain arguments. One such advantage is that you can figure out where your argument variables are. If you do not have any switches, you either have to rely on the user (big mistake) of the program to know the syntax (which would be incredibly frustrating for them), or use some kind of regular expression, which would be incredibly frustrating for you. So using a switch is definately worth it. With a switch, you can do this: "movefile -f myfile -p mypath -o myotherpath". Then you can easily find out where your arguments are:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (i + 1 != argc) // Check that we haven't finished parsing already
    if (argv[i] == "-f") {
        // We know the next argument *should* be the filename:
        myFile = argv[i + 1];
    } else if (argv[i] == "-p") {
        myPath = argv[i + 1];
    } else if (argv[i] == "-o") {
        myOutPath = argv[i + 1];
    } else {
        std::cout << "Not enough or invalid arguments, please try again.\n";
        Sleep(2000);
                 /*
                  *  Sleep for 2 seconds to allow user (if any) to read above statement. 
                  *  The issue with this is that if we're a backend program to a GUI as mentioned above; 
                  *  that program would also sleep for 2 seconds. Most programs don't
                  *  have this - the console will keep the text there until it scrolls off the page or whatever, so you may aswell leave it out.
                  ***/
        exit(0);
    }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>

// When passing char arrays as parameters they must be pointers
int main(int argc, char* argv[]) {
    if (argc < 5) { // Check the value of argc. If not enough parameters have been passed, inform user and exit.
        std::cout << "Usage is -in <infile> -out <outdir>\n"; // Inform the user of how to use the program
        std::cin.get();
        exit(0);
    } else { // if we got enough parameters...
        char* myFile, myPath, myOutPath;
        std::cout << argv[0];
        for (int i = 1; i < argc; i++) { /* We will iterate over argv[] to get the parameters stored inside.
                                          * Note that we're starting on 1 because we don't need to know the 
                                          * path of the program, which is stored in argv[0] */
            if (i + 1 != argc) // Check that we haven't finished parsing already
                if (argv[i] == "-f") {
                    // We know the next argument *should* be the filename:
                    myFile = argv[i + 1];
                } else if (argv[i] == "-p") {
                    myPath = argv[i + 1];
                } else if (argv[i] == "-o") {
                    myOutPath = argv[i + 1];
                } else {
                    std::cout << "Not enough or invalid arguments, please try again.\n";
                    Sleep(2000); 
                    exit(0);
            }
            std::cout << argv[i] << " ";
        }
        //... some more code
        std::cin.get();
        return 0;
    }
}


-sec "Words with a space:"
Where arguments or paths with a space key must be used (e.g. C:\My Path\SomeDocument or /home/some path/somedocument" the argument must be "enclosed in quotes".

-epilogue "\
Hopefully now you can understand what is going on with the whole arguments/parameters thing; and also how I titled each section :)
"
-command exit
Last edited on
Also, I would check to make sure that i+1 != argc, if they forgot to add the argument.
Good idea. Thanks :)
The reason it would be three is because argv[0] is the path to your program.


Technically... I think argv[0] is the command used to invoke the program. This may or may not include be the full path depending on how the program was invoked.
Actually that's a good point -- I was playing around earlier and if you type, from command line "movefile.exe -in infile.txt -out outdir" argv[0] is "movefile.exe" not "C:\SomeLocation\movefile.exe".
 
argv[1] == "-in"

That actually works for C string comparison? Don't you need to use std::strcmp or an equivalent function?
Obviously not, but it may be better to use it. I don't use C strings often, so I tend to accidentally the string functions.
I always use operator==, and it always works for me. I was thinking of rewriting this for formatting and wording.
You could also just go:

string(argv[i]) == "stuff"

But you already knew that. ;)
I didn't know you could do that with strings; I thought it was just with the basic data types (e.g. int(myFloat) strips the decimal and the numbers after it... which sucks because before I knew about it I spent half an hour writing a function to do that... it worked, too...)

Anyway; I rewrote the article,
U can do C++-style function conversions(casts) such as:
string(argv[i]) == "stuff"
if the object or data type you are trying to cast TO(in our case std::string) has a defined constructor that takes the kind of data you are trying to cast FROM as a parameter(in our case char*).

And std::string DOES have a defined constructor that takes char* as a parameter. The reason casting works for all the basic types is because all of them are more or less numeric values.

Basically you could cast anything into anything as long as there's a defined method to do it.
Last edited on
Hm, but I didn't know you could cast std::string and the primitive types...
So the process was foreign to you all togheter? It's hard to believe you never did stuff like int(3.6) or char(97). Because that's casting. Or at least that's how i know the process is called.
In the original post you said
The GUI's executable invokes the calculator, for example, like this: calc add 5 5, calc.exe returns 10. Then the GUI can display "10" in a text box.

does this have to be done through calling system("program_name") and piping the output or is there some other way. If not how do you pipe it.
I don't think so, I think you would use fork() and execl(), then use pipe() (on Linux) or use CreateProcess or ShellExecute[ex] in windows.
Thanks
LolFactor I know what casting is, I just never used it between objects and primitives, like this std::string(myChar)
Weirdly in C it's like this: (int)myVariableThatIsntAnInt;
Actually, std::string(...) is not a cast, is it creating a temporary unnamed std::string using the constructor. It just so happens that function style casting int(10.0) looks the same.
oh, ok.
Topic archived. No new replies allowed.