Search code examples
c++exceptionexitargsboost-program-options

How should I exit a C++ program cleanly after printing the CLI help message from a function?


I'm trying to figure out the best way to exit my program cleanly after printing the CLI help information in my program. Currently it looks like this.

int main(int argc, *char[] argv) {
    try {
    std::string settings_file = process_args(argc, argv);

    do_more_stuff();
    ...
    ...
    } catch (...) {
        std::cerr << "Error" << std::endl;
        return (EXIT_FAILURE)
    }
}

std::string process_args(int argc, char *argv[]) {
    boost::program_options::options_description desc("Program options");
    desc.add_options()("help,h", "Print usage message")(
      "settings,s", boost::program_options::value<std::string>(),
      "Specify settings file");

    boost::program_options::variables_map vm;

    store(parse_command_line(argc, argv, desc), vm);

    if (vm.count("help")) {
        std::cout << desc << std::endl;
        ---->!!!!!!!! WHAT DO I DO HERE TO GET OUT !!!!!!!!<----
    }
    if (vm.count("settings")) {
        std::cout << "Settings file " << vm["settings"].as<std::string() << std::endl;
        return vm["settings"].as<std::string>();
    } else {
        throw std::runtime_error("Settings file must be specified.");
    }
}

So my question is after I print the CLI 'help' message what do I do to exit my program?

  1. Should my function not be returning std::string and just return an error code?

  2. Should I throw an exception and catch it in main and then exit?

  3. Is there a much nicer / better way to do it?

Thanks in advance.


Solution

  • Another better way in my opinion, according to this post : C++ catching all exceptions

    #include <cstdlib>
    #include <iostream>
    #include <stdexcept>
    
    static bool checkHelp(char * const argv[])
    {
        bool usage = (!strcmp(argv[1], "-h") or !strcmp(argv[1], "--help"));
    
        if (usage) {
            std::cout << "Print your usage" << std::endl;
        }
        return usage;
    }
    
    int main(int argc, char *argv[])
    {
        /* Set eval to default value */
        int eval = EXIT_SUCCESS;
    
        try {
            if (!checkHelp(argv)) {
                /*
                ** do some stuff here
                */
            }
        } catch (std::exception const &err) {
             /* update eval on throwed exceptions */
             eval = EXIT_FAILURE;
             std::cerr << "error: " << err.what() << std::endl;
        }
        return eval;
    }
    

    Doing by this way, you can manage all your program execution in error context and choose what to do : update return value, do other stuff, etc. You don't break your main thread and prevent you from memory errors/leaks without exit by exit() function.

    1) eval (for 'exit value') is instancianted and destroyed with main context. In accordance with the type of main's return value, we have to change it's value if an error occurred before returning it while exit the program.

    2) In my example, I send argv to the checkHelp function who returned true if the first argument of my software (argv[1] here) is equal to "-h" or "--help", if not checkHelp returns false. You can also do a loop on all argv arguments received by main.

    3) Finally, catch (std::exception const &err) allows you to get your throwed exception by reference and print his message. So, you are using your main try/catch block in all his integrity.