├── .gitignore ├── LICENSE ├── README.md └── src ├── cpp_cli ├── args_parser_templates.h ├── cli_help.h ├── cpp_cli.h ├── template_definitions.h └── w_specialization.h ├── subcommand_example ├── bin │ └── Makefile ├── external_includes └── src │ └── subcommand_example.cpp ├── test_cpp_command_line_parser ├── bin │ └── Makefile ├── external_includes └── src │ └── test_cmd_library.cpp └── w_specialization_example ├── bin └── Makefile ├── external_includes └── src └── w_specialization.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*/ 3 | !*.cpp 4 | !*.cxx 5 | !*.h 6 | !*.c 7 | !*.hpp 8 | !.git* 9 | !Makefile 10 | !README.md 11 | !LICENSE 12 | release/* 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 TheLandfill 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpp_cli 2 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/7f047f6cbe31451f87096d6a64e277fa)](https://www.codacy.com/app/TheLandfill/cpp_cli?utm_source=github.com&utm_medium=referral&utm_content=TheLandfill/cpp_cli&utm_campaign=Badge_Grade) 3 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 4 | 5 | ## Quick Version 6 | 7 | If you just want to get something up and running, read here. 8 | 9 | ### How to Use 10 | 1. Download the latest release. 11 | 2. Include "cpp_cli.h" wherever you call any function of Parser. 12 | 3. Create a new scope (this is just so the local variables you need to create disappear, but it's not necessary). 13 | 4. Inside that scope, create your `Var`s, create your `Value`s, create your `Vector`s, add your subcommands, and set help information (syntax specified below). 14 | 5. If you want to generate a help message, run the function `Parser::generate_help(argv[0])`, which will create a text file with a help message. 15 | 6. Once you've created all the `Var`s, run the function `Parser::parse(argc, argv);`. 16 | 7. All the variables will be set after hash finishes. 17 | 8. Any non-flagged argument or ignored argument will be returned in a vector of "non_options" in the order in which they appear in the command line. 18 | 19 | ### Example Usage 20 | ```cpp 21 | #include "cpp_cli.h" 22 | 23 | void sample_subcommand(int argc, char ** argv, void * data); 24 | 25 | int main(int argc, char ** argv){ 26 | std::string filename = ""; 27 | int recursion_level = 0; 28 | std::string show_output = ""; 29 | bool standard_input = false; 30 | char c_version_of_string[20]; 31 | char file_type = '\0'; 32 | std::vector list_of_ints; 33 | std::vector non_options; 34 | 35 | using namespace cli; 36 | 37 | Parser p; 38 | 39 | // Set filename to the argument of "-o", "--output=", or "--out=" 40 | // These are variables with throwaway names. 41 | Var fv(filename, { "o", "output", "out" }, true); 42 | Var rlv(recursion_level, { "r", "recursion", "max_depth" }, true); 43 | 44 | // Set show_output to "show_output", "s", "no_out", or "half_out". 45 | Var sov(show_output, { "show_output", "s", "no_out", "half_out" }, false); 46 | 47 | // Used for arguments like "-vvvvv" for levels of verboseness 48 | Var vv(c_version_of_string, { "v" }, false, 20); 49 | 50 | Value siv(standard_input, { "-" }, true); 51 | Value ftfv(file_type, { "file", "f" }, 'f'); 52 | Value ftdv(file_type, { "d", "dir", "directory" }, 'd'); 53 | Value ftlv(file_type, { "l", "link" }, 'l'); 54 | 55 | // Every instance of -L will add its argument to the vector "list_of_ints" 56 | Vector loiv(list_of_ints, { "list", "L" }); 57 | 58 | p.add_subcommand(sample_subcommand, "test"); 59 | 60 | // The third argument argument isn't necessary to set in this simple example. 61 | non_options = Parser::parse(argc, argv); 62 | 63 | // Other code. At this point, all variables are set and the "test" subcommand has been run if it was on the command line. 64 | } 65 | ``` 66 | 67 | If you want to provide a help message for anything, just add it to the end of the arguments to the function. The next section will display the more generic syntax. 68 | 69 | ### Generic Syntax 70 | ```cpp 71 | std::vector non_options; 72 | 73 | using namespace cli; 74 | 75 | Parser p; 76 | Var generic_syntax(T& variable_to_set, std::vector flags, bool takes_args, const char * help_mess = "") 77 | Value generic_syntax(T& variable_to_set, std::vector flags, T value_to_set_variable, const char * help_mess = ""); 78 | Vector generic_syntax(std::vector& vector_to_add_to, std::vector flags, const char * help_mess = ""); 79 | 80 | p.add_subcommand(const char * name_of_subcommand, subcommand, const char * help_mess = ""); 81 | // subcommand is a function of type "void function(int argc, char ** argv, void * data)" 82 | 83 | p.set_usage("[options] non-option0 non-option1"); 84 | p.set_header("Here is a description of what your program does and so on."); 85 | p.set_footer("For more information, contact us at the.landfill.coding@gmail.com."); 86 | // Must be set to a folder you can access. 87 | p.set_help_file_path("/usr/local/bin/folder_you_can_access/"); 88 | 89 | // Should be called after everything else is set and before Parser::parse 90 | p.generate_help(argv[0]); 91 | 92 | // data is an object/literal with data you want to pass to the subcommand. 93 | non_options = p.parse(argc, argv, &data); 94 | ``` 95 | 96 | where `T` is the type of the variable and `variable_name_var` is a stack allocated variable. Single char flags have a dash while multichar flags have two dashes. Supports any flag syntax you would expect from a POSIX standard program, including 97 | 98 | - `-fValue` 99 | 100 | - `-f Value` 101 | 102 | - `-abc` 103 | 104 | - `-abcf Value` 105 | 106 | - `--long-opt` 107 | 108 | - `--long-opt=Value` 109 | 110 | The first argument to a `Var` is either the variable you want to set, its address (as long as it isn't a `Var`), or a `nullptr`, the second variable is an array/vector of strings corresponding to flags that control the value of the variable, the third argument is a bool that determines whether or not the flags take arguments themselves, and the last argument is an optional help message. If you don't provide one, the flags will still show up in the help message so that you don't forget about documenting a flag. To have a set of flags ignored, pass in anything starting with a backtick (\`) for the help message. `Var`s have an additional argument before the help message to declare the buffer size. 111 | 112 | The only difference between a `Value` and a `Var` is that the third argument is what you want the variable to be set to if the flag is found. `Value`s do not take arguments. 113 | 114 | `Vector`s take in a vector of type `T` and only take three arguments instead of four like a `Var` or a `Value`. 115 | 116 | `parse` returns an `std::vector` consisting of all the arguments that are not flags or arguments to flags, including the initial command name and all subcommands. Every subcommand has a nullptr immediately before it in the vector so that you know that the argument is a subcommand. 117 | 118 | If a `nullptr` is provided for the first argument of a Var or Value, the parser will just treat it as if it were a non-option. 119 | 120 | Valid subcommands have the syntax `void function(int argc, char ** argv, void * data)`. The third argument is what you choose to pass in when you call `parse`, and all three arguments will be passed by `parse` once the subcommand is found. 121 | 122 | ## Long Version 123 | 124 | cpp_cli is a library designed to make parsing command line arguments for c++ easy, needing only one line per variable you want to set and one line to parse, and efficient, running in linear time with respect to the number of arguments passed to the command line. 125 | 126 | Currently, this library has a stable release with all the functionality you could want from a CLI library, including subcommand handling and automatic help generation. 127 | 128 | While the source code itself is standard c++11, the test program's Makefile uses the GNU/Linux tools make and g++. 129 | 130 | ## Table of Contents 131 | 1. [Why I Wrote This Library](#why-i-wrote-this-library) 132 | 133 | 2. [Getting Started](#getting-started) 134 | 135 | 1. [Prerequisites](#prerequisites) 136 | 137 | 2. [Install](#install) 138 | 139 | 3. [Parsing Rules](#parsing-rules) 140 | 141 | 1. [How Parsing Works With Subcommands](#how-parsing-works-with-subcommands) 142 | 143 | 2. [Types the Library Can Handle](#types-the-library-can-handle) 144 | 145 | 1. [How to Handle a `char` Array](#how-to-handle-a-char-array) 146 | 147 | 3. [Using Options Whose Locations Matter](#using-options-whose-locations-matter) 148 | 149 | 4. [Exception Throwing](#exception-throwing) 150 | 151 | 5. [Help Message](#help-message) 152 | 153 | 6. [More Complex Command Line Parsing](#more-complex-command-line-parsing) 154 | 155 | 1. [Subcommands](#subcommands) 156 | 157 | 1. [Subcommands Example](#subcommands-example) 158 | 159 | 2. [`Value`s](#values) 160 | 161 | 3. [`Vector`s](#vectors) 162 | 163 | 4. [WSpecialization](#wspecialization) 164 | 165 | 1. [WSpecialization Example](#wspecialization-example) 166 | 167 | 5. [Adding Your Own Extensions](#adding-your-own-extensions) 168 | 169 | 7. [Goals](#goals) 170 | 171 | 8. [Goals Completed](#goals-completed) 172 | 173 | 9. [License](#license) 174 | 175 | ## Why I Wrote This Library 176 | - Every non-trivial program has to parse command line arguments, which leaves programmers often writing their own parsers for each individual project even though they are often writing the same inefficient, rigid, and unnecessarily complex algorithms. 177 | 178 | - Current CLI parsers either do far too little, such as GetOpt, or far too much, such as CLI11. Programs should do one thing and do it well. This library takes the data in the command line and puts it into your variables. 179 | 180 | - I wanted to contribute to the open source community and familiarize myself with GitHub. 181 | 182 | - After actually using some of the other competitors ([CLI11](https://github.com/CLIUtils/CLI11) has a good list) to test how my library stacks up to theirs, I'm starting to remember more why I made this library. 183 | 184 | - For starters, I don't include half the STL to parse the command line, which keeps executables much smaller than other CLI libraries. 185 | 186 | - This library is also way more flexible than most other libraries. It understands that its only job is to organize data on the command line so that it becomes much easier for you to read. It also documents itself using the automatic help generation. 187 | 188 | ## Getting Started 189 | As it currently stands, the entire product is valid c++11 except for the makefiles for compiling the projects. 190 | 191 | ### Prerequisites 192 | The library requires nothing but c++11. The test program already has symlinks to the required library and header file. 193 | 194 | ### Install 195 | No installation required. Just download the current release and include "cpp_cli.h" in your cpp source code wherever you want to parse the command line. 196 | 197 | ## Parsing Rules 198 | [This answer](https://stackoverflow.com/a/14738273/6629221) on stackexchange does a good job of summarizing the standard for command line argument syntax, and the library follows these rules, which are copied below for convenience. 199 | 200 | > - Arguments are divided into options and non-options. Options start with a dash, non-options don't. 201 | > 202 | > - Options, as the name implies, are supposed to be optional. If your program requires some command-line arguments to do anything at all useful, those arguments should be non-options (i.e. they should not start with a dash). 203 | > 204 | > - Options can be further divided into short options, which are a single dash followed by a single letter (-r, -f), and long options, which are two dashes followed by one or more dash-separated words (--recursive, --frobnicate-the-gourds). Short options can be glommed together into one argument (-rf) as long as none of them takes arguments (see below). 205 | > 206 | > - Options may themselves take arguments. 207 | > 208 | > - The argument to a short option -x is either the remainder of the argv entry, or if there is no further text in that entry, the very next argv entry whether or not it starts with a dash. 209 | > 210 | > - The argument to a long option is set off with an equals sign: --output=outputfile.txt. 211 | > 212 | > - If at all possible, the relative ordering of distinct options (with their arguments) should have no observable effect. 213 | > 214 | > - The special option -- means "do not treat anything after this point on the command line as an option, even if it looks like one." This is so, for instance, you can remove a file named '-f' by typing rm -- -f. 215 | > 216 | > - The special option - means "read standard input". 217 | 218 | Note that this library does not force you to use any of the commonly reserved short options at the bottom of the list, nor does it treat them any differently than any other options, nor does it reserve them. It is up to the user to maintain this standard. Furthermore, the special option "-" is treated just like any other option, so it is not reserved for standard input either. Finally, the special argument "--" will turn any arguments that come after it into non-options. 219 | 220 | ### How Parsing Works With Subcommands 221 | 222 | When any word that can be identified as a valid subcommand shows up, the parser will then call that subcommand, add a `nullptr` to the list of non_options, then add the subcommand, and then it will then run the subcommand. Each subcommand has its own totally independent set of flags, but they all share the same non_options. This functionality is modeled after the functionality of the `git` command and its subcommands. 223 | 224 | ### Types the Library Can Handle 225 | As it currently stands, this library can handle standard types that can be converted from a `char *`, which include all numeric types, std::string, and `char *`. To extend the library to handle other types, you need to either add a template specialization, which is what I have done for the numeric types, or overload the "=" operator to take in `char *`, which is what std::string has done. 226 | 227 | When using a `Var` that does not take subarguments, the variable will be set to whatever the subargument is. For instance, if the flag "-s" does not take arguments, it will set its corresponding variable to "s". Likewise, "--stop-early" will set its corresponding variable to "stop-early" or however many characters allocated if the variable is a `char *`. If the argument can be stacked (such as -vvvv), then it will set its variable to "vvvv". 228 | 229 | The default template version takes the form 230 | ```cpp 231 | virtual void set_base_variable(const char * b_v) { 232 | *(T *)base_variable = b_v; 233 | } 234 | ``` 235 | 236 | #### How to Handle a `char` Array 237 | To create a `Var` with a `char *` variable: 238 | 239 | 1. Use a template type of `char`. 240 | 241 | 2. Provide a fourth argument with the size of the buffer. 242 | 243 | ```cpp 244 | const int example_string_bf_size = 20; 245 | char example_string[example_string_bf_size]; 246 | Var example_string_var(example_string, ..., ..., example_string_bf_size, optional_help_string); 247 | ``` 248 | 249 | Furthermore, you should allocate enough memory to store the longest argument you expect to receive. If you do not, you run the risk of a segmentation fault, and there is nothing the library can do to fix it or notify you that your buffer is too small. 250 | 251 | If you just want to set a singular `char`, passing it in as a normal variable works and you do not need to specify a buffer size. 252 | ```cpp 253 | char example_char = 'x'; 254 | Var example_char_var(example_char, ..., ...); 255 | ``` 256 | 257 | ### Using Options Whose Locations Matter 258 | Without going into too much detail, the `-l` flag in `gcc` and `g++` has to come after other arguments in gcc, but the library destroys the order of options in most cases. To keep a flag in the list of non-options, provide `nullptr` for the first argument in the Var constructor where the address of a variable would normally go. For example: 259 | ```cpp 260 | Var example_ignored_variable(nullptr, { "l", "library"}, true); 261 | ``` 262 | Any flag that starts with `-l` or `--library` will be considered as if it were a non-option. Note that the templated type was an int in this example, but the type doesn't matter. 263 | 264 | Having a `-l` in a group of multiple short arguments such as `-albc` will throw an exception identifying the flag. 265 | 266 | ## Exception Throwing 267 | The library will throw exceptions (`std::invalid_argument`) when you provide a flag on the command line that you did not specify (except for the single hyphen flag for standard input), provide an argument to a flag that does not take arguments, leave out an argument to a flag that does take arguments, or try to use a short option whose location on the command line matters inside a group of short options. When the library throws an argument, it will tell you the error and which flag caused the error. 268 | 269 | The library will also throw an `std::runtime_error` if there is a problem with writing the help files. 270 | 271 | ## Help Message 272 | This library can automatically generate a help message by calling `Parser::generate_help(argv[0])`, which will generate a help message and store it in a file within the directory you specify named ".X_help_message", where "X" is the name of each subcommand leading up to and including the current subcommand. For instance, if git used this library, the command "git push" would produce the file ".git_push_help_message" while just "git" would produce ".git_help_message". 273 | 274 | To print out the current help message, use `Parser::print_help()`, which will print out the last help message of the last subcommand that called `generate_help(argv[0])`. It will only generate the help message if there is no help message file corresponding to the current subcommand, meaning you should delete all help message files on compiling. This library has no automatic trigger for a help message, so you'll still need to create a `Value` for each help message display. You may need to set the filename if you want a help message from a supercommand to be displayed. If `Parser::print_help()` is called without calling `Parser::generate_help(argv[0])`, the program will throw a runtime exception detailing which subcommand needs to have the `generate_help(argv[0])` added. 275 | 276 | The help file path can either be relative or absolute, but you should only make it relative if you can guarantee that the executable will only be run from one unchanging directory. Otherwise, running it in multiple locations will produce a new help file in each of those new locations. `Parser::set_help_file_path("")` will set the file path to the current directory. 277 | 278 | If the help file path you specify does not exist or you do not have permission to create a file in the directory, then the program will throw a runtime exception and notify you of the error. Either rerun the command with the proper privileges, make the directory, or use another directory. 279 | 280 | ## More Complex Command Line Parsing 281 | 282 | ### Subcommands 283 | 284 | Subcommands are commands embedded inside a single command. The classic example of a command with subcommands is `git`, which has a huge list of subcommands that do completely different things but all use the same functionality of `git`. In this library, each subcommand is treated like a different main function that allows you to pass data from a supercommand into it. Other than the data passed to the subcommand (or data shared by a class if the supercommand and the subcommand have access to the same data), each subcommand is entirely different from the rest of the program, including its supercommand, its subcommands, and any other subcommand, meaning that each subcommand can also have different flags, the same flags, the same flags but referring to different things, etc. Subcommands are declared with the syntax `Parser::add_subcommand("Command Name", subcommand_function)`, where `void subcommand_function(int argc, char ** argv, void * data)` is the style of the subcommand function. It is important to note that each subcommand will treat itself as if it were its own program and completely ignore everything before it on the command line. 285 | 286 | #### Subcommands Example 287 | 288 | ```cpp 289 | #include "cpp_cli.h" 290 | 291 | void push_subcommand(int argc, char ** argv, void * data); 292 | void pull_subcommand(int argc, char ** argv, void * data); 293 | 294 | struct Data_From_Principle_Subcommand { 295 | std::string file_path; 296 | int level; 297 | }; 298 | 299 | 300 | int main(int argc, char ** argv) { 301 | using namespace cpp_cli; 302 | Data_For_Push_Subcommand dfps; 303 | 304 | Parser::add_subcommand("push", push_subcommand); 305 | Parser::add_subcommand("pull", pull_subcommand); 306 | // Other stuff like flags 307 | Var file_path_var(dfps.file_path, { "p", "path" }, true); 308 | Vat level_var(dfps.level, { "l", "level" }, true); 309 | 310 | // A void pointer to dfps will be passed to any subcommand used. 311 | Parser::parse(argc, argv, &dfps); 312 | } 313 | 314 | void push_subcommand(int argc, char ** argv, void * data_ptr) { 315 | Data_For_Push_Subcommand data = *((Data_For_Push_Subcommand *)data_ptr); 316 | // Do whatever 317 | } 318 | ``` 319 | 320 | ### `Value`s 321 | 322 | A `Value` has the same syntax as the `Var`, except the third argument is what you want the value to be set to when the flag appears. For instance: `Value some_var(some, { "some", "not-nothing", "s", "less-than-all" }, 's')` will set `some` to `'s'` if any of the flags in the list are found. These are better suited to options that do not take args than `Var`s. 323 | 324 | ### `Vector`s 325 | 326 | A `Vector` has a similar syntax to the `Var`, except the third argument is removed entirely because you always need to provide an argument to the flags and it has no default value. It has the syntax: 327 | 328 | ```cpp 329 | std::vector list; 330 | 331 | Vector list_var(list, { "f", "flag" }, "Help String"); 332 | ``` 333 | 334 | This will take a vector of type `T` as the first argument. Whenever one of its flags are found, it will use `Var::set_base_variable(const char * b_v)` to convert the argument to the type and push it back to the end of the vector unless `T = const char *`, `T = char *`, or `T = char`. If `T = const char *`, then the vector will be filled with pointers to the command line arguments themselves, which should work fine since they're `const`. If `T = char *` on the other hand, the program will throw an exception if you try to run it because you can't set a `char *` to a `const char *` and because you can't fill a vector with bare `char *`s beforehand and tell the program what the buffer size is. Finally, if `T = char`, it will push back every `char` in `b_v` in order because you should be using strings and I'm going to make you feel bad for it. 335 | 336 | ### WSpecialization 337 | 338 | This is where things get more complicated, though not by as much as you would expect. 339 | 340 | I have implemented a general system to handle gcc-style flags called `WSpecialization` after gcc's `-W` argument. `WSpecialization` is in its own separate header file, "w_specialization.h", which must be included for you to use. You must still include "cpp_cli.h". The syntax for `WSpecialization` is intentionally similar to the syntax for a `Var`. Note that you are not in any way required to use this type of argument if you don't want to, and can use the simpler versions above. 341 | 342 | `WSpecialization` also works normally with other command flags and with other `WSpecializationS`. 343 | 344 | Unlike a `Var`, `Wvalue`s and `Warg`s prevent you from providing more than one subalias, though you could if you just made multiple `Wvalue`s or `WargS`. 345 | 346 | #### WSpecialization Example 347 | 348 | ```cpp 349 | #include "cpp_cli.h" 350 | #include 351 | 352 | int main(int argc, char ** argv){ 353 | bool w_sign_conversion = true; 354 | bool w_all = false; 355 | int w_error_level = 1; 356 | char w_type = 'x'; 357 | 358 | std::string regular_c_string; 359 | 360 | { 361 | using namespace cpp_cli; 362 | WSpecialization w_options(100); 363 | 364 | Wvalue w_sign_conversion_var(w_sign_conversion, w_options, "sign-conversion", true); 365 | Wvalue w_no_sign_conversion_var(w_sign_conversion, w_options, "no-sign-conversion" , false); 366 | Wvalue w_all_var(w_all, w_options, "all", true); 367 | 368 | Warg w_error_level_var(w_error_level, w_options, "error-level"); 369 | 370 | Wvalue w_type_a_var(w_type, w_options, "file", 'f'); 371 | Wvalue w_type_b_var(w_type, w_options, "dir", 'd'); 372 | Wvalue w_type_c_var(w_type, w_options, "link", 'l'); 373 | Wvalue w_type_d_var(w_type, w_options, "any", 'a'); 374 | 375 | Var w_options_var(&w_options, { "W" }, true); 376 | 377 | Var regular_c_string_var(regular_c_string, { "f", "file", "filename" }, true);= 378 | non_options = Parser::parse(argc, argv); 379 | } 380 | 381 | // Other code. At this point, all variables are set. 382 | } 383 | ``` 384 | This might look a little daunting, but bear in mind that we're linking somewhere around twenty flags to twelve variables while imposing a superstucture on the flags by using multiple `WSpecialization`s. Four of these links come from setting `w_type` alone. It's also now to around three lines per variable, which isn't that much of an increase. 385 | 386 | ### Adding Your Own Extensions 387 | You can implement more complex parsing by defining your own class or struct and overriding the template for a `Var` and writing your own version of `set_base_variable`. Below is the template specialization for `char` which allows it to act like a `char *`: 388 | 389 | ```cpp 390 | // args_parser_templates.h 391 | 392 | template<> 393 | class Var : public Var_Interface { 394 | private: 395 | int buffer_size; 396 | public: 397 | Var(void * b_v, std::vector a, bool ta, int b_s); 398 | virtual void set_base_variable(const char * b_v); 399 | }; 400 | 401 | // cpp_cli.h 402 | #include "args_parser_templates.h" 403 | 404 | // Other code 405 | 406 | inline Var::Var(void * b_v, std::vector a, bool ta, int b_s) : Var_Interface(b_v, a, ta), buffer_size(b_s) {} 407 | 408 | inilne void Var::set_base_variable(const char * b_v) { 409 | char * base_variable_string = (char *)base_variable; 410 | int i = 0; 411 | while (b_v[i] != '\0' && i < buffer_size) { 412 | base_variable_string[i] = b_v[i]; 413 | i++; 414 | } 415 | base_variable_string[i] = '\0'; 416 | } 417 | ``` 418 | 419 | Note that: 420 | - The template specialization for `char` extends `public Var_Interface`. 421 | - It includes a new variable `buffer_size`, which prevents it from going beyond its buffer. Because it has more variables than a `Var_Interface`, it needs to define its own constructor. Normally, if you don't have extra variables or don't need to do anything besides setting variables in the constructor, you don't need to define a constructor. 422 | - `set_base_variable` is a virtual function takes in a `const char *` and returns `void`. This function must be implemented to make the template specialization behave differently. You should convert the variable `base_variable` to a pointer to whatever type you're trying to implement. 423 | 424 | ```cpp 425 | void Var::set_base_variable(const char * b_v) { 426 | T& variable = *(T *)base_variable; 427 | // set variable to whatever. 428 | } 429 | ``` 430 | 431 | To specialize the template, you must include the header file `args_parser_templates.h`. 432 | 433 | ## Goals 434 | 1. Add ability to run a function from the command line and early exit. 435 | 436 | 2. Refactor the code so that help file generation is more flexible and its own class so it doesn't clutter up cpp_cli.h. 437 | 438 | 3. Make Windows specific compilation. 439 | 440 | 1. Either convert Makefiles to CMake or roll my own Project for Visual Studio. 441 | 442 | 4. Add helpful error messages. 443 | 444 | 1. Currently, the program will convert strings into 0 if the argument takes a numeric argument. 445 | For example, `--prob=test` will set prob to 0.0, because prob is a double. 446 | 447 | 2. Other examples will come up whenever I encounter more errors. 448 | 449 | 5. See if I can't move `base_variable` from `Var_Interface` to the templated subclass of `Var`, which would really just reduce the typecast. 450 | 451 | 1. Not really a priority. 452 | 453 | 6. Run more tests, specifically trying to simulate command line response in standard Linux tools. 454 | 455 | 1. `wget` in particular looks perfect for this, with the notable exception of non-standard command-line arguments, such as -nc, which the library would treat as --nc. 456 | 457 | 2. It is not a good idea for me to try to implement all the flags for `gcc`, but it does have a more complex parsing algorithm I could try to simulate at least part of. 458 | 459 | ## Goals Completed 460 | 1. Added ability to create a vector of arguments passed to a flag by creating `Vector`s. 461 | 462 | 2. Add autogenerated help. 463 | 464 | 3. Add a help message for the test programs. 465 | 466 | 1. Using the autogenerated help, no less. 467 | 468 | 4. Add subcommands, which are things like `git commit`, where `git` is the main program and `commit` is a subcommand. 469 | 470 | 5. Add example of template class specialization as specified in the section in test program. 471 | 472 | 6. Single `char` arguments work. You could now type something like ```Var single_char_var(single_char, { "A", "a", "B", "b" }, false)``` and it will work. 473 | 474 | 7. Fix segmentation fault from having ignored flag in list of flags. 475 | 476 | 8. Add way to allow user to automatically move flags to non-options by default. 477 | 478 | 1. This is most important when dealing with flags that need to be in order, like gcc's -l library flag. 479 | 480 | 9. Convert library to a single file for the user to include. 481 | 482 | 1. Doing so would solve the issue of Windows specific compilation, as it would automatically be taken care of by the compiler. 483 | 484 | 2. Because of the nature of the algorithm, it has a one time use so it should only be included once, meaning the hit from making the functions inline shouldn't be any worse than just having the library. However, users might not want to recompile everything everytime, so I have to provide the functionality in its own header file. 485 | 486 | 3. It makes the algorithm easier for the user to include and use. 487 | 488 | 10. Make sure that symlinks work on Windows. 489 | 490 | 11. Fix response to nonexistant flags. Right now, it just crashes the program with a seg fault. I can either make it ignore them or treat them as non-options. 491 | 492 | 12. Fix response to providing arguments to options that do not take arguments. 493 | 494 | 13. Fix the potential seg fault of writing outside the valid range for `char *`. 495 | 496 | 14. Add support for repeated single flags, such as `-vvvv` meaning level four verboseness. 497 | 498 | 15. Add helpful error messages for using invalid flags. 499 | 500 | 16. Add helpful error messages for providing arguments to flags that do not take arguments. 501 | 502 | ## License 503 | This project is licensed under the MIT License - see the LICENSE.md file for details. 504 | -------------------------------------------------------------------------------- /src/cpp_cli/args_parser_templates.h: -------------------------------------------------------------------------------- 1 | #ifndef CPP_CMD_LINE_PARSER_TEMPLATES_H 2 | #define CPP_CMD_LINE_PARSER_TEMPLATES_H 3 | #include 4 | 5 | namespace cli{ 6 | 7 | class CLI_Interface { 8 | protected: 9 | bool takes_args_var; 10 | void * base_variable; 11 | std::vector aliases; 12 | const char * help_message; 13 | public: 14 | CLI_Interface(void * b_v, std::vector a, bool ta, const char * hm = ""); 15 | virtual ~CLI_Interface() = default; 16 | const std::vector& get_aliases() const; 17 | bool takes_args() const; 18 | bool ignored() const; 19 | const char * get_help_message() const; 20 | virtual void set_base_variable(const char * b_v) = 0; 21 | }; 22 | 23 | template 24 | class Var : public CLI_Interface { 25 | public: 26 | Var(T & b_v, std::vector a, bool ta, const char * hm = ""); 27 | Var(T * b_v, std::vector a, bool ta, const char * hm = ""); 28 | virtual void set_base_variable(const char * b_v); 29 | }; 30 | 31 | template 32 | class Vector : public CLI_Interface { 33 | public: 34 | Vector(std::vector& b_v, std::vector a, const char * hm = ""); 35 | Vector(std::vector* b_v, std::vector a, const char * hm = ""); 36 | virtual void set_base_variable(const char * b_v); 37 | }; 38 | 39 | template 40 | class Value : public CLI_Interface { 41 | private: 42 | T value; 43 | public: 44 | Value(T & b_v, std::vector a, T v, const char * hm = ""); 45 | Value(T * b_v, std::vector a, T v, const char * hm = ""); 46 | virtual void set_base_variable(const char * b_v); 47 | }; 48 | 49 | template<> 50 | class Var : public CLI_Interface { 51 | private: 52 | int buffer_size; 53 | public: 54 | Var(char * b_v, std::vector a, bool ta, int b_s, const char * hm = ""); 55 | Var(char & b_v, std::vector a, bool ta, const char * hm = ""); 56 | virtual void set_base_variable(const char * b_v); 57 | }; 58 | 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/cpp_cli/cli_help.h: -------------------------------------------------------------------------------- 1 | #ifndef CLI_HELP_H 2 | #define CLI_HELP_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class CLI_Interface; 9 | 10 | namespace cli { 11 | struct CLI_Help { 12 | public: 13 | std::string header; 14 | std::string usage; 15 | std::string footer; 16 | static std::string help_file_name; 17 | static std::string help_file_path; 18 | size_t help_width = 80; 19 | 20 | static std::vector current_command_list; 21 | std::vector subcommand_descriptions; 22 | public: 23 | void set_usage(std::string u); 24 | void set_header(std::string h); 25 | void set_footer(std::string f); 26 | void set_help_width(size_t hw); 27 | void set_help_file_name(std::string hfn); 28 | void set_help_file_path(std::string hfp); 29 | 30 | void print_within_length_stream(const std::string& str, size_t indent = 0, std::ostream& file_writer = std::cout); 31 | std::string print_within_length_str(const std::string& str, size_t indent = 0); 32 | 33 | void generate_help(const char * subcommand_name, std::vector subcommand_aliases, std::vector list_of_cmd_var); 34 | void print_help(); 35 | 36 | }; 37 | 38 | inline void CLI_Help::set_header(std::string h) { 39 | header = h; 40 | } 41 | 42 | inline void CLI_Help::set_usage(std::string u) { 43 | usage = u; 44 | } 45 | 46 | inline void CLI_Help::set_footer(std::string f) { 47 | footer = f; 48 | } 49 | 50 | inline void CLI_Help::set_help_width(size_t hw) { 51 | help_width = hw; 52 | } 53 | 54 | inline void CLI_Help::set_help_file_name(std::string hfn) { 55 | help_file_name = hfn; 56 | } 57 | 58 | inline void CLI_Help::set_help_file_path(std::string hfp) { 59 | help_file_path = hfp; 60 | } 61 | 62 | inline void CLI_Help::generate_help(const char * subcommand_name, std::vector subcommand_aliases, std::vector list_of_cmd_var) { 63 | const size_t last_slash_idx = std::string(subcommand_name).find_last_of("\\/"); 64 | if (std::string::npos != last_slash_idx) 65 | { 66 | subcommand_name += last_slash_idx; 67 | } 68 | current_command_list.push_back(subcommand_name); 69 | std::string buffer; 70 | buffer.reserve(2048); 71 | if (help_file_path == "`") { 72 | std::string error_message = "The help file path has not been set. " 73 | "Use the command 'CLI_Help::set_help_file_path(std::string hfn)' to set a valid file path before calling generate_help. " 74 | "The file path should be an absolute path if you want the program to run anywhere. " 75 | "Only use a relative path if your executable can only be executed from one spot."; 76 | throw std::runtime_error(error_message); 77 | } 78 | buffer = help_file_path; 79 | buffer += "."; 80 | for (size_t i = 0; i < current_command_list.size(); i++) { 81 | buffer += current_command_list[i]; 82 | buffer += "_"; 83 | } 84 | buffer += "help_file"; 85 | set_help_file_name(buffer); 86 | 87 | std::ofstream file_writer; 88 | file_writer.open(help_file_name); 89 | 90 | if (!file_writer.is_open()) { 91 | std::string error_message; 92 | error_message.reserve(1024); 93 | error_message += help_file_name; 94 | error_message += " must be accessable by the current user."; 95 | throw std::runtime_error(error_message); 96 | } 97 | 98 | buffer = "usage: "; 99 | for (size_t i = 0; i < current_command_list.size(); i++) { 100 | buffer += current_command_list[i]; 101 | buffer += " "; 102 | } 103 | 104 | buffer += usage; 105 | 106 | print_within_length_stream(buffer, 0, file_writer); 107 | file_writer << "\n"; 108 | print_within_length_stream(header, 0, file_writer); 109 | file_writer << "\n"; 110 | 111 | bool any_descriptions = false; 112 | 113 | for (size_t i = 0; !any_descriptions && i < subcommand_descriptions.size(); i++) { 114 | any_descriptions = subcommand_descriptions[i][0] != '`'; 115 | } 116 | 117 | if (any_descriptions) { 118 | file_writer << "SUBCOMMANDS:\n"; 119 | for (size_t i = 0; i < subcommand_descriptions.size(); i++) { 120 | if (subcommand_descriptions[i][0] != '`') { 121 | file_writer << subcommand_aliases[i] << "\n"; 122 | print_within_length_stream(subcommand_descriptions[i], 8, file_writer); 123 | } 124 | } 125 | file_writer << "\n"; 126 | } 127 | 128 | file_writer << "OPTIONS:\n"; 129 | for (size_t i = 0; i < list_of_cmd_var.size(); i++) { 130 | CLI_Interface * clv = list_of_cmd_var[i]; 131 | const std::vector& a = clv->get_aliases(); 132 | buffer = ""; 133 | if (clv->get_help_message()[0] != '`') { 134 | const char * n_dash = "--"; 135 | 136 | for (size_t j = 0; j < a.size(); j++) { 137 | buffer += n_dash + (a[j][1] == '\0') * (1 + (a[j][0] == '-')); 138 | buffer += a[j]; 139 | buffer += ", "; 140 | } 141 | buffer.pop_back(); 142 | buffer.pop_back(); 143 | 144 | file_writer << buffer << "\n"; 145 | print_within_length_stream(std::string(clv->get_help_message()), 8, file_writer); 146 | } 147 | } 148 | file_writer << "\n"; 149 | print_within_length_stream(footer, 0, file_writer); 150 | file_writer.close(); 151 | } 152 | 153 | inline void CLI_Help::print_help() { 154 | std::ifstream file_reader; 155 | file_reader.open(help_file_name); 156 | if (!file_reader.is_open()) { 157 | std::string error_message; 158 | error_message.reserve(1024); 159 | error_message += "\""; 160 | error_message += help_file_name; 161 | error_message += "\" in \""; 162 | error_message += help_file_path; 163 | error_message += "\""; 164 | if (help_file_path == "") { 165 | error_message += " (current running directory)"; 166 | } 167 | error_message += " has not been generated. Please put CLI_Help::generate_help(argv[0]) right before calling parse in the subcommand: "; 168 | for (size_t i = 0; i < error_message.size(); i++) { 169 | error_message += current_command_list[i]; 170 | error_message += "->"; 171 | } 172 | error_message.pop_back(); 173 | error_message.pop_back(); 174 | throw std::runtime_error(error_message); 175 | } else { 176 | std::string line; 177 | while (std::getline(file_reader, line)) { 178 | std::cout << line << "\n"; 179 | } 180 | } 181 | file_reader.close(); 182 | } 183 | 184 | void CLI_Help::print_within_length_stream(const std::string& str, size_t indent, std::ostream& stream) { 185 | size_t cur_index = 0; 186 | std::string indent_str(indent, ' '); 187 | while (cur_index + help_width < str.length()) { 188 | size_t line_length = str.find_last_of(" \t", cur_index + help_width - indent); 189 | size_t next_newline = str.find("\n", cur_index); 190 | if (next_newline < line_length) { 191 | line_length = next_newline; 192 | } 193 | stream << indent_str << str.substr(cur_index, line_length - cur_index) << "\n"; 194 | cur_index = line_length + 1; 195 | } 196 | stream << std::string(indent, ' ') << str.substr(cur_index) << "\n"; 197 | } 198 | 199 | std::string CLI_Help::print_within_length_str(const std::string& str, size_t indent) { 200 | size_t cur_index = 0; 201 | std::string next_str; 202 | next_str.reserve(str.length()); 203 | while (cur_index < str.length()) { 204 | size_t line_length = str.rfind(" ", cur_index + help_width - indent); 205 | next_str += std::string(indent, ' '); 206 | next_str += str.substr(cur_index, line_length - cur_index); 207 | next_str += "\n"; 208 | cur_index += line_length + 1; 209 | } 210 | return next_str; 211 | } 212 | 213 | std::string CLI_Help::help_file_path = "`"; 214 | std::string CLI_Help::help_file_name = "If you see this message, the help_file_name is not being set."; 215 | std::vector CLI_Help::current_command_list = std::vector(); 216 | } 217 | 218 | #endif 219 | -------------------------------------------------------------------------------- /src/cpp_cli/cpp_cli.h: -------------------------------------------------------------------------------- 1 | #ifndef CPP_CMD_LINE_PARSER_H 2 | #define CPP_CMD_LINE_PARSER_H 3 | #include "args_parser_templates.h" 4 | #include "template_definitions.h" 5 | #include "cli_help.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace cli{ 12 | 13 | class Parser { 14 | friend class CLI_Interface; 15 | public: 16 | typedef void (*subcommand_func)(int, char **, void *); 17 | private: 18 | std::unordered_map command_line_settings_map; 19 | std::vector list_of_cmd_var; 20 | std::vector non_options; 21 | 22 | std::unordered_map subcommand_map; 23 | std::vector subcommand_list; 24 | std::vector subcommand_aliases; 25 | 26 | size_t num_unique_flags = 0; 27 | 28 | static CLI_Help help_manager; 29 | private: 30 | void fill_hash_table(); 31 | void fill_subcommand_hash_table(); 32 | 33 | void subcommand_handling(int argc, char ** argv, void * data); 34 | 35 | void long_option_handling(char ** argv, int& i); 36 | int find_and_mark_split_location(char * flag); 37 | void check_if_option_exists(const char * error_message, const char * flag, const bool exists, const bool should_exist); 38 | 39 | void short_option_handling(int argc, char ** argv, int& i); 40 | void multiple_short_options_handling(int argc, char ** argv, int& cur_argument); 41 | 42 | void clear_everything(); 43 | void clear_managed_vars(); 44 | 45 | void print_flags(); 46 | public: 47 | ~Parser(); 48 | void set_usage(const std::string& u); 49 | void set_header(const std::string& h); 50 | void set_footer(const std::string& f); 51 | void set_help_width(size_t hw); 52 | void set_help_file_path(const std::string& hfp); 53 | void generate_help(const char * subcommand_name); 54 | void print_help(); 55 | 56 | void add_subcommand(const char * subcommand, subcommand_func sub_func, const char * description = ""); 57 | void reserve_space_for_subcommand(size_t number_of_subcommand); 58 | 59 | std::vector parse(int argc, char ** argv, void * data = nullptr); 60 | 61 | template 62 | CLI_Interface * arg(T& var, std::vector flags, const char * help_message = ""); 63 | 64 | template 65 | CLI_Interface * value(T& var, std::vector flags, T to_set, const char * help_message = ""); 66 | 67 | template 68 | CLI_Interface * vector(std::vector& var, std::vector flags, const char * help_message = ""); 69 | 70 | CLI_Interface * ignored(std::vector flags, const char * help_message = ""); 71 | CLI_Interface * repeated(size_t& var, std::vector flags, const char * help_message = ""); 72 | }; 73 | 74 | Parser::~Parser() { 75 | clear_managed_vars(); 76 | } 77 | 78 | 79 | /////////////////////////////////////////////////////////////////////////////// 80 | //////////////////////////////INLINE DECLARATIONS////////////////////////////// 81 | /////////////////////////////////////////////////////////////////////////////// 82 | 83 | inline std::vector Parser::parse(int argc, char ** argv, void * data) { 84 | fill_hash_table(); 85 | fill_subcommand_hash_table(); 86 | non_options.reserve(2 * argc); 87 | int i = 1; 88 | for (; i < argc; i++) { 89 | // case: subcommand, which is recursive 90 | if (subcommand_map.count(argv[i]) != 0) { 91 | subcommand_handling(argc - i, argv + i, data); 92 | break; 93 | } 94 | // cases: --long-option 95 | else if (argv[i][2] != '\0' && argv[i][1] == '-' && argv[i][0] == '-') { 96 | long_option_handling(argv, i); 97 | // case: -- and all arguments are options 98 | } else if (argv[i][0] == '-' && argv[i][1] == '-' && argv[i][2] == '\0') { 99 | i++; 100 | for (; i < argc; i++) { 101 | non_options.push_back(argv[i]); 102 | } 103 | break; 104 | // case: - 105 | } else if (argv[i][0] == '-' && argv[i][1] == '\0' && command_line_settings_map.count("-") != 0) { 106 | command_line_settings_map["-"]->set_base_variable("-"); 107 | } else if (argv[i][0] == '-') { 108 | short_option_handling(argc, argv, i); 109 | } else { 110 | non_options.push_back(argv[i]); 111 | } 112 | } 113 | return non_options; 114 | } 115 | 116 | inline void Parser::fill_hash_table() { 117 | command_line_settings_map = std::unordered_map(num_unique_flags); 118 | std::unordered_map flag_already_used(num_unique_flags); 119 | for (size_t i = 0; i < Parser::list_of_cmd_var.size(); i++) { 120 | CLI_Interface * cur_com_var = list_of_cmd_var[i]; 121 | const std::vector & cur_aliases = cur_com_var->get_aliases(); 122 | for (size_t j = 0; j < cur_aliases.size(); j++) { 123 | check_if_option_exists("Flag already used: ", cur_aliases[j], flag_already_used.count(cur_aliases[j]), false); 124 | flag_already_used.insert({cur_aliases[j], nullptr}); 125 | command_line_settings_map.insert({cur_aliases[j], cur_com_var}); 126 | } 127 | } 128 | } 129 | 130 | inline void Parser::subcommand_handling(int argc, char ** argv, void * data) { 131 | subcommand_func sub_com = *(subcommand_map[argv[0]]); 132 | non_options.push_back(nullptr); 133 | non_options.push_back(argv[0]); 134 | help_manager.subcommand_descriptions.clear(); 135 | sub_com(argc, argv, data); 136 | } 137 | 138 | inline void Parser::reserve_space_for_subcommand(size_t number_of_subcommand) { 139 | subcommand_list.reserve(number_of_subcommand); 140 | subcommand_aliases.reserve(number_of_subcommand); 141 | } 142 | 143 | inline void Parser::add_subcommand(const char * subcommand, Parser::subcommand_func sub_func, const char * description) { 144 | subcommand_list.push_back(sub_func); 145 | subcommand_aliases.push_back(subcommand); 146 | help_manager.subcommand_descriptions.push_back(description); 147 | } 148 | 149 | inline void Parser::fill_subcommand_hash_table() { 150 | size_t n_sub = subcommand_aliases.size(); 151 | subcommand_map.reserve(2 * n_sub); 152 | std::unordered_map flag_already_used(2 * n_sub); 153 | for (size_t i = 0; i < n_sub; i++) { 154 | check_if_option_exists("Subcommand already used: ", subcommand_aliases[i], flag_already_used.count(subcommand_aliases[i]), false); 155 | flag_already_used.insert({subcommand_aliases[i], nullptr}); 156 | subcommand_map.insert({subcommand_aliases[i], subcommand_list[i]}); 157 | } 158 | } 159 | 160 | void Parser::clear_managed_vars() { 161 | for (size_t i = 0; i < list_of_cmd_var.size(); i++) { 162 | delete list_of_cmd_var[i]; 163 | } 164 | } 165 | 166 | inline int Parser::find_and_mark_split_location(char * flag) { 167 | int split_location = 0; 168 | for (; flag[split_location] != '\0'; split_location++) { 169 | if (flag[split_location] == '=') { 170 | flag[split_location] = '\0'; 171 | split_location++; 172 | break; 173 | } 174 | } 175 | return split_location; 176 | } 177 | 178 | inline void Parser::check_if_option_exists(const char * error_message, const char * potential_option, const bool exists, const bool should_exist) { 179 | if (exists != should_exist) { 180 | std::string error_message_buffer; 181 | error_message_buffer.reserve(1024); 182 | error_message_buffer += error_message; 183 | error_message_buffer += potential_option; 184 | throw std::invalid_argument(error_message_buffer); 185 | } 186 | } 187 | 188 | inline void Parser::long_option_handling(char ** argv, int& i) { 189 | char * temp_alias = argv[i] + 2; 190 | int split_location = find_and_mark_split_location(temp_alias); 191 | 192 | check_if_option_exists("Unrecognized Option: --", temp_alias, command_line_settings_map.count(temp_alias), true); 193 | 194 | if (command_line_settings_map[temp_alias]->ignored()) { 195 | if (command_line_settings_map[temp_alias]->takes_args()) { 196 | temp_alias[split_location - 1] = '='; 197 | } 198 | non_options.push_back(argv[i]); 199 | return; 200 | } 201 | 202 | // case: --long-option=value 203 | if (temp_alias[split_location] != '\0') { 204 | if (command_line_settings_map[temp_alias]->takes_args()) { 205 | command_line_settings_map[temp_alias]->set_base_variable(temp_alias + split_location); 206 | temp_alias[split_location - 1] = '='; 207 | } else { 208 | std::string error_message; 209 | error_message.reserve(1024); 210 | error_message += "Option does not take arguments: --"; 211 | error_message += temp_alias; 212 | throw std::invalid_argument(error_message); 213 | } 214 | // case: --long-option 215 | } else if (command_line_settings_map[temp_alias]->takes_args()) { 216 | std::string error_message; 217 | error_message.reserve(1024); 218 | error_message += "Option requires arguments: --"; 219 | error_message += temp_alias; 220 | throw std::invalid_argument(error_message); 221 | } else { 222 | command_line_settings_map[temp_alias]->set_base_variable(temp_alias); 223 | } 224 | } 225 | 226 | inline void Parser::short_option_handling(int argc, char ** argv, int& i) { 227 | char temp_alias[2] = "\0"; 228 | temp_alias[0] = argv[i][1]; 229 | 230 | check_if_option_exists("Unrecognized Option: -", temp_alias, command_line_settings_map.count(temp_alias), true); 231 | 232 | if (command_line_settings_map[temp_alias]->ignored()) { 233 | non_options.push_back(argv[i]); 234 | return; 235 | } 236 | 237 | // case: -o value // -o already exists because we checked for it initally. 238 | if (argv[i][2] == '\0' && i + 1 < argc && command_line_settings_map[argv[i] + 1]->takes_args()) { 239 | command_line_settings_map[argv[i] + 1]->set_base_variable(argv[i + 1]); 240 | i++; 241 | return; 242 | } 243 | 244 | // case: -oValue 245 | if (command_line_settings_map[temp_alias]->takes_args()) { 246 | command_line_settings_map[temp_alias]->set_base_variable(argv[i] + 2); 247 | return; 248 | } 249 | 250 | // case: -abc or -vvv 251 | multiple_short_options_handling(argc, argv, i); 252 | } 253 | 254 | inline void Parser::multiple_short_options_handling(int argc, char ** argv, int& cur_argument) { 255 | int i = 0; 256 | char * flag = argv[cur_argument] + 1; 257 | char temp_alias[2] = "\0"; 258 | 259 | // case -vvv 260 | std::string temp_repetition; 261 | temp_repetition.reserve(32); 262 | bool repeated_short_arguments = false; 263 | while (flag[i] != '\0' && flag[i] == flag[0]) { 264 | repeated_short_arguments = true; 265 | temp_repetition.push_back(flag[i]); 266 | i++; 267 | } 268 | 269 | if (repeated_short_arguments) { 270 | temp_alias[0] = temp_repetition[0]; 271 | command_line_settings_map[temp_alias]->set_base_variable(std::to_string(temp_repetition.length()).c_str()); 272 | } 273 | 274 | // case -abc 275 | while (flag[i] != '\0') { 276 | temp_alias[0] = flag[i]; 277 | 278 | check_if_option_exists("Unrecognized Option: -", temp_alias, command_line_settings_map.count(temp_alias), true); 279 | 280 | if (command_line_settings_map[temp_alias]->ignored()) { 281 | std::string error_message; 282 | error_message.reserve(128); 283 | error_message += "Order of -"; 284 | error_message += flag[i]; 285 | error_message += " matters, so it cannoth be part of multiple short arguments."; 286 | throw std::invalid_argument(error_message); 287 | } 288 | if (command_line_settings_map[temp_alias]->takes_args()) { 289 | if (flag[i + 1] != '\0' || cur_argument + 1 >= argc) { 290 | std::string error_message; 291 | error_message.reserve(64); 292 | error_message += "Option requires arguments: "; 293 | error_message += flag[i]; 294 | throw std::invalid_argument(error_message); 295 | } else { 296 | cur_argument++; 297 | command_line_settings_map[temp_alias]->set_base_variable(argv[cur_argument]); 298 | break; 299 | } 300 | } else { 301 | command_line_settings_map[temp_alias]->set_base_variable(temp_alias); 302 | } 303 | i++; 304 | } 305 | } 306 | 307 | void Parser::set_usage(const std::string& u) { 308 | help_manager.set_usage(u); 309 | } 310 | void Parser::set_header(const std::string& h) { 311 | help_manager.set_header(h); 312 | } 313 | void Parser::set_footer(const std::string& f) { 314 | help_manager.set_footer(f); 315 | } 316 | void Parser::set_help_width(size_t hw) { 317 | help_manager.set_help_width(hw); 318 | } 319 | void Parser::set_help_file_path(const std::string& hfp) { 320 | help_manager.set_help_file_path(hfp); 321 | } 322 | void Parser::generate_help(const char * subcommand_name) { 323 | help_manager.generate_help(subcommand_name, subcommand_aliases, list_of_cmd_var); 324 | } 325 | void Parser::print_help() { 326 | help_manager.print_help(); 327 | } 328 | 329 | template 330 | CLI_Interface * Parser::arg(T& var, std::vector flags, const char * help_message) { 331 | list_of_cmd_var.push_back(new Var(var, flags, true, help_message)); 332 | return list_of_cmd_var.back(); 333 | } 334 | 335 | template 336 | CLI_Interface * Parser::value(T& var, std::vector flags, T to_set, const char * help_message) { 337 | list_of_cmd_var.push_back(new Value(var, flags, to_set, help_message)); 338 | return list_of_cmd_var.back(); 339 | } 340 | 341 | template 342 | CLI_Interface * Parser::vector(std::vector& var, std::vector flags, const char * help_message) { 343 | list_of_cmd_var.push_back(new Vector(var, flags, help_message)); 344 | return list_of_cmd_var.back(); 345 | } 346 | 347 | CLI_Interface * Parser::ignored(std::vector flags, const char * help_message) { 348 | list_of_cmd_var.push_back(new Var(nullptr, flags, false, help_message)); 349 | return list_of_cmd_var.back(); 350 | } 351 | 352 | CLI_Interface * Parser::repeated(size_t& var, std::vector flags, const char * help_message) { 353 | list_of_cmd_var.push_back(new Var(var, flags, false, help_message)); 354 | return list_of_cmd_var.back(); 355 | } 356 | CLI_Help Parser::help_manager = CLI_Help(); 357 | } 358 | #endif 359 | -------------------------------------------------------------------------------- /src/cpp_cli/template_definitions.h: -------------------------------------------------------------------------------- 1 | #ifndef TEMPLATE_DEFINITIONS_H 2 | #define TEMPLATE_DEFINITIONS_H 3 | #include "args_parser_templates.h" 4 | #include 5 | #include 6 | 7 | namespace cli { 8 | /////////////////////CLI_Interface Definitions//////////////////// 9 | 10 | inline CLI_Interface::CLI_Interface(void * b_v, std::vector a, bool ta, const char * hm) : takes_args_var(ta), base_variable(b_v), aliases(a), help_message(hm) {} 11 | 12 | inline const std::vector& CLI_Interface::get_aliases() const { 13 | return aliases; 14 | } 15 | 16 | inline bool CLI_Interface::takes_args() const { 17 | return takes_args_var; 18 | } 19 | 20 | inline bool CLI_Interface::ignored() const { 21 | return base_variable == nullptr; 22 | } 23 | 24 | inline const char * CLI_Interface::get_help_message() const { 25 | return help_message; 26 | } 27 | 28 | //////////////////////////Var Definitions///////////////////////// 29 | 30 | template 31 | inline Var::Var(T & b_v, std::vector a, bool ta, const char * hm) : CLI_Interface(&b_v, a, ta, hm) {} 32 | 33 | template 34 | inline Var::Var(T * b_v, std::vector a, bool ta, const char * hm) : CLI_Interface(b_v, a, ta, hm) {} 35 | 36 | template 37 | inline void Var::set_base_variable(const char * b_v) { 38 | *(T *)base_variable = b_v; 39 | } 40 | 41 | ///////////////////////Value Definitions////////////////////// 42 | 43 | template 44 | inline Value::Value(T & b_v, std::vectora, T v, const char * hm) : CLI_Interface(&b_v, a, false, hm), value(v) {} 45 | 46 | template 47 | inline Value::Value(T * b_v, std::vectora, T v, const char * hm) : CLI_Interface(b_v, a, false, hm), value(v) {} 48 | 49 | template 50 | inline void Value::set_base_variable(const char * b_v) { 51 | (void)b_v; 52 | *(T*)base_variable = value; 53 | } 54 | 55 | //////////////////////Vector Definitions////////////////////// 56 | 57 | template 58 | inline Vector::Vector(std::vector & b_v, std::vectora, const char * hm) : CLI_Interface(&b_v, a, true, hm) {} 59 | 60 | template 61 | inline Vector::Vector(std::vector * b_v, std::vectora, const char * hm) : CLI_Interface(b_v, a, true, hm) {} 62 | 63 | template 64 | inline void Vector::set_base_variable(const char * b_v) { 65 | std::vector& base_variable_vector = *(std::vector *)base_variable; 66 | T temp; 67 | Var temp_var(temp, {}, true); 68 | temp_var.set_base_variable(b_v); 69 | base_variable_vector.push_back(temp); 70 | } 71 | 72 | /////////////////////////Template Specializations////////////////////////// 73 | 74 | inline Var::Var(char * b_v, std::vector a, bool ta, int b_s, const char * hm) : CLI_Interface(b_v, a, ta, hm), buffer_size(b_s) {} 75 | 76 | inline Var::Var(char & b_v, std::vector a, bool ta, const char * hm) : CLI_Interface(&b_v, a, ta, hm), buffer_size(1) {} 77 | 78 | inline void Var::set_base_variable(const char * b_v) { 79 | char * base_variable_string = (char *)base_variable; 80 | if (buffer_size == 1) { 81 | *base_variable_string = b_v[0]; 82 | return; 83 | } 84 | int i = 0; 85 | const int adj_buffer_size = buffer_size - 1; 86 | while (b_v[i] != '\0' && i < adj_buffer_size) { 87 | base_variable_string[i] = b_v[i]; 88 | i++; 89 | } 90 | base_variable_string[i] = '\0'; 91 | } 92 | 93 | template<> 94 | inline void Var::set_base_variable(const char * b_v) { 95 | *(int *)base_variable = strtol(b_v, nullptr, 10); 96 | } 97 | 98 | template<> 99 | inline void Var::set_base_variable(const char * b_v) { 100 | *(unsigned int *)base_variable = strtoul(b_v, nullptr, 10); 101 | } 102 | 103 | template<> 104 | inline void Var::set_base_variable(const char * b_v) { 105 | *(long *)base_variable = strtol(b_v, nullptr, 10); 106 | } 107 | 108 | template<> 109 | inline void Var::set_base_variable(const char * b_v) { 110 | *(unsigned long *)base_variable = strtoul(b_v, nullptr, 10); 111 | } 112 | 113 | template<> 114 | inline void Var::set_base_variable(const char * b_v) { 115 | *(long long *)base_variable = strtoll(b_v, nullptr, 10); 116 | } 117 | 118 | template<> 119 | inline void Var::set_base_variable(const char * b_v) { 120 | *(unsigned long long *)base_variable = strtoull(b_v, nullptr, 10); 121 | } 122 | 123 | template<> 124 | inline void Var::set_base_variable(const char * b_v) { 125 | *(float *)base_variable = strtof(b_v, nullptr); 126 | } 127 | 128 | template<> 129 | inline void Var::set_base_variable(const char * b_v) { 130 | *(double *)base_variable = strtod(b_v, nullptr); 131 | } 132 | 133 | template<> 134 | inline void Var::set_base_variable(const char * b_v) { 135 | *(long double *)base_variable = strtold(b_v, nullptr); 136 | } 137 | 138 | template<> 139 | inline void Vector::set_base_variable(const char * b_v) { 140 | std::vector& base_variable_vector = *(std::vector*)base_variable; 141 | base_variable_vector.push_back(b_v); 142 | } 143 | 144 | template<> 145 | inline void Vector::set_base_variable(const char * b_v) { 146 | (void)b_v; 147 | const char * error_message = "Because the length of the char buffers in the vector cannot " 148 | "be specified and you cannot set a char * to a const char *, you cannot use char " 149 | "as an acceptable type for a Vector. Use const char *, std::string, or " 150 | "another template overload."; 151 | throw std::invalid_argument(error_message); 152 | } 153 | 154 | template<> 155 | inline void Vector::set_base_variable(const char * b_v) { 156 | std::vector& base_variable_vector = *(std::vector*)base_variable; 157 | while (*b_v != '\0') { 158 | base_variable_vector.push_back(*b_v); 159 | b_v++; 160 | } 161 | } 162 | } 163 | #endif 164 | -------------------------------------------------------------------------------- /src/cpp_cli/w_specialization.h: -------------------------------------------------------------------------------- 1 | #ifndef WSpecialization_H 2 | #define WSpecialization_H 3 | #include "args_parser_templates.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | namespace cli { 9 | class WInterface; 10 | 11 | // Named after the -W flag of gcc 12 | class WSpecialization { 13 | friend class WInterface; 14 | friend class Var; 15 | private: 16 | std::unordered_map setters; 17 | public: 18 | WSpecialization(size_t initial_size) { 19 | setters.reserve(initial_size); 20 | } 21 | WInterface * operator[](const std::string& flag) { 22 | return setters[flag]; 23 | } 24 | }; 25 | 26 | class WInterface { 27 | protected: 28 | void * base_variable; 29 | public: 30 | WInterface(void * b_v, WSpecialization & w_s, std::string alias) : base_variable(b_v) { 31 | w_s.setters.insert({alias, this}); 32 | } 33 | virtual void set_base_variable(const char * flag) = 0; 34 | }; 35 | 36 | template 37 | class Wvalue : public WInterface { 38 | private: 39 | T value; 40 | public: 41 | Wvalue(T & b_v, WSpecialization & w_s, const char * flag, T v) : WInterface(&b_v, w_s, flag), value(v) {} 42 | virtual void set_base_variable(const char * flag) { 43 | if (flag[0] != '\0') { 44 | std::string error_message; 45 | error_message.reserve(64); 46 | error_message += flag; 47 | error_message += " does not require arguments."; 48 | throw std::invalid_argument(error_message); 49 | } 50 | *(T *)base_variable = value; 51 | } 52 | }; 53 | 54 | template 55 | class Warg : public WInterface { 56 | public: 57 | Warg(T & b_v, WSpecialization & w_s, const char * alias) : WInterface(&b_v, w_s, alias) {} 58 | virtual void set_base_variable(const char * arg) { 59 | *(T *)base_variable = arg; 60 | } 61 | }; 62 | 63 | template<> 64 | class Warg : public WInterface { 65 | private: 66 | int buffer_size; 67 | public: 68 | Warg(char * b_v, WSpecialization & w_s, const char * alias, int b_s) : WInterface(b_v, w_s, alias), buffer_size(b_s) {} 69 | virtual void set_base_variable(const char * arg) { 70 | char * base_variable_string = (char *)base_variable; 71 | int i = 0; 72 | while (arg[i] != '\0' && i < buffer_size) { 73 | base_variable_string[i] = arg[i]; 74 | i++; 75 | } 76 | base_variable_string[i] = '\0'; 77 | } 78 | }; 79 | 80 | template<> 81 | inline void Var::set_base_variable(const char * flag) { 82 | std::string flag_str = flag; 83 | size_t split_location = flag_str.find('='); 84 | std::string temp_flag = flag_str.substr(0, split_location); 85 | if (split_location == std::string::npos) { 86 | split_location = flag_str.length(); 87 | } else { 88 | split_location++; 89 | } 90 | if (((WSpecialization *)base_variable)->setters.count(temp_flag) == 0) { 91 | std::string error_message; 92 | error_message.reserve(200); 93 | error_message = "Option does not exist: -"; 94 | error_message += aliases[0]; 95 | error_message += temp_flag; 96 | throw std::invalid_argument(error_message); 97 | } 98 | ((WSpecialization *)base_variable)->setters[temp_flag]->set_base_variable(flag + split_location); 99 | } 100 | 101 | template<> 102 | inline void Warg::set_base_variable(const char * b_v) { 103 | *(int *)base_variable = strtol(b_v, nullptr, 10); 104 | } 105 | 106 | template<> 107 | inline void Warg::set_base_variable(const char * b_v) { 108 | *(unsigned int *)base_variable = strtoul(b_v, nullptr, 10); 109 | } 110 | 111 | template<> 112 | inline void Warg::set_base_variable(const char * b_v) { 113 | *(long *)base_variable = strtol(b_v, nullptr, 10); 114 | } 115 | 116 | template<> 117 | inline void Warg::set_base_variable(const char * b_v) { 118 | *(unsigned long *)base_variable = strtoul(b_v, nullptr, 10); 119 | } 120 | 121 | template<> 122 | inline void Warg::set_base_variable(const char * b_v) { 123 | *(long long *)base_variable = strtoll(b_v, nullptr, 10); 124 | } 125 | 126 | template<> 127 | inline void Warg::set_base_variable(const char * b_v) { 128 | *(unsigned long long *)base_variable = strtoull(b_v, nullptr, 10); 129 | } 130 | 131 | template<> 132 | inline void Warg::set_base_variable(const char * b_v) { 133 | *(float *)base_variable = strtof(b_v, nullptr); 134 | } 135 | 136 | template<> 137 | inline void Warg::set_base_variable(const char * b_v) { 138 | *(double *)base_variable = strtod(b_v, nullptr); 139 | } 140 | 141 | template<> 142 | inline void Warg::set_base_variable(const char * b_v) { 143 | *(long double *)base_variable = strtold(b_v, nullptr); 144 | } 145 | } 146 | #endif 147 | -------------------------------------------------------------------------------- /src/subcommand_example/bin/Makefile: -------------------------------------------------------------------------------- 1 | PRODUCT := subcom 2 | DEBUG_PRODUCT := $(PRODUCT)-debug 3 | BINDIR := . 4 | INCDIR := ../includes 5 | INCDIR_EXT := ../external_includes 6 | LIBDIR := ../libs 7 | SRCDIR := ../src 8 | OBJDIR := ../obj 9 | RELEASE_OBJDIR := $(OBJDIR)/release/ 10 | DEBUG_OBJDIR := $(OBJDIR)/debug/ 11 | 12 | MKDIR_P = mkdir -p 13 | 14 | # Language -------------------------------------------------------------------- 15 | EXTENSION := cpp 16 | LANGUAGE_STANDARD := -std=c++11 17 | COMPILER := g++ 18 | LINKER := g++ 19 | 20 | # Flags ----------------------------------------------------------------------- 21 | # ----- General --------------------------------------------------------------- 22 | INCLUDES := -I$(INCDIR) -I$(INCDIR_EXT) 23 | LIBRARIES := -L$(LIBDIR) 24 | WARNING_FLAGS := -Wall -Wextra 25 | DEPENDENCY_GENERATION_FLAGS := -MMD -MP 26 | 27 | # ----- Release --------------------------------------------------------------- 28 | UNUSED_CODE_COMPILER_FLAGS := -ffunction-sections -fdata-sections -flto 29 | OPTIMIZATION_LEVEL := -O3 30 | RELEASE_FLAGS := $(OPTIMIZATION_LEVEL) $(UNUSED_CODE_COMPILER_FLAGS) 31 | RELEASE_LINKER_FLAGS := -Wl,--gc-sections 32 | RELEASE_MACROS := 33 | 34 | # ----- Debug ----------------------------------------------------------------- 35 | DEBUG_FLAGS := -O0 -g 36 | DEBUG_MACROS := 37 | 38 | 39 | 40 | 41 | 42 | 43 | # ----------------------------------------------------------------------------- 44 | # DON'T MESS WITH ANYTHING AFTER THIS UNLESS YOU KNOW WHAT YOU'RE DOING ------- 45 | # ----------------------------------------------------------------------------- 46 | 47 | GENERAL_COMPILER_FLAGS := $(LANGUAGE_STANDARD) $(WARNING_FLAGS) $(DEPENDENCY_GENERATION_FLAGS) 48 | 49 | LINKER_FLAGS := $(RELEASE_LINKER_FLAGS) 50 | COMPILER_FLAGS := $(RELEASE_FLAGS) $(GENERAL_COMPILER_FLAGS) $(RELEASE_MACROS) 51 | 52 | # Finds all .$(EXTENSION) files and puts them into SRC 53 | SRC := $(wildcard $(SRCDIR)/*.$(EXTENSION)) 54 | # Creates .o files for every .$(EXTENSION) file in SRC (patsubst is pattern substitution) 55 | RELEASE_OBJ := $(patsubst $(SRCDIR)/%.$(EXTENSION),$(RELEASE_OBJDIR)/%.o,$(SRC)) 56 | DEBUG_OBJ := $(patsubst $(SRCDIR)/%.$(EXTENSION),$(DEBUG_OBJDIR)/%.o,$(SRC)) 57 | # Creates .d files (dependencies) for every .$(EXTENSION) file in SRC 58 | DEP := $(patsubst $(SRCDIR)/%.$(EXTENSION),$(OBJDIR)/%.d,$(SRC)) 59 | # Finds all lib*.a files and puts them into LIB 60 | LIB := $(wildcard $(LIBDIR)/lib*.a) 61 | 62 | # $^ is list of dependencies and $@ is the target file 63 | # Link all the object files or make a library 64 | $(BINDIR)/$(PRODUCT): directories $(RELEASE_OBJ) $(LIB) 65 | # Make a library 66 | # ar rcs $(BINDIR)/$(PRODUCT) $(RELEASE_OBJ) $(LIB) 67 | # Make a program 68 | $(LINKER) $(LINKER_FLAGS) $(COMPILER_FLAGS) $(RELEASE_OBJ) $(LIB) -o $@ 69 | 70 | $(BINDIR)/$(DEBUG_PRODUCT): directories $(DEBUG_OBJ) $(LIB) 71 | # Make a library 72 | # ar rcs $(BINDIR)/$(DEBUG_PRODUCT) $(DEBUG_OBJ) $(LIB) 73 | # Make a program 74 | $(LINKER) $(LINKER_FLAGS) $(COMPILER_FLAGS) $(DEBUG_OBJ) $(LIB) -o $@ 75 | 76 | # Compile individual .$(EXTENSION) source files into object files 77 | $(RELEASE_OBJDIR)/%.o: $(SRCDIR)/%.$(EXTENSION) 78 | $(COMPILER) $(COMPILER_FLAGS) $(INCLUDES) -c $< -o $@ 79 | 80 | $(DEBUG_OBJDIR)/%.o: $(SRCDIR)/%.$(EXTENSION) 81 | $(COMPILER) $(COMPILER_FLAGS) $(INCLUDES) -c $< -o $@ 82 | 83 | -include $(DEP) 84 | 85 | .PHONY: directories 86 | 87 | directories: $(OBJDIR) $(RELEASE_OBJDIR) $(DEBUG_OBJDIR) 88 | 89 | $(OBJDIR): 90 | $(MKDIR_P) $(OBJDIR) 91 | 92 | $(RELEASE_OBJDIR): 93 | $(MKDIR_P) $(RELEASE_OBJDIR) 94 | 95 | $(DEBUG_OBJDIR): 96 | $(MKDIR_P) $(DEBUG_OBJDIR) 97 | 98 | .PHONY: clean 99 | 100 | clean: 101 | rm -rf $(OBJDIR)/* $(PRODUCT) $(DEBUG_PRODUCT) $(BINDIR)/.*_help_file 102 | 103 | .PHONY: release 104 | 105 | release: COMPILER_FLAGS := $(RELEASE_FLAGS) $(GENERAL_COMPILER_FLAGS) 106 | release: LINKER_FLAGS := $(RELEASE_LINKER_FLAGS) 107 | release: directories $(RELEASE_OBJ) $(BINDIR)/$(PRODUCT) 108 | 109 | .PHONY: debug 110 | 111 | debug: COMPILER_FLAGS := $(DEBUG_FLAGS) $(GENERAL_COMPILER_FLAGS) 112 | debug: LINKER_FLAGS := 113 | debug: directories $(DEBUG_OBJ) $(BINDIR)/$(DEBUG_PRODUCT) 114 | -------------------------------------------------------------------------------- /src/subcommand_example/external_includes: -------------------------------------------------------------------------------- 1 | ../cpp_cli/ -------------------------------------------------------------------------------- /src/subcommand_example/src/subcommand_example.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp_cli.h" 2 | #include 3 | #include 4 | 5 | void push_prog(int argc, char ** argv, void * data); 6 | void test_prog(int argc, char ** argv, void * data); 7 | void pull_prog(int argc, char ** argv, void * data); 8 | 9 | bool help = false; 10 | 11 | struct Main_Subcommand_Variables { 12 | std::string file_path; 13 | size_t verbosity = 0; 14 | std::vector non_options; 15 | }; 16 | 17 | int main(int argc, char ** argv) { 18 | using namespace cli; 19 | Parser p; 20 | Main_Subcommand_Variables msv; 21 | p.arg(msv.file_path, { "path", "p" }, "Dummy variable."); 22 | p.repeated(msv.verbosity, { "v" }, "Dummy variable."); 23 | p.value(help, { "h", "help" }, true, "Prints this help message and exits."); 24 | 25 | p.add_subcommand("pull", pull_prog, "Does something like 'git pull' if this program actually did anything."); 26 | p.add_subcommand("push", push_prog, "Does something like 'git push' if this program actually did anything."); 27 | 28 | p.set_help_file_path(""); 29 | 30 | p.set_usage("[options/non-options] [subcommand]\n\t\t[subcommand's options/non-options] [subcommand's subcommand]\n\n" 31 | "Since this program kind of uses recursion when dealing with subcommands, the general pattern of [command] [command's options/non-options] [subcommand] is" 32 | " repeated, which each subcommand acting like a completely new command with its own options and subcommands."); 33 | 34 | p.set_header("This program serves mainly to demonstrate how subcommands work in the library. " 35 | "Essentially, each subcommand acts just like you wrote the program for the subcommand completely on its own without any " 36 | "knowledge of any supercommands or sibling commands except what information their supercommand passes to them, global variables, and the " 37 | "header, usage, and footers of their supercommands unless you change them. Changing them will have no effect on the help message of the " 38 | "supercommand, since you can only generate the help message before running 'Parser::parse'. " 39 | "In this case, 'push' and 'pull' are sibling commands, with both being subcommands to the main program, and 'push' has its own subcommand 'test'. " 40 | "Furthermore, they all have their own flags completely independent from any other subcommands."); 41 | 42 | p.set_footer("For more information, contact me at the.landfill.coding@gmail.com or on the github page. You could also put your version information stuff here, which would be cool."); 43 | 44 | p.generate_help(argv[0]); 45 | 46 | msv.non_options = p.parse(argc, argv, &msv); 47 | 48 | if (help) { 49 | p.print_help(); 50 | return 0; 51 | } 52 | 53 | std::cout << "file_path: " << msv.file_path << std::endl; 54 | std::cout << "verbosity: " << msv.verbosity << std::endl; 55 | 56 | std::string nesting = ""; 57 | for (size_t i = 0; i < msv.non_options.size(); i++) { 58 | if (msv.non_options[i] == nullptr) { 59 | nesting += "\t"; 60 | continue; 61 | } 62 | std::cout << nesting << "NON_OPTION " << std::to_string(i) << ": " << msv.non_options[i] << std::endl; 63 | } 64 | } 65 | 66 | void push_prog(int argc, char ** argv, void * data) { 67 | using namespace cli; 68 | Parser p; 69 | Main_Subcommand_Variables * fixed_data = static_cast(data); 70 | std::string URL = ""; 71 | p.arg(URL, { "u", "URL" }, "Sets the URL."); 72 | p.value(help, { "h", "help" }, true, "Displays this help message and exits."); 73 | 74 | p.add_subcommand("test", test_prog); 75 | p.generate_help(argv[0]); 76 | try { 77 | p.parse(argc, argv, &URL); 78 | } catch (std::invalid_argument& e) { 79 | std::cerr << "INVALID ARGUMENT FOR SUBCOMMAND PUSH" << std::endl; 80 | throw; 81 | } 82 | 83 | if (help) { 84 | return; 85 | } 86 | std::cout << std::endl << "IN SUBCOMMAND PUSH" << std::endl; 87 | std::cout << "argc: " << argc << std::endl; 88 | std::cout << "argv[0]: " << argv[0] << std::endl; 89 | std::cout << "file_path: " << fixed_data->file_path << std::endl; 90 | std::cout << "verbosity: " << fixed_data->verbosity << std::endl; 91 | 92 | 93 | std::cout << "URL: " << URL << std::endl; 94 | std::cout << "LEAVING SUBCOMMAND PUSH" << std::endl << std::endl; 95 | } 96 | 97 | void pull_prog(int argc, char ** argv, void * data) { 98 | using namespace cli; 99 | Parser p; 100 | (void)data; 101 | std::string URL = ""; 102 | unsigned long long timeout = 100; 103 | p.arg(URL, { "u", "URL" }, "Sets the URL."); 104 | p.arg(timeout, { "t", "timeout" }, "Sets the amount of time before a timeout."); 105 | p.value(help, { "h", "help" }, true, "Displays this help message and exits."); 106 | p.generate_help(argv[0]); 107 | try { 108 | p.parse(argc, argv, nullptr); 109 | } catch (std::invalid_argument& e) { 110 | std::cerr << "INVALID ARGUMENT FOR SUBCOMMAND PULL" << std::endl; 111 | throw; 112 | } 113 | 114 | if (help) { 115 | return; 116 | } 117 | std::cout << std::endl << "IN SUBCOMMAND PULL" << std::endl; 118 | std::cout << "argc: " << argc << std::endl; 119 | std::cout << "argv[0]: " << argv[0] << std::endl; 120 | 121 | 122 | std::cout << "URL: " << URL << std::endl; 123 | std::cout << "Timeout: " << timeout << std::endl; 124 | std::cout << "LEAVING SUBCOMMAND PULL" << std::endl << std::endl; 125 | } 126 | 127 | void test_prog(int argc, char ** argv, void * data) { 128 | using namespace cli; 129 | Parser p; 130 | int underwear_count = 0; 131 | double EURL = -1; 132 | 133 | p.arg(underwear_count, { "u" }, "Sets the number of underwear currently available. Note that this has no idea that '-u' corresponded to the URL variable in the push subcommand."); 134 | p.arg(EURL, { "e", "E", "EURL", "URL" }, "Sets the value of EURL. Note that '--URL' and '-u' correspond to completely different variables."); 135 | p.value(help, { "h", "help" }, true, "Displays this help message and exits."); 136 | 137 | p.set_usage("\n\tTest."); 138 | 139 | p.set_header("I'm just changing this to show you that changing any of these help message variables does nothing to any other help message."); 140 | 141 | p.generate_help(argv[0]); 142 | p.parse(argc, argv, nullptr); 143 | 144 | if (help) { 145 | return; 146 | } 147 | 148 | std::cout << std::endl << "IN SUBCOMMAND TEST" << std::endl; 149 | std::cout << "argc: " << argc << std::endl; 150 | std::cout << "argv[0]: " << argv[0] << std::endl; 151 | std::cout << "URL: " << *(std::string *)data << std::endl; 152 | 153 | std::cout << "underwear_count: " << underwear_count << std::endl; 154 | std::cout << "EURL: " << EURL << std::endl; 155 | } 156 | -------------------------------------------------------------------------------- /src/test_cpp_command_line_parser/bin/Makefile: -------------------------------------------------------------------------------- 1 | PRODUCT := cli-test 2 | DEBUG_PRODUCT := $(PRODUCT)-debug 3 | BINDIR := . 4 | INCDIR := ../includes 5 | INCDIR_EXT := ../external_includes 6 | LIBDIR := ../libs 7 | SRCDIR := ../src 8 | OBJDIR := ../obj 9 | RELEASE_OBJDIR := $(OBJDIR)/release/ 10 | DEBUG_OBJDIR := $(OBJDIR)/debug/ 11 | 12 | MKDIR_P = mkdir -p 13 | 14 | # Language -------------------------------------------------------------------- 15 | EXTENSION := cpp 16 | LANGUAGE_STANDARD := -std=c++11 17 | COMPILER := g++ 18 | LINKER := g++ 19 | 20 | # Flags ----------------------------------------------------------------------- 21 | # ----- General --------------------------------------------------------------- 22 | INCLUDES := -I$(INCDIR) -I$(INCDIR_EXT) 23 | LIBRARIES := -L$(LIBDIR) 24 | WARNING_FLAGS := -Wall -Wextra 25 | DEPENDENCY_GENERATION_FLAGS := -MMD -MP 26 | 27 | # ----- Release --------------------------------------------------------------- 28 | UNUSED_CODE_COMPILER_FLAGS := -ffunction-sections -fdata-sections -flto 29 | OPTIMIZATION_LEVEL := -O3 30 | RELEASE_FLAGS := $(OPTIMIZATION_LEVEL) $(UNUSED_CODE_COMPILER_FLAGS) 31 | RELEASE_LINKER_FLAGS := -Wl,--gc-sections 32 | RELEASE_MACROS := 33 | 34 | # ----- Debug ----------------------------------------------------------------- 35 | DEBUG_FLAGS := -O0 -g 36 | DEBUG_MACROS := 37 | 38 | 39 | 40 | 41 | 42 | 43 | # ----------------------------------------------------------------------------- 44 | # DON'T MESS WITH ANYTHING AFTER THIS UNLESS YOU KNOW WHAT YOU'RE DOING ------- 45 | # ----------------------------------------------------------------------------- 46 | 47 | GENERAL_COMPILER_FLAGS := $(LANGUAGE_STANDARD) $(WARNING_FLAGS) $(DEPENDENCY_GENERATION_FLAGS) 48 | 49 | LINKER_FLAGS := $(RELEASE_LINKER_FLAGS) 50 | COMPILER_FLAGS := $(RELEASE_FLAGS) $(GENERAL_COMPILER_FLAGS) $(RELEASE_MACROS) 51 | 52 | # Finds all .$(EXTENSION) files and puts them into SRC 53 | SRC := $(wildcard $(SRCDIR)/*.$(EXTENSION)) 54 | # Creates .o files for every .$(EXTENSION) file in SRC (patsubst is pattern substitution) 55 | RELEASE_OBJ := $(patsubst $(SRCDIR)/%.$(EXTENSION),$(RELEASE_OBJDIR)/%.o,$(SRC)) 56 | DEBUG_OBJ := $(patsubst $(SRCDIR)/%.$(EXTENSION),$(DEBUG_OBJDIR)/%.o,$(SRC)) 57 | # Creates .d files (dependencies) for every .$(EXTENSION) file in SRC 58 | DEP := $(patsubst $(SRCDIR)/%.$(EXTENSION),$(OBJDIR)/%.d,$(SRC)) 59 | # Finds all lib*.a files and puts them into LIB 60 | LIB := $(wildcard $(LIBDIR)/lib*.a) 61 | 62 | # $^ is list of dependencies and $@ is the target file 63 | # Link all the object files or make a library 64 | $(BINDIR)/$(PRODUCT): directories $(RELEASE_OBJ) $(LIB) 65 | # Make a library 66 | # ar rcs $(BINDIR)/$(PRODUCT) $(RELEASE_OBJ) $(LIB) 67 | # Make a program 68 | $(LINKER) $(LINKER_FLAGS) $(COMPILER_FLAGS) $(RELEASE_OBJ) $(LIB) -o $@ 69 | 70 | $(BINDIR)/$(DEBUG_PRODUCT): directories $(DEBUG_OBJ) $(LIB) 71 | # Make a library 72 | # ar rcs $(BINDIR)/$(DEBUG_PRODUCT) $(DEBUG_OBJ) $(LIB) 73 | # Make a program 74 | $(LINKER) $(LINKER_FLAGS) $(COMPILER_FLAGS) $(DEBUG_OBJ) $(LIB) -o $@ 75 | 76 | # Compile individual .$(EXTENSION) source files into object files 77 | $(RELEASE_OBJDIR)/%.o: $(SRCDIR)/%.$(EXTENSION) 78 | $(COMPILER) $(COMPILER_FLAGS) $(INCLUDES) -c $< -o $@ 79 | 80 | $(DEBUG_OBJDIR)/%.o: $(SRCDIR)/%.$(EXTENSION) 81 | $(COMPILER) $(COMPILER_FLAGS) $(INCLUDES) -c $< -o $@ 82 | 83 | -include $(DEP) 84 | 85 | .PHONY: directories 86 | 87 | directories: $(OBJDIR) $(RELEASE_OBJDIR) $(DEBUG_OBJDIR) 88 | 89 | $(OBJDIR): 90 | $(MKDIR_P) $(OBJDIR) 91 | 92 | $(RELEASE_OBJDIR): 93 | $(MKDIR_P) $(RELEASE_OBJDIR) 94 | 95 | $(DEBUG_OBJDIR): 96 | $(MKDIR_P) $(DEBUG_OBJDIR) 97 | 98 | .PHONY: clean 99 | 100 | clean: 101 | rm -rf $(OBJDIR)/* $(PRODUCT) $(DEBUG_PRODUCT) $(BINDIR)/.*_help_file 102 | 103 | .PHONY: release 104 | 105 | release: COMPILER_FLAGS := $(RELEASE_FLAGS) $(GENERAL_COMPILER_FLAGS) 106 | release: LINKER_FLAGS := $(RELEASE_LINKER_FLAGS) 107 | release: directories $(RELEASE_OBJ) $(BINDIR)/$(PRODUCT) 108 | 109 | .PHONY: debug 110 | 111 | debug: COMPILER_FLAGS := $(DEBUG_FLAGS) $(GENERAL_COMPILER_FLAGS) 112 | debug: LINKER_FLAGS := 113 | debug: directories $(DEBUG_OBJ) $(BINDIR)/$(DEBUG_PRODUCT) 114 | -------------------------------------------------------------------------------- /src/test_cpp_command_line_parser/external_includes: -------------------------------------------------------------------------------- 1 | ../cpp_cli/ -------------------------------------------------------------------------------- /src/test_cpp_command_line_parser/src/test_cmd_library.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp_cli.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char ** argv) { 7 | using namespace cli; 8 | std::string filename = "a.out"; 9 | int recursion_level = -1; 10 | char flag = '\0'; 11 | std::string flag2 = "false"; 12 | double probability_of_success = 0.0001; 13 | std::string standard_input_hyphen = ""; 14 | size_t verbosity; 15 | bool help = false; 16 | std::vector list_of_ints; 17 | std::vector list_of_declarations; 18 | list_of_ints.reserve(30); 19 | 20 | // non_options is a vector of args that did not start with a hyphen, did not 21 | // start with a double hyphen, or came after the special argument "--". 22 | // non_options orders its elements by the same order they came in on the 23 | // command line. 24 | std::vector non_options; 25 | 26 | // This scoping is just to automatically delete the Vars after 27 | // they are no longer necessary. They need to be in accessable in the same 28 | // scope as when hash is called. 29 | 30 | Parser p; 31 | char header[] = "This test program will demonstrate the basic functionality of this library. Other test programs will demonstrate more advances functionality. This is just going to be a really long test header to demonstrate that the function that will break this into lines will work, so I have to add a bunch of unecessary information after the first few sentences so I can make sure that the formatter works."; 32 | 33 | char usage[] = "[options/non-options]\n\nIt doesn't really matter what you put here since none of the positions matter."; 34 | 35 | char footer[] = "For more information, contact me at the.landfill.coding@gmail.com or on the github page. You could also put your version information stuff here, which would be cool."; 36 | 37 | p.set_header(header); 38 | p.set_usage(usage); 39 | p.set_footer(footer); 40 | 41 | // The help file path must be set or else the program will throw an 42 | // exception. The help file path must also be a valid, writable path. 43 | // "" just means current directory. Do not use relative paths unless 44 | // you only plan on running it from a single folder. 45 | p.set_help_file_path(""); 46 | 47 | p.arg(filename, { "f", "file", "filename" }, "Determines the file to be read. In this program, though, it doesn't do anything."); 48 | p.arg(recursion_level, { "r", "recursion", "max-depth" }, "Determines the maximum level of recursion allowed before nothing happens because this is a test program."); 49 | p.arg(probability_of_success, { "p", "prob", "probability" }, "Sets the probability of this program working properly, which is mostly dependent on whether or not I forgot a minor syntax error."); 50 | p.repeated(verbosity, { "v" }, "This is a standard verbosity variable that is supposed to set increasing levels of verbosity, so -v would mean be a little verbose, -vvvv would mean be very verbose, etc."); 51 | 52 | // Value s set the variable to the third argument. 53 | // If --flag or -a is passed, flag will be set to 'a'. If --no-flag or 54 | // -b is passed, flag will be set to 'b'. 55 | p.value(flag, { "flag", "a" }, 'a', "This uses the improved Value syntax to make sure that multiple flags set a variable to the same flag. Notice that it is of 'char' type and that flag is not an array."); 56 | p.value(flag, { "no-flag", "b" }, 'b', "Same as --flag, -a, except it will set the flag variable to a different value."); 57 | p.value(flag, { "some-flag", "c" }, 'c', "Same as --flag, -a, except it will set the flag variable to a different value."); 58 | 59 | // Generally, the solitary hyphen flag is used to indicate that the 60 | // program should take in standard input. The library will tell you that 61 | // someone typed a hyphen, and it is up to you to determine what you 62 | // want to do with it. It is not necessary to do anything with it. If 63 | // Unless you do something with it, the hyphen will be ignored. 64 | p.arg(standard_input_hyphen, { "-" }, "This is just a standard input hyphen, which normally means that your program wants to take in user input from stdin. It's only here to demonstrate that this library can parse it."); 65 | 66 | // This will put any argument starting with "-l" or "--library" into 67 | // non_options, even though it's still an option, as its order matters. 68 | // The type doesn't matter as you're not setting a variable. You will 69 | // have to take template specializations into account, such as char. 70 | // You should also set takes_args to be true, or else its position does 71 | // not matter. 72 | p.ignored({ "l", "library" }, "Flag that corresponds to gcc's -l flag. It is a position dependent flag."); 73 | 74 | // Having the same flag refer to two different Vars will 75 | // cause the program to fail and tell you which flag. 76 | // Var breaks_program(nullptr, { "l" }, false); 77 | 78 | p.value(help, { "h", "help" }, true, "Prints this help message and exits."); 79 | 80 | p.vector(list_of_ints, { "i", "list" }, "Just here to demonstrate that it can deal with vectors of arguments."); 81 | p.vector(list_of_declarations, { "D" }, "Just here to demonstrate that const char *'s work."); 82 | 83 | p.generate_help(argv[0]); 84 | 85 | // Non options must be declared outside the scope unless you don't 86 | // need to use them. 87 | non_options = p.parse(argc, argv); 88 | 89 | // This section just prints out the values to demonstrate that hash worked. 90 | if (help) { 91 | p.print_help(); 92 | } else { 93 | std::cout << "filename:\t" << filename << std::endl; 94 | std::cout << "recursion:\t" << recursion_level << std::endl; 95 | std::cout << "flag:\t\t'" << flag << "'" << std::endl; 96 | std::cout << "flag2:\t\t" << flag2 << std::endl; 97 | std::cout << "probability_of_success:\t" << probability_of_success << std::endl; 98 | std::cout << "standard_input_hyphen:\t" << standard_input_hyphen << std::endl; 99 | std::cout << "verbosity:\t" << verbosity << std::endl; 100 | std::string list_of_ints_string; 101 | list_of_ints_string.reserve(100); 102 | for (size_t i = 0; i < list_of_ints.size(); i++) { 103 | list_of_ints_string += std::to_string(list_of_ints[i]); 104 | list_of_ints_string += ", "; 105 | } 106 | std::cout << "List of ints:\t[ " << list_of_ints_string.substr(0, list_of_ints_string.length() - 2) << " ]" << std::endl; 107 | std::cout << "List of ints size:\t" << std::to_string(list_of_ints.size()) << std::endl; 108 | std::string list_of_declarations_string = ""; 109 | list_of_declarations_string.reserve(1000); 110 | for (size_t i = 0; i < list_of_declarations.size(); i++) { 111 | list_of_declarations_string += list_of_declarations[i]; 112 | list_of_declarations_string += ", "; 113 | } 114 | std::cout << "List of declarations:\t[ " << list_of_declarations_string.substr(0, list_of_declarations_string.length() - 2) << " ]" << std::endl; 115 | for (size_t i = 0; i < non_options.size(); i++) { 116 | std::cout << "NON_OPTION " << i << ":\t" << non_options[i] << std::endl; 117 | } 118 | } 119 | 120 | return 0; 121 | } 122 | -------------------------------------------------------------------------------- /src/w_specialization_example/bin/Makefile: -------------------------------------------------------------------------------- 1 | PRODUCT := cli-w 2 | DEBUG_PRODUCT := $(PRODUCT)-debug 3 | BINDIR := . 4 | INCDIR := ../includes 5 | INCDIR_EXT := ../external_includes 6 | LIBDIR := ../libs 7 | SRCDIR := ../src 8 | OBJDIR := ../obj 9 | RELEASE_OBJDIR := $(OBJDIR)/release/ 10 | DEBUG_OBJDIR := $(OBJDIR)/debug/ 11 | 12 | MKDIR_P = mkdir -p 13 | 14 | # Language -------------------------------------------------------------------- 15 | EXTENSION := cpp 16 | LANGUAGE_STANDARD := -std=c++11 17 | COMPILER := g++ 18 | LINKER := g++ 19 | 20 | # Flags ----------------------------------------------------------------------- 21 | # ----- General --------------------------------------------------------------- 22 | INCLUDES := -I$(INCDIR) -I$(INCDIR_EXT) 23 | LIBRARIES := -L$(LIBDIR) 24 | WARNING_FLAGS := -Wall -Wextra 25 | DEPENDENCY_GENERATION_FLAGS := -MMD -MP 26 | 27 | # ----- Release --------------------------------------------------------------- 28 | UNUSED_CODE_COMPILER_FLAGS := -ffunction-sections -fdata-sections -flto 29 | OPTIMIZATION_LEVEL := -O3 30 | RELEASE_FLAGS := $(OPTIMIZATION_LEVEL) $(UNUSED_CODE_COMPILER_FLAGS) 31 | RELEASE_LINKER_FLAGS := -Wl,--gc-sections 32 | RELEASE_MACROS := 33 | 34 | # ----- Debug ----------------------------------------------------------------- 35 | DEBUG_FLAGS := -O0 -g 36 | DEBUG_MACROS := 37 | 38 | 39 | 40 | 41 | 42 | 43 | # ----------------------------------------------------------------------------- 44 | # DON'T MESS WITH ANYTHING AFTER THIS UNLESS YOU KNOW WHAT YOU'RE DOING ------- 45 | # ----------------------------------------------------------------------------- 46 | 47 | GENERAL_COMPILER_FLAGS := $(LANGUAGE_STANDARD) $(WARNING_FLAGS) $(DEPENDENCY_GENERATION_FLAGS) 48 | 49 | LINKER_FLAGS := $(RELEASE_LINKER_FLAGS) 50 | COMPILER_FLAGS := $(RELEASE_FLAGS) $(GENERAL_COMPILER_FLAGS) $(RELEASE_MACROS) 51 | 52 | # Finds all .$(EXTENSION) files and puts them into SRC 53 | SRC := $(wildcard $(SRCDIR)/*.$(EXTENSION)) 54 | # Creates .o files for every .$(EXTENSION) file in SRC (patsubst is pattern substitution) 55 | RELEASE_OBJ := $(patsubst $(SRCDIR)/%.$(EXTENSION),$(RELEASE_OBJDIR)/%.o,$(SRC)) 56 | DEBUG_OBJ := $(patsubst $(SRCDIR)/%.$(EXTENSION),$(DEBUG_OBJDIR)/%.o,$(SRC)) 57 | # Creates .d files (dependencies) for every .$(EXTENSION) file in SRC 58 | DEP := $(patsubst $(SRCDIR)/%.$(EXTENSION),$(OBJDIR)/%.d,$(SRC)) 59 | # Finds all lib*.a files and puts them into LIB 60 | LIB := $(wildcard $(LIBDIR)/lib*.a) 61 | 62 | # $^ is list of dependencies and $@ is the target file 63 | # Link all the object files or make a library 64 | $(BINDIR)/$(PRODUCT): directories $(RELEASE_OBJ) $(LIB) 65 | # Make a library 66 | # ar rcs $(BINDIR)/$(PRODUCT) $(RELEASE_OBJ) $(LIB) 67 | # Make a program 68 | $(LINKER) $(LINKER_FLAGS) $(COMPILER_FLAGS) $(RELEASE_OBJ) $(LIB) -o $@ 69 | 70 | $(BINDIR)/$(DEBUG_PRODUCT): directories $(DEBUG_OBJ) $(LIB) 71 | # Make a library 72 | # ar rcs $(BINDIR)/$(DEBUG_PRODUCT) $(DEBUG_OBJ) $(LIB) 73 | # Make a program 74 | $(LINKER) $(LINKER_FLAGS) $(COMPILER_FLAGS) $(DEBUG_OBJ) $(LIB) -o $@ 75 | 76 | # Compile individual .$(EXTENSION) source files into object files 77 | $(RELEASE_OBJDIR)/%.o: $(SRCDIR)/%.$(EXTENSION) 78 | $(COMPILER) $(COMPILER_FLAGS) $(INCLUDES) -c $< -o $@ 79 | 80 | $(DEBUG_OBJDIR)/%.o: $(SRCDIR)/%.$(EXTENSION) 81 | $(COMPILER) $(COMPILER_FLAGS) $(INCLUDES) -c $< -o $@ 82 | 83 | -include $(DEP) 84 | 85 | .PHONY: directories 86 | 87 | directories: $(OBJDIR) $(RELEASE_OBJDIR) $(DEBUG_OBJDIR) 88 | 89 | $(OBJDIR): 90 | $(MKDIR_P) $(OBJDIR) 91 | 92 | $(RELEASE_OBJDIR): 93 | $(MKDIR_P) $(RELEASE_OBJDIR) 94 | 95 | $(DEBUG_OBJDIR): 96 | $(MKDIR_P) $(DEBUG_OBJDIR) 97 | 98 | .PHONY: clean 99 | 100 | clean: 101 | rm -rf $(OBJDIR)/* $(PRODUCT) $(DEBUG_PRODUCT) 102 | 103 | .PHONY: release 104 | 105 | release: COMPILER_FLAGS := $(RELEASE_FLAGS) $(GENERAL_COMPILER_FLAGS) 106 | release: LINKER_FLAGS := $(RELEASE_LINKER_FLAGS) 107 | release: directories $(RELEASE_OBJ) $(BINDIR)/$(PRODUCT) 108 | 109 | .PHONY: debug 110 | 111 | debug: COMPILER_FLAGS := $(DEBUG_FLAGS) $(GENERAL_COMPILER_FLAGS) 112 | debug: LINKER_FLAGS := 113 | debug: directories $(DEBUG_OBJ) $(BINDIR)/$(DEBUG_PRODUCT) 114 | -------------------------------------------------------------------------------- /src/w_specialization_example/external_includes: -------------------------------------------------------------------------------- 1 | ../cpp_cli/ -------------------------------------------------------------------------------- /src/w_specialization_example/src/w_specialization.cpp: -------------------------------------------------------------------------------- 1 | #include "cpp_cli.h" 2 | #include "w_specialization.h" 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char ** argv) { 8 | using namespace cli; 9 | std::string filename = "a.out"; 10 | int recursion_level = -1; 11 | std::string flag = "false"; 12 | std::string flag2 = "false"; 13 | double probability_of_success = 0.0001; 14 | bool standard_input_hyphen = false; 15 | size_t verbosity = 0; 16 | std::vector non_options; 17 | 18 | bool w_sign_conversion = true; 19 | bool w_all = false; 20 | bool w_extra = false; 21 | int w_error_level = 1; 22 | int w_warning_level = 1; 23 | char w_type = 'x'; 24 | 25 | bool d_ignore_parentheses = false; 26 | bool d_allow_infinite_recursion = false; 27 | std::string d_configuration_name = ""; 28 | int d_debug_level = 0; 29 | 30 | bool help = false; 31 | 32 | { 33 | Parser p; 34 | p.arg(filename, { "f", "file", "filename" }, "Dummy variable corresponding to some sort of file name, usually either and input or an output file."); 35 | p.arg(recursion_level, { "r", "recursion", "max-depth" }, "Dummy variable corresponding to some sort of recursion restriction."); 36 | p.arg(probability_of_success, { "p", "prob", "probability" }, "Determines the probability that this program will work."); 37 | p.repeated(verbosity, { "v" }, "Dummy variable that corresponds to an option to set the verbosity."); 38 | p.value(standard_input_hyphen, { "-" }, true, "Just here to show that the standard input hyphen still works with all the W_specialization stuff."); 39 | p.ignored({ "l", "library" }, "I copied these from an earlier test program."); 40 | 41 | // YOU DO NOT NEED TO USE WSpecializationS, WvalueS, or WargS 42 | WSpecialization w_options(100); 43 | Wvalue w_sign_conversion_var(w_sign_conversion, w_options, "sign-conversion", true); 44 | Wvalue w_no_sign_conversion_var(w_sign_conversion, w_options, "no-sign-conversion" , false); 45 | Wvalue w_all_var(w_all, w_options, "all", true); 46 | Wvalue w_extra_var(w_extra, w_options, "extra", true); 47 | Wvalue w_type_a_var(w_type, w_options, "file", 'f'); 48 | Wvalue w_type_b_var(w_type, w_options, "dir", 'd'); 49 | Wvalue w_type_c_var(w_type, w_options, "link", 'l'); 50 | Wvalue w_type_d_var(w_type, w_options, "any", 'a'); 51 | Warg w_error_level_var(w_error_level, w_options, "error-level"); 52 | Warg w_warning_level_var(w_warning_level, w_options, "warning-level"); 53 | p.arg(w_options, { "W" }, "Only the -W main argument has a help message at this point because these options are better documented outside of a simple help message."); 54 | 55 | WSpecialization d_options(100); 56 | Wvalue d_ignore_parentheses_var(d_ignore_parentheses, d_options, "ignore-parentheses", true); 57 | Wvalue d_allow_infinite_recursion_var(d_allow_infinite_recursion, d_options, "allow-infinite-recursion", true); 58 | Wvalue d_no_infinite_recursion_var(d_allow_infinite_recursion, d_options, "no-infinite-recursion", false); 59 | Warg d_configuration_name_var(d_configuration_name, d_options, "config"); 60 | Warg d_debug_level_var(d_debug_level, d_options, "level"); 61 | p.arg(d_options, { "D" }, "Just here for fun."); 62 | 63 | p.value(help, { "help", "h" }, true, "Displays this help message and exits."); 64 | 65 | const char * header = "This program will basically show you that this library can emulate gcc's -W flag for multiple variables." 66 | " While I'm here, I might as well show you that your compiler will concatenate string literals if you put them in sequence, " 67 | "which should make them easier to read, especially if you plan on writing a big long description like what I'm writing right now. The library will also make " 68 | "sure all this text is formatted properly within whatever help width you specify. The default is 80 characters, but I've set it to 60 to prove that you can."; 69 | 70 | const char * usage = "[options/non-options]\n\n" 71 | "Once again, these test programs don't really have any superstructure to the options/non-options, so I can't really be any more specific."; 72 | 73 | const char * footer = "For more information, contact me at the.landfill.coding@gmail.com or on the github page. You could also put your version information" 74 | " stuff here, which would be cool."; 75 | 76 | p.set_header(header); 77 | p.set_usage(usage); 78 | p.set_footer(footer); 79 | p.set_help_width(90); 80 | p.set_help_file_path(""); 81 | 82 | p.generate_help(argv[0]); 83 | 84 | non_options = p.parse(argc, argv); 85 | 86 | if (help) { 87 | p.print_help(); 88 | return 0; 89 | } 90 | } 91 | 92 | std::cout << "--------------------STANDARD--------------------" << std::endl; 93 | std::cout << "filename:\t" << filename << std::endl; 94 | std::cout << "recursion:\t" << recursion_level << std::endl; 95 | std::cout << "flag:\t\t" << flag << std::endl; 96 | std::cout << "flag2:\t\t" << flag2 << std::endl; 97 | std::cout << "probability_of_success:\t" << probability_of_success << std::endl; 98 | std::cout << "standard_input_hyphen:\t" << standard_input_hyphen << std::endl; 99 | std::cout << "verbosity:\t" << verbosity << std::endl; 100 | 101 | std::cout << "--------------------W FLAGS---------------------" << std::endl; 102 | std::cout << "w_sign_conversion:\t" << w_sign_conversion << std::endl; 103 | std::cout << "w_all:\t" << w_all << std::endl; 104 | std::cout << "w_extra:\t" << w_extra << std::endl; 105 | std::cout << "w_error_level:\t" << w_error_level << std::endl; 106 | std::cout << "w_warning_level:\t" << w_warning_level << std::endl; 107 | std::cout << "w_type:\t" << w_type << std::endl; 108 | 109 | std::cout << "--------------------D FLAGS---------------------" << std::endl; 110 | std::cout << "d_ignore_parentheses:\t" << d_ignore_parentheses << std::endl; 111 | std::cout << "d_allow_infinite_recursion:\t" << d_allow_infinite_recursion << std::endl; 112 | std::cout << "d_configuration_name:\t" << d_configuration_name << std::endl; 113 | std::cout << "d_debug_level:\t" << d_debug_level << std::endl; 114 | 115 | for (size_t i = 0; i < non_options.size(); i++) { 116 | std::cout << "NON_OPTION " << i << ":\t" << non_options[i] << std::endl; 117 | } 118 | return 0; 119 | } 120 | --------------------------------------------------------------------------------