Search code examples
c++boostboost-program-options

boost::program_options - display the value entered by the user in error msg when validation fails


I am using boost program_options and I can't found a way to specify the exception message to include the value entered by the user, like:

error: the argument for option '--ipc' is invalid: "shm"

I pass the value entered by the user to the call below, but this has no effect:

throw po::validation_error(po::validation_error::invalid_option_value, enteredValue);

The error message remains this one:

error: the argument for option '--ipc' is invalid

Here my complete code:

#include <iostream>
#include <vector>
#include <boost/program_options.hpp>
#include <boost/algorithm/string.hpp>

using namespace std;

namespace po_style = boost::program_options::command_line_style;
namespace po = boost::program_options;

enum class Ipc : int8_t
{
    TCP = 0,
    UDP,
};

// Convert a log severity level to its name formatted as a string
// Enum cannot contain methods but you can add external methods like this one
static const char* to_string(const Ipc& id)
{
    switch (id)
    {
    case Ipc::TCP:  return "TCP";
    case Ipc::UDP:  return "UDP";
    default:        return "TCP";
    }
}

// This function is called for arguments of the type Ipc
void validate(boost::any& v, const vector<string>& values, Ipc*, int)
{
    po::validators::check_first_occurrence(v);
    const string& enteredValue = po::validators::get_single_string(values);
    const string& enteredValueUpper = boost::to_upper_copy(enteredValue);

    if (enteredValueUpper == "TCP")
    {
        v = boost::any(Ipc::TCP);
    }
    else if (enteredValueUpper == "UPD")
    {
        v = boost::any(Ipc::UDP);
    }
    else
    {
        throw po::validation_error(po::validation_error::invalid_option_value, enteredValue);
    }
}

int main(int argc, char* argv[])
{
    try
    {
        // Declare all allowed options using the options_description class. 
        po::options_description desc("Usage");
        desc.add_options()
            ("help,h", "produce help message")
            ("ipc,i", po::value<Ipc>()->default_value(Ipc::TCP, "TCP"), "set the inter-process communication");

        po::variables_map cmdline;
        po::store(po::command_line_parser(argc, argv)
            .options(desc)
            .style(po_style::unix_style | po_style::case_insensitive)
            .run(), cmdline);
        po::notify(cmdline);

        // -i[--ipc] set the inter-process communication
        if (cmdline.count("ipc"))
        {
            Ipc level = cmdline["ipc"].as<Ipc>();
            cout << "ipc=" << to_string(level) << endl;
        }

        // -h[--help] produce help message
        if (cmdline.count("help"))
        {
            cout << desc << "\n";
            return 0;
        }
    }
    catch (exception& e)
    {
        cerr << "error: " << e.what() << "\n";
        return 1;
    }
    catch (...)
    {
        cerr << "Exception of unknown type!\n";
    }

    return 0;
}

Solution

  • I just found one solution to display the value entered by the user when the validation fails:

    ...
    else
    {
        // ***************************************************
        // SOLUTION FOUND BY Dan Mašek
        // ***************************************************
        throw po::invalid_option_value(enteredValue);
    
    
    
        // This line does not display the enteredValue
        //throw po::validation_error(po::validation_error::invalid_option_value, enteredValue);
    
        // This works but hardcode the option name --ipc
        //std::ostringstream ss;
        //ss << "the argument for option '--ipc' is invalid: " << enteredValue;
        //throw std::invalid_argument(ss.str());
    }
    

    It gives an output like:

    error: the argument for option '--ipc' is invalid: shm
    

    Can we do better (ie by not duplicating the error message) ?