commandoptions/0002755000175000017500000000000011105653117012232 5ustar oleolecommandoptions/commandoptions.cpp0000644000175000017500000002372307733106764016012 0ustar oleole// Implementation of the command line parsing class. // // Copyright (C) 2001, 02, 03 Ole Laursen // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the // Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307 USA. #include #include #include #include #include // for exit #include #include "commandoptions.hpp" // true if val is in container con template bool is_in_container(T val, C con) { return std::find(con.begin(), con.end(), val) != con.end(); } void commandoptions::process_command_line(int argc, const char *argv[]) { argument_iterator ait = argument_table.begin(); std::vector processed_flags; int i = 1; while (i < argc) { std::string s(argv[i]); if (s[0] == '-') { // option encountered if (s.size() == 1) // ensure we got some meat to work on throw commandoptions_error("'-' specified without option name"); if (s[1] == '-') { // long option if (s.size() == 2) // ensure we got some meat to work on throw commandoptions_error("'--' specified without option name"); std::string name = s.substr(2); // catch the built-in options if (name == "help") print_help(argv[0]); if (name == "usage") print_usage(argv[0]); // search flag table for (flag_iterator it = flag_table.begin(); it != flag_table.end(); ++it) if (it->long_name == name) { // make sure we don't get "-f -f" = "" if (!is_in_container(it, processed_flags)) { *(it->par) = !(*(it->par)); // reverse flag processed_flags.push_back(it); // remember this flag } goto next_token; } // then the option table for (option_iterator it = option_table.begin(); it != option_table.end(); ++it) if (it->long_name == name) { if (++i >= argc) // check that there's one more waiting throw commandoptions_error("option '--" + name + "' specified without argument"); else if (!it->par->update(argv[i])) throw commandoptions_error("invalid argument '" + std::string(argv[i]) + "' for option '--" + name + "'"); goto next_token; } // if we get here, the option wasn't recognized throw commandoptions_error("unrecognized option '" + s + "'"); } else { // short option(s) bool has_had_option = false; // loop through entire string in case some flags were grouped unsigned int j = 1; while (j < s.length()) { char name = s[j]; // catch the built-in short help option if (name == '?') print_help(argv[0]); // search flag table for (flag_iterator it = flag_table.begin(); it != flag_table.end(); ++it) if (it->short_name == name) { if (!is_in_container(it, processed_flags)) { *(it->par) = !(*(it->par)); // reverse flag processed_flags.push_back(it); // remember the flag } goto next_option_in_group; } // then the option table for (option_iterator it = option_table.begin(); it != option_table.end(); ++it) if (it->short_name == name) { if (has_had_option) throw commandoptions_error("more than one option in the option group '" + s + "' requires an argument"); else if (++i >= argc) // ensure that there's one more waiting throw commandoptions_error(std::string("option '-") + name + "' specified without argument"); else if (!it->par->update(argv[i])) throw commandoptions_error("invalid argument '" + std::string(argv[i]) + "' for option '-" + name + "'"); has_had_option = true; goto next_option_in_group; } // if we get here, the option wasn't recognized throw commandoptions_error(std::string("unrecognized option '-") + name + "'"); next_option_in_group: ++j; } } } else { // just a normal argument if (ait == argument_table.end()) { if (leftover_arguments != 0) leftover_arguments->add(s); else throw commandoptions_error("too many arguments specified (last one processed is '" + s + "')"); } else if (!ait->par->update(s)) throw commandoptions_error("invalid argument '" + s + "' passed as " + requirify(ait->name)); else ++ait; // prepare for next argument } next_token: ++i; } // check that we got enough arguments if (ait != argument_table.end()) { std::string missing; while (ait != argument_table.end()) { // build up list of missing arguments missing += " '" + requirify(ait->name) + "'"; ++ait; } throw commandoptions_error("not enough arguments specified, missing" + missing); } } // FIXME: switch to dedicated functions for handling encountered options, the // above function is much too long std::string commandoptions::strip_path(std::string s) { #ifdef HAVE_DOS return s.substr(s.rfind('\\') + 1); #else return s.substr(s.rfind('/') + 1); #endif } void commandoptions::print_usage(std::string executable) { // print usage and program name std::cerr << "Usage: " << strip_path(executable); // gather and print flags std::string shorts = "-"; // we sort the flags into short and long ones std::string longs; for (flag_iterator i = flag_table.begin(); i != flag_table.end(); ++i) { if (i->short_name != '\0') shorts += i->short_name; else longs += ' ' + optionalify("--" + i->long_name); } // insert -? (for help) shorts += '?'; std::cerr << ' ' << optionalify(shorts) << longs; // print options for (option_iterator i = option_table.begin(); i != option_table.end(); ++i) if (i->short_name != '\0') std::cerr << ' ' << optionalify(std::string("-") + i->short_name + " " + i->arg_name); else std::cerr << ' ' << optionalify("--" + i->long_name + " " + i->arg_name); // then print --usage so it doesn't clutter up the more interesting options std::cerr << ' ' + optionalify("--usage"); // and finally print arguments for (argument_iterator i = argument_table.begin(); i != argument_table.end(); ++i) std::cerr << ' ' << requirify(i->name); if (leftover_arguments != 0) std::cerr << ' ' << optionalify(leftover_arguments_name); std::cerr << std::endl; std::exit(1); } // helper for securing right indentation void indent_for_description(int current_indent) { const int description_indentation = 24; int extra_space = description_indentation - current_indent; if (extra_space > 0) std::cerr << std::string(extra_space, ' '); else std::cerr << ' '; // we always need at least one space } void commandoptions::print_help(std::string executable) { // print "Usage: programname [OPTIONS...]" std::cerr << "Usage: " << strip_path(executable) << ' ' << optionalify("OPTIONS..."); // concatenate argument names for (argument_iterator i = argument_table.begin(); i != argument_table.end(); ++i) std::cerr << ' ' << requirify(i->name); if (leftover_arguments != 0) std::cerr << ' ' << optionalify(leftover_arguments_name); std::cerr << std::endl; if (flag_table.size() > 0 || option_table.size() > 0) std::cerr << "\nOptions:\n"; // then list the flags and options for (flag_iterator i = flag_table.begin(); i != flag_table.end(); ++i) { std::cerr << " "; // indentation int x = 2; // we need to keep track of how far we've reached if (i->short_name != '\0') { std::cerr << '-' << i->short_name; x += 2; } if (i->long_name.length() > 0) { if (i->short_name != '\0') { std::cerr << ", "; // add separator x += 2; } std::cerr << "--" << i->long_name; x += 2 + i->long_name.length(); } indent_for_description(x); std::cerr << i->des << std::endl; } for (option_iterator i = option_table.begin(); i != option_table.end(); ++i) { std::cerr << " "; int x = 2; if (i->short_name != '\0') { std::cerr << '-' << i->short_name; x += 2; } if (i->long_name.length() > 0) { if (i->short_name != '\0') { // output separator std::cerr << ", "; x += 2; } std::cerr << "--" << i->long_name; x += 2 + i->long_name.length(); } // output the name of the option argument std::cerr << ' ' << i->arg_name; x += 1 + i->arg_name.length(); indent_for_description(x); std::cerr << i->des << std::endl; } bool first_argument = true; // output descriptions of arguments, if any for (argument_iterator i = argument_table.begin(); i != argument_table.end(); ++i) { if (i->des.length() > 0) { if (first_argument) // prefix the first argument std::cerr << "\nArguments:\n"; first_argument = false; std::cerr << " " << i->name; indent_for_description(2 + i->name.length()); std::cerr << i->des << std::endl; } } if (leftover_arguments != 0) { if (first_argument) std::cerr << "\nArguments:\n"; std::cerr << " " << leftover_arguments_name; indent_for_description(2 + leftover_arguments_name.length()); std::cerr << leftover_arguments_des << std::endl; } std::cerr << "\nHelp options:\n"; // and finally output help about help options std::string s = " -?, --help"; std::cerr << s; indent_for_description(s.length()); std::cerr << "Show this help message\n"; s = " --usage"; std::cerr << s; indent_for_description(s.length()); std::cerr << "Display brief usage message\n"; std::exit(1); } commandoptions/INSTALL0000644000175000017500000000240707335712430013270 0ustar oleoleInstallation of the library ought to be simple. If you're using GNU Make together with GCC, start by running make in the source directory. That will give you a test program. Play around with that (hint: try "./test --help" or something similar) to get the feel of what the library is capable of doing, then read the README to get some theoretical background and finally go back to 'test.cpp' to see some full example code. You may want to copy & paste (otherwise known as kill & yank) some of this code to your own project to get you started. Then you're basically done. Otherwise there's no real build system yet. To link with the library, simply add 'commandoptions.cpp' as a source file to your project build system (make or whatever). I reckon it isn't very elegant but at least it will work with whatever compiler and IDE thingie you're using. To build the test program without make/GCC, simply start a new project with 'test.cpp' and 'commandoptions.cpp' as source files. The library has currently only been tested with GCC v. 2.96 (as shipped by Red Hat) so unfortunately I can't yet guarantee that it is fully standard compliant - it might for instance be necessary to add in some std::'s here and there. Please report such bugs so they can be fixed as soon as possible. commandoptions/test.cpp0000644000175000017500000000276507733106757013744 0ustar oleole// a small program that demonstrates the commandoptions library #include #include "commandoptions.hpp" int main(int argc, const char *argv[]) { commandoptions c; bool beep = false; int no_of_hits = 3; std::string boink_sound = "*boink*"; std::string ouch_sound = "*ouch*"; std::string victim; std::vector audience; c.register_flag(beep, "beep", 'b', "Turn on beeping"); c.register_option(no_of_hits, "hits", 'h', "How many times to hit victim", "NUMBER"); c.register_option(boink_sound, "", 's', "How hitting victim sounds", "SOUND"); c.register_option(ouch_sound, "help-cry", '\0', "Victim response", "SOUND"); c.register_argument(victim, "victim", ""); c.register_leftover_arguments(audience, "spectators...", "Name list for the audience"); try { c.process_command_line(argc, argv); } catch (commandoptions_error &ex) { std::cerr << "Error: " << ex.what() << std::endl; return 1; } if (no_of_hits > 0) { std::cout << "Now hitting " << victim << ' ' << no_of_hits << " times:" << std::endl; for (int i = 0; i < no_of_hits; ++i) std::cout << boink_sound << ' ' << ouch_sound << std::endl; } if (beep) std::cout << "*beep*" << std::endl; if (audience.size() > 0) { std::cout << std::endl << "Act of violence watched by" << std::endl; for (std::vector::iterator i = audience.begin(), end = audience.end(); i != end; ++i) std::cout << " " << *i << std::endl; } } commandoptions/TODO0000644000175000017500000000173210176705072012730 0ustar oleoleThings that should be done some day: * support the GNU*-long-option=argument * rewrite the documentation, also with a better format such as HTML or perhaps even DocBook * switch to a proper Autoconf/Automake build solution * i18n by the means of gettext (most error messages would have to be rewritten with a string composition library) * show how to support user-defined types with an operator >> * when an option in a group isn't recognized, we ought to include the whole group in the error message * show default values of options * support a short description of the program in --help (Dirk Feytons suggestion) Other things that may be worth including: * option groups with headers like 'output options', 'input options' in the*-help * automatic proper line breaking in descriptions in*-help messages * support for requesting output from --help and --usage (in case of certain errors, e.g.) * proper exception hierarchy commandoptions/README0000644000175000017500000003046210176704466013130 0ustar oleoleCommandoptions v. 1.1 --------------------- ...is a class for parsing the options and parameters passed to your program from the command line. It has no requirements besides a relatively standard conforming C++ compiler. A recent GCC will do. The class has been designed with two goals in mind: - to be easy to use - to take over as much as possible of the tedious and boring work of defining and parsing arguments Consequently the class will automatically construct the options and arguments with the least possible given information, format usage and help messages ('--usage' and '-?/--help') which documents the program interface to the user, and report errors together with a meaningful explanation when the supplied arguments are wrong. So when you have registered with the class the variables you want users of your program to be able to influence, the entire command line interface has essentially been taken care of. All code is written by me, Ole Laursen, and released under the terms of the GNU Lesser General Public License. See COPYING for details. Free software is fun software. Tutorial -------- Usage is (deliberately) very simple: 1. Include the 'commandoptions.hpp' header: #include Depending on how you have installed the library, you might need to alter this directive. 2. Define a commandoptions object: commandoptions op; 3. And define the variables which you want the command line options to affect: int foo = 2; bool baz_bar = false; std::string outfile; std::vector infiles; Options that the user doesn't specify are left alone so it might be a good idea to initialize the variables to sensible defaults before processing the command line - here the default for 'foo' is 2. 4. Register the various arguments and options needed: op.register_option(foo, "foo", 'f', "How many times to foo the bar", "NUMBER"); op.register_flag(baz_bar, "bazzing", 'b', "Turn on bazzing of bars"); op.register_argument(outfile, "output-file", "Processed bars end up here"); op.register_leftover_arguments(infile, "input-files...", "Preprocessed bars are read from here"); 5. Process the command line by feeding argc and argv to the object: op.process_command_line(argc, argv); Since various errors might occur, you may want to wrap this in a try block. Upon error, the method will throw a commandoptions_error with the member function what() which returns a meaningful description of the problem: try { op.process_command_line(argc, argv); } catch (commandoptions_error &ex) { std::cerr << "Error: " << ex.what() << std::endl; exit(1); } 6. Use the registered variables std::cout << "reading from " << infiles.size() << "files"; // ... foobar(foo, bars); if (baz_bar) baz(bars); std::ofstream out(outfile); out << bars; Library API ----------- Three different sorts of parameters are currently supported: options, flags and arguments. The first two is based on the notion of a switch which is either a '-' followed by a character or a '--' followed by a string. The following command line contains two switches: foobar --foo 9 -b The basic idea in the library is that you register the variables you want to be configurable by the user - if they are specified by the user, they will then be updated by the commandoptions object, else they are left alone. Thus it is probably a good idea to specify a sensible default value for the variables you use for options; for flags it is required since they reverse the default value so if you don't specify that, you get unpredictable behaviour; for ordinary arguments it doesn't matter since they are mandatory and must be supplied by the user anyway. Options are in this library defined to be an optional switch followed by a (mandatory) argument. The prototype for registration is (the standard namespace is implicitly assumed to be imported to improve readability): template void register_option(T &variable, string long_name, char short_name, string description, string argument_name) The arguments are: the variable to update, a long option name (becomes '--xxx'), a short name (becomes '-y'), and a short description which is displayed in the help message. To get meaningful help messages the name of the mandatory argument for the option is specied as the last parameter. The register call in the tutorial above will produce '[-f NUMBER]' in the usage message. Flags are defined to be optional switches which simply reverses the value of the variable when supplied - flags operate on Boolean variables. Thus if the flag for 'baz_bar' is specified in the command line for a call to the tutorial program above, the value of the variable will be true after calling 'process_command_line'. The prototype for registration is void register_flag(bool &variable, string long_name, char short_name, string description) The arguments for flag registration are the same as for options, though of course with the difference that the last argument, the name of the option argument, is omitted since flags don't take arguments. Arguments are defined to be mandatory ordinary command line arguments (i.e. unlike switches they aren't prefixed with dashes). In the following command line there are two arguments: mv bomb-manufacturing.html doc/howto-dig-potatoes.html The prototype for registering arguments is template void register_argument(T &variable, string name, string description) And the registration arguments are: the variable to set, the name of the argument for use in error and usage reporting, and a description of the argument for the help message. For flags and options, either the long name or short name may be omitted by passing the empty string "" or the null character '\0' respectively to the register method. Option argument names and all descriptions may also be omitted/squashed in a similar way. For support for a variable number of arguments, the library has a concept of "left-over arguments": template void register_leftover_arguments(vector &arg_vector, string name, string description) With this member function it is possible to register a vector for storing any left-over arguments, i.e. arguments that have not been swallowed by any other registered arguments. Note that left-over arguments are always considered optional so if you always need one or more Ts you have to check the size of the vector yourself after processing the command line. If you don't call this function, the library will barf at the user if extra arguments are supplied. Calling register_leftover_arguments a second time will just overwrite the first value so is rather pointless. It doesn't matter which order you call this function in with respect to other register functions, since the vector always will be filled only when there are no other registered arguments left for processing. Supported command line concepts ------------------------------- As mentioned in the introductionary section, the commandoptions class will supply your program with a complete interface to the command line. The argument parsing follows approximately the GNU standard since it is quite sensible and, well..., a standard on the GNU/Linux platform. This means that switches have either the long form '--name-of-switch' which consist of two dashes followed by a name string (that must not begin with a space), or the short one '-c' which consist of a single dash followed by a single character. Options which take an argument consist of the long or the short form of the option followed by whitespace followed by the argument, like '--name-of-option argument' or '-c argument'. Ordinary program arguments are recognized by the fact that they don't begin with a dash, and may appear anywhere between the options as long as the relative order among the arguments is preserved, thus the following command lines are equal program --boink -t blip beep program --boink blip beep -t program blip --boink beep -t provided 'boink' and 't' are flags. All options are processed from left to right so the rightmost supplied option will overwrite any previous specifications. Consequently program --sound boink --sound blip results in the variable behind the option 'sound' getting the value "blip". If the same flag is specified multiple times, all but one is ignored. So if the variable behind the flag 'think' has a default value of 'false', the two commands program --think program --think --think are the same way of giving it the value 'true' (although the second is somewhat more cumbersome). Short flags may be combined by putting multiple characters after the single dash, like this: '-trf'. Any number of flags may be combined this way, but since options take an argument, only one option may be stuffed in such group. If '-o' and '-i' are two options, the following command lines are all equal program -s -q -w -o floink -i boink program -sq -w -o floink -i boink program -sqw -o floink -i boink program -sqwo floink -i boink program -sqwi boink -o floink A few swiches are generated automatically by the library from the information you supply when you register switches. Currently these are '--usage', '--help' and '-?'. When one of these switches are met, a help message is output on std::cerr and the program is terminated with the exit code 1. '--usage' outputs a very terse listing of the available flags, options (short names are used when available) and necessary arguments, like Usage: program [-t?] [-o OUTPUT] [--usage] for a program with the flag '-t', the option '-o' and the argument 'inputfile'. '--help' and '-?' are the same and output a complete listing of the available switches with both long and short names together with the registered descriptions. If an ordinary argument has a description it will also get a special entry for that. So if your arguments are selfdescriptive, like 'inputfile' or 'outputfile', thus making any comment superfluous, feel free to omit the description (by passing an empty string). This avoids the extra entry which saves some space in the output and makes it easier for the user to get an overview of the program interface. Invalid supplied command lines at runtime results in the library trowing an commandoptions_error exception which has the member function what() that returns a const char * with a description of the error. A lot of different things are detected, e.g. wrong number of arguments, unknown options, etc. An important thing is that invalid arguments (either option arguments or ordinary program arguments) cause an exception. If, for instance, an option '--pointless-sounds' which is registered to update an integer variable is passed a string that can't be converted to a number, as in '--pointless-sounds three', the parsing will fail and throw an exception. This releaves the burden of parsing (and reporting errors in connection with) arguments from your shoulders. If you really need the kind of flexibility that homebrewed parsing gives, you can always simply register a string and then do the parsing with that afterwards. Though another, and better, option is to define a new type, e.g. a class, for the argument and then define the input operator >> for the type; internally the library puts the argument string inside an istringstream and then uses '>>' to extract it. This also implies that userdefined types for which operator >> already has been defined will work seamlessly with this library. Contact info ------------ Suggestions, criticism or any other sorts of comments are welcome - my email address is Ole Laursen Some people think the last part of the address is a joke; it isn't, it's the truth. The .dk suffix means I live in Denmark. Which also means I normally speak Danish - that's the cause (and the only one, of course) of all spelling mistakes & other linguistic errors in this documentation. I was wondering if I ought to include my snail mail address here too, but since you don't need that unless you're going to date me, I've left it out (in my experience not many C++ coders are female anyway). You can probably find the library home at http://www.cs.aau.dk/~olau/commandoptions/ in case you want to know whether a new version is out. Or if you want to enjoy my web art. commandoptions/NEWS0000644000175000017500000000103311105652272012725 0ustar oleoleVersion 1.1.1 (November 9, 2008) -------------------------------- Added patch by Wojciech Jaczewski to fix problem with using strings with spaces as left-over arguments. V. 1.1 (September 20, 2003) --------------------------- Introduced left-over arguments for taking a variable number of arguments. Made --help say that the short option for --help is -? and not -h. Assorted portability fixes. Memory-leak fixes (not terribly important considering the typical use of the library, though). Renamed the header to commandoptions.hpp commandoptions/Makefile0000644000175000017500000000033211105651714013667 0ustar oleoleCXXFLAGS=-Wall -g CC=g++ .PHONY: all clean all: test commandoptions: echo commandoptions.o: commandoptions.cpp commandoptions test.o: test.cpp commandoptions test: test.o commandoptions.o clean: -rm -rf *.o *~ commandoptions/commandoptions.hpp0000644000175000017500000001601711105651414015776 0ustar oleole// A -*- C++ -*- class for parsing command line options. // // Copyright (C) 2001, 2002, 2003 Ole Laursen // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Library General Public // License as published by the Free Software Foundation; either // version 2 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // // You should have received a copy of the GNU Library General Public // License along with this library; if not, write to the // Free Software Foundation, Inc., 59 Temple Place - Suite 330, // Boston, MA 02111-1307 USA. #ifndef COMMANDOPTIONS_HEADER #define COMMANDOPTIONS_HEADER #include #include #include #include class commandoptions { public: // register an option with pointer to variable, long and short argument names, a // description of the option and the name of the argument to the option template void register_option(T &par, std::string long_name, char short_name, std::string des, std::string arg_name) { option_table.push_back(option(new updater(par), long_name, short_name, des, arg_name)); } void register_flag(bool &par, std::string long_name, char short_name, std::string des) { flag_table.push_back(flag(par, long_name, short_name, des)); } template void register_argument(T &par, std::string name, std::string des) { argument_table.push_back(argument(new updater(par), name, des)); } // register a vector for swallowing leftover arguments template void register_leftover_arguments(std::vector &par, std::string name, std::string des) { if (leftover_arguments != 0) delete leftover_arguments; leftover_arguments = new vector_updater(par); leftover_arguments_name = name; leftover_arguments_des = des; } void process_command_line(int argc, const char *argv[]); commandoptions() : leftover_arguments(0) {} ~commandoptions() { if (leftover_arguments != 0) delete leftover_arguments; } private: // // member helpers // std::string requirify(std::string s) { return '<' + s + '>'; } std::string optionalify(std::string s) { return '[' + s + ']'; } std::string strip_path(std::string s); void print_usage(std::string executable); void print_help(std::string executable); // // private data structures // // first a few classes for updating a variable from a stream class updaterbase { public: virtual ~updaterbase() {} virtual bool update(std::string s) = 0; virtual updaterbase *clone() = 0; }; template class updater: public updaterbase { public: updater(T &par) : var(par) {} virtual ~updater() {} // update the contained variable through reference - return false upon failure virtual bool update(std::string s) { std::istringstream is(s); is >> var; if (!is) return false; else return true; } virtual updaterbase *clone() { return new updater(var); } private: T &var; }; // a similar class for updating the leftover vector of arguments class vector_updaterbase { public: virtual bool add(std::string s) = 0; virtual ~vector_updaterbase() {} }; template class vector_updater: public vector_updaterbase { public: vector_updater(std::vector &par) : vec(par) {} // update the contained variable through reference - return false upon failure virtual bool add(std::string s) { std::istringstream is(s); T var; is >> var; if (!is) return false; else { vec.push_back(var); return true; } } virtual ~vector_updater() {} private: std::vector &vec; }; // a structure for keeping a command line option struct option { updaterbase *par; std::string long_name, des, arg_name; char short_name; option(updaterbase *p, std::string ln, char sn, std::string d, std::string an) : par(p), long_name(ln), des(d), arg_name(an), short_name(sn) {} ~option() { delete par; } option(const option &o) : par(o.par->clone()), long_name(o.long_name), des(o.des), arg_name(o.arg_name), short_name(o.short_name) {} void operator =(const option &o) { updaterbase *tmp = o.par->clone(); delete par; par = tmp; long_name = o.long_name; des = o.des; arg_name = o.arg_name; short_name = o.short_name; } }; // one for keeping a flag struct flag { bool *par; std::string long_name, des; char short_name; flag(bool &p, std::string ln, char sn, std::string d) : par(&p), long_name(ln), des(d), short_name(sn) {} }; // and one for keeping an argument struct argument { updaterbase *par; std::string name, des; argument(updaterbase *p, std::string n, std::string d) : par(p), name(n), des(d) {} ~argument() { delete par; } argument(const argument &a) : par(a.par->clone()), name(a.name), des(a.des) {} void operator =(const argument &a) { updaterbase *tmp = a.par->clone(); delete par; par = tmp; name = a.name; des = a.des; } }; std::vector