Search code examples
c++c++11boostboost-program-options

Custom validator doesn't allow default_value


I am trying to use a custom validator instead of overloading operator>> to support an enum type in my option parsing. I have done the following:

#include <iostream>

#include <boost/program_options.hpp>

enum class MyEnum
{
    OneThing,
    AnotherThing
};

void validate(boost::any& v, const std::vector<std::string>& values,
              MyEnum*, int)
{
    // parse the enum from values[0]
    std::cout << "Test" << std::endl;
    MyEnum enumValue = MyEnum::OneThing;
    v = enumValue;
}

int main(int argc, char*argv[])
{
    namespace po = boost::program_options;

    po::options_description desc("Options");

    //desc.add_options()("myEnum", po::value<MyEnum>(), "MyEnum value"); // works fine
    desc.add_options()("myEnum", po::value<MyEnum>()->default_value(MyEnum::OneThing), "MyEnum value"); // compiler error: Source type is not streamable

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    MyEnum myEnum = vm["myEnum"].as<MyEnum>();

    return 0;
}

This works fine as long as I don't try to set a default_value, but when I do specify a default_value, I get error: static assertion failed: Source type is neither std::ostream able nor std::wostream able. What else do I need (without overloading stream operators, which is the whole point of using a validator as far as I understand) to do to allow a custom type to get a default_value?


Solution

  • You can set a default value, however, it needs to know how to represent the default value in the descriptions.

    Since the enum isn't streamable, the automatic way doesn't work, so you must specify it, e.g.:

    default_value(MyEnum::OneThing, "OneThing")
    

    Live On Coliru

    #include <iostream>
    
    #include <boost/program_options.hpp>
    
    enum class MyEnum
    {
        OneThing,
        AnotherThing
    };
    
    void validate(boost::any& v, const std::vector<std::string>& values,
                  MyEnum*, int)
    {
        // parse the enum from values[0]
        std::cout << "Test" << std::endl;
        MyEnum enumValue = MyEnum::OneThing;
        v = enumValue;
    }
    
    int main(int argc, char*argv[])
    {
        namespace po = boost::program_options;
    
        po::options_description desc("Options");
    
        //desc.add_options()("myEnum", po::value<MyEnum>(), "MyEnum value"); // works fine
        desc.add_options()("myEnum", po::value<MyEnum>()->default_value(MyEnum::OneThing, "OneThing"), "MyEnum value");
    
        po::variables_map vm;
        po::store(po::parse_command_line(argc, argv, desc), vm);
        po::notify(vm);
    
        MyEnum myEnum = vm["myEnum"].as<MyEnum>();
        std::cout << std::boolalpha << (myEnum == MyEnum::OneThing) << "\n";
    }
    

    Prints

    true