Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Object Oriented Command Line Parser for C++ Similar to Windows Script Arguments Object

4.00/5 (5 votes)
21 Mar 2009CPOL7 min read 31.4K   447  
It is very much like WScript.arguments plus it keeps track of how the named arguments (options) are used.

Introduction

I see there are several articles on this subject. At the time I needed an object oriented command line parser (about 2 years ago), there were less of them. I remember I liked C#/.NET Command Line Arguments Parser. I used it and improved it a bit. (Hmm, maybe I should post this too.) But I needed something for C++. I do not say it didn't exist, just that I got tired of searching, on CodeProject and elsewhere, and started programming, and I haven't got tired of programming soon enough to switch back to searching. ;)

My notion of it was that is should be something like WScript.arguments object, something I was used to, since I used it a lot in my WSH (i.e. JScript and VBcript) scripts, and kind of liked it.

It is really very much like WScript.arguments plus it keeps track of how the named arguments (options) are used. 

I hope this makes it different enough from other parsers for C++ to be interesting and that it will find its users.

Description

Like in WScript.arguments the option names and its values have to be separated with ':' (but unlike WSH, it does not have to be : but any character of your choice):

/option1:its_value

or

/option2=its_value

etc., but not with spaces or tabs. This is not supported:

argument1 argument2 /option1 its_value /option2 its_value

Just like in WSH, the options may be what we could call simple, i.e. without value:

/option1 /option2

The options (named arguments) can be given in any order and can be mixed with (unnamed) arguments. Long options (i.e. the ones beginning with 2 or more characters, like with --) are not supported.

Like WScript.arguments, this parser in the time of its instantiation does not demand any information about desired command line arguments. That's why I added keeping track of named arguments usage. Any time you can retrieve the number or named arguments not yet accessed, and the list of them.

It is actually quite a natural approach. Why would we have to tell the parser what options we expect when it can conclude that itself by watching us using that option in our code???

It serves as some kind of additional debugging information too. We are no more warned only when the code is trying to use some options that are not given on the command line, but the other way round too.

I hope people will find this approach handy and interesting.

It's other most important characteristics:

  • It's a template class that can be used with char or wchar_t.
  • When used with wchar_t, it works flawlessly with Unicode characters, I test it all the time. (Printing them properly to the Windows console is another matter. One way to do it is to print directly to console using functions from conio.h like _tcprintf. For the other way, see my article on this matter Unicode output to the Windows console.) 
  • If you compile it on Linux, you should of course use char. (Thanks to the consistent usage of UTF-8, Linux has no problems with Unicode characters so, if you want to print them out to the console, simply using printf or cout will do the job.)
  • It is so similar to WScript.arguments that, if you have any experience with WScript.arguments, you can start using it right away. There are some additional features (keeping track of named arguments usage), but you can study them if and when you will need them.   
  • It can be used on Windows non-console applications, i.e. those that have _tWinMain (i.e. wWinMain or WinMain) function instead of _tmain (i.e. wmain or main) function. Can be instantiated anywhere in the program, not only in _tmain function.
  • You can choose whether the options should begin with '/' or '-', or any other character.
  • You can choose whether the options names should be separated from their values with ':' or '=', or any other character.

Usage

Note: I explained everything as you would use TCHAR. (_TCHAR and TCHAR are the same thing. I sometimes write TCHAR and sometimes _TCHAR. I apologize for confusing you by this.) Therefore strings are written as _T("foo"). But you can of course use char or wchar_t directly. In that case, string literals would be written as "foo" and L"foo" respectively.

You will probably first try to use the parser inside of the example project. To use it in your project, you will of course have to put my header files into your project folder or somewhere in the include path of your compiler. 

Instantiating

There are 3 classes:

  • CStlCmdLineArgs
  • CArgList 
  • CStlCmdLineArgsWin

CStlCmdLineArgs is the very parser. It needs argc and argv to be passed as arguments in the constructor. CArgList creates that argument using the Windows function GetCommandLine and CStlCmdLineArgsWin connects them by inheritance and containment respectively, so that you can instantiate the parser in the most easy way. Having included CStlCmdLineArgsWin_4.h

C++
CStlCmdLineArgsWin<TCHAR> cma;  

Since you are not dependent on argv and argc, you can instantiate it this way easily anywhere in the code. It is the most recommended way for Windows applications.

On Linux, CArgList makes no sense so you must instantiate CStlCmdLineArgs directly using argc and argv. This approach can also be used in Windows command line applications. On Linux, the template argument should be char instead of TCHAR. Having included CStlCmdLineArgs4.h:  

C++
CStlCmdLineArgs<TCHAR> cma(argc, argv); 

The Very Basic Usage

You use them just like WScript.arguments. The only differences are that in WScript.arguments, you would use round brackets and here you use square brackets:

You can access plain arguments (i.e., unnamed arguments) with:  

C++
cma.unnamed[0]  
C++
cma.unnamed[1]  

etc.

... and that, unlike WScript.arguments.unnamed, cma.unnamed starts with the 0th command line argument so that cma.unnamed[0] gives you what you would get retrieving argv[0] in the _tmain function.  

If the index is out of bounds, i.e. if you try to access more arguments than present on the command line, it will simply return NULL

You can access options (i.e., named arguments) with: 

C++
cma.named[_T("name_of_your_option")]

If the particular option is not present on the command line, it will return NULL.

If the option is given without a value, it will return empty string. This is how you check if a "simple" option, i.e. an option that does not require a value is present.

You Can Also

See how many unnamed arguments were on the command line with (something you will need in every program): 

C++
cma.unnamed.size()

See how many named arguments were on the command line with (something you will probably never use ;): 

C++
cma.named.size() 

Advanced Use - If You Want to Give the User More Precise Error Messages

This is something WScript.arguments hasn't got. The class keeps track of named arguments you access. The information about arguments that were present on the command line and are not accessed can be retrieved with: 

C++
cma.unusednamed.size() 

That will give you their number, and...

C++
cma.unusednamed.toString()  

... that will give you all of them in a single string, quoted and separated by spaces.

There is More

Till now, we have treated command line options like in Windows Script: they all begin with '/' and ':' separates values from names. This is the default. But class constructors accept additional arguments, so that we can for example do this: 

C++
CStlCmdLineArgsWin<TCHAR> cma('-', '='); 

Now the command line options are expected to begin with '-' and '=' will separate its names from its values. 

Let's say you have chosen the default and you need to have an unnamed argument beginning with '/', say /arg1, you can escape it like this: /:/arg1. If you have chosen '-' and have an argument beginning with '-', you would escape it like this: -:-arg1.

History

  • 21st March, 2009: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)