////////////////////////////////////////////////////////////////////// // C++ Base Application Object // Written by Jonathan A. Foster #include #include ////////////////////////////////////////////////////////////////////// // Busy indicator aka. "Live Bug" // // This makes it stupid simple to step through a string that contains // a single character per "frame". You can either call next() to get // the next char in a sequence or use << with an ostream. ////////////////////////////////////////////////////////////////////// struct LiveBug { std::string seq; int p; LiveBug(): seq("|/-\\"), p(0) {} inline char next() { if(p>=seq.size()) p=0; return seq[p++]; } }; inline std::ostream &operator<<(std::ostream &o, LiveBug &bug) { return o << bug.next(); } ////////////////////////////////////////////////////////////////////// // Base Unix Application // // Its apparent to me tha all applications should be objects. Therefor // running an app should consist of setting up an object from a class, // running something on it. And then tearing it back down. With that // in mind the intent of this class is to provide the boiler plate // needed for this model and the bridge from the typical C/C++ main(). // // How to use: // 1. Create a sublcass of this class with your app's functionality. // 2. Override do_switch(), do_switch_arg() and do_arg() to process // arguments passed from the OS. See main() for the built-in // processing. // 3. Override main to provide functionality not triggered by args, // including setup, teardown or even to replace the argument // processing. Don't call the inherited method if you don't need // the argument handling. // 4. override crash() if you want to do something more than dump // the exception string to cerr. // 5. Tell the compiler about your class with the MAIN(class) // macro. // // I'm calling this a "Unix" application because its what developed // the current main() interface concept. This is also emulated / // provided on other platforms, but it started in Unix. ////////////////////////////////////////////////////////////////////// // globally accessible pointer to allow polymorphism struct cBaseApp; extern cBaseApp *baseapp; struct cBaseApp { int command_argc; // main(argc ...) char **command_args; // main(... argv) int ExitCode; // App's code returned to OS. /// STARTUP /// //cBaseApp(); cBaseApp(): command_argc(0), command_args(0), ExitCode(0) { if(!baseapp) baseapp = this; } // Only the first gets registered /// Initialize /// // // This is called, prior to this->main(), with the argc & args from the C // main() entry point. Override this to perform any initialization here that // can't be done before the CLI args are known. // virtual cBaseApp &init(int argc, char **argv); /// argument handlers /// // // Override these routines to implement argument processing. These are char// // instead of string since this is how they come to us from the OS. // // how many args needed for val virtual unsigned do_switch(const char *arg); // proccess a val for switch virtual void do_switch_arg(const char *sw, const std::string &val) { } // process a non-switch arg. virtual void do_arg(const char *arg) { } // TODO: switch lookup methods ("has switch?", "what's switch val?", ...) /// new main() - loop through args /// // // This implementation runs through the arguments passed in using the usual // GNU semantics. It calls do_switch() and do_arg(), depending on the // presence of a leading "-", passing the current argument (without leading // dash(es)). if do_switch() returns > 0 then that many arguments are passed // to do_switch_val() with the corresponding switch that triggered the // calls. Typically do_switch() would only return 0 or 1. // // Switches are defined as arguments starting with one or two dashes. A // single "--" switch will terminate switch processing, sending all // remaining args to do_arg(). Switches with names longer than one char must // be preceded with two dashes ("--" long switches). // // A switch starting with a single dash is a short switch, consisting of a // single character. More than one character can follow a dash in which case // they will be treated as a series of single char switches // (Ex: -abc = -a -b -c). Only the last one is allowed to have an argument // (do_switch()>0). // // This should be overridden to provide any functionality not directly // triggered by switches or args and its functionality can be replaced // completely if you don't need arguments. // virtual int main(); /// Provide help text for CLI arg parse errors // // This is intended to show a command line help message on the terminal // about what the proper CLI syntax is. The return is the desired exit // code. The default is 1. This implementation will provide the app meta // data, if present. This simplified method is used so an exception is // not required to call it. virtual int help(); /// Catch exceptions /// // // This is called by the boiler plate main() (see bottom) when an excpetion // falls out. This is meant to handle std::exception descendents. You can // catch other odd bals in main() above and deal with then accordingly. But // good style dictates all exceptions should descend from std::exception. // The return value is the application's exit code. // virtual int crash(const std::exception &e); /// virtualize destructor /// virtual ~cBaseApp() { } }; ////////////////////////////////////////////////////////////////////// // An exception class to signal CLI errors ////////////////////////////////////////////////////////////////////// struct CLIerror: public std::runtime_error { CLIerror(const std::string &s): runtime_error(s) {} }; ////////////////////////////////////////////////////////////////////// // Macro to bridge the C/C++ main() to the app class' main() // // This sets up a global static variable named "app" that contains // your application object. It then copies the CLI args into it and // calls main. An std::exception handler is setup to pass exceptions // to the "app" object for further handling prior to exiting the app. // The app's exit code is returned from app.main() or app.crash(). ////////////////////////////////////////////////////////////////////// #define MAIN(__APPCLASS__) \ __APPCLASS__ app; \ int main(int argc, char **args) { \ try { \ return app.init(argc, args).main();\ } catch(const std::exception &e) { \ return app.crash(e); \ } \ } #endif