Search code examples
c++boostboost-program-options

Custom validator for boost program_options doesn't work with GCC, works with MSVC


Let's say I would like special processing for integer options. According to the documentation I have to write my own validate function. Consider the following short program.

#include <iostream>
#include <vector>
#include <string>

#include <boost/program_options.hpp>

namespace po = boost::program_options;

namespace boost { namespace program_options {
template <class charT>
void validate(boost::any& v, const std::vector<std::basic_string<charT> >& xs, unsigned int*, int)
{
    std::cout << "validate is redefined" << std::endl;
    // do something else
}
}}

int main(int argc, char* argv[])
{
    po::options_description cmdLineOptions;
    po::variables_map vm;

    unsigned int v;
    const char* args[] = {"tst", "-k", "10"};

    cmdLineOptions.add_options()
        ("key,k", po::value<unsigned int>(&v)->required())
      ;

    po::store(po::command_line_parser(sizeof(args) / sizeof(args[0]), args).options(cmdLineOptions).run(), vm);
    po::notify(vm);

    std::cout << v << '\n';

    return 0;
}

It perfectly works in VS 2013 and outputs

validate is redefined
10

In GCC it never steps inside the validate function.

Proof: http://coliru.stacked-crooked.com/a/fd558ebf987a4bbe

If I try to use a custom type instead of unsigned int, GCC would try to use a validate from program_option anyway and will end up with bunch of errors.

What I am doing wrong?


Solution

  • On a preliminary hunch, consider using

    BOOST_STRONG_TYPEDEF(unsigned int, Unsigned);
    

    It seems like a bad idea to customize behaviour just on built-in types.


    Solution: It has to do with partial ordering.

    If you move your overload outside the boost::program_options namespace it will start working (as it no longer competes with the base template).

    Live On Coliru

    #include <iostream>
    #include <vector>
    #include <string>
    
    #include <boost/any.hpp>
    #include <boost/serialization/strong_typedef.hpp>
    #include <boost/program_options.hpp>
    
    BOOST_STRONG_TYPEDEF(unsigned, Unsigned)
    
    template<class charT>
        void validate(boost::any& v, 
                const std::vector< std::basic_string<charT> >& xs, 
                Unsigned* p, int)
        {
            std::cout << "validate is redefined" << std::endl;
            // do something else
        }
    
    namespace po = boost::program_options;
    
    int main()
    {
        po::options_description cmdLineOptions;
        po::variables_map vm;
    
        Unsigned v;
        const char* args[] = {"tst", "-k", "10"};
    
        cmdLineOptions.add_options()
            ("key,k", po::value<Unsigned>(&v)->required())
          ;
    
        po::store(po::command_line_parser(sizeof(args) / sizeof(args[0]), args).options(cmdLineOptions).run(), vm);
        po::notify(vm);
    
        std::cout << v << '\n';
    
        return 0;
    }
    

    The cause is likely MSVC's famously broken 2-phase lookup