2

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?

facetus
  • 1,091
  • 6
  • 20

1 Answers1

1

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

BenC
  • 8,729
  • 3
  • 49
  • 68
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks for a workaround. Yes, it seems like in GCC partial ordering is broken. – facetus Nov 21 '14 at 23:58
  • @noxmetus I'm not convinced yet. Clang does the same. It's probably something that could be documented slightly better... – sehe Nov 22 '14 at 00:02
  • The workaround is not really useful, since usually one would want to call check_first_occurrence and get_single_string inside a validator and they are defined inside program_options header files. – facetus Nov 22 '14 at 00:02
  • 1
    It wasn't intended as a workaround. I'm trouble shooting and sharing my ideas :) – sehe Nov 22 '14 at 00:04
  • "It seems like a bad idea to customize behaviour just on built-in types" First of all, there is nothing wrong in it. Second, a quote from my post: "if you 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". – facetus Nov 22 '14 at 00:05
  • I wasn't saying that was causing anything. (And consider ODR violations if another TU defines the validate function differently for the same built-in type. Or just need to validate to unsigned integers differently.) I read your question. It's essentially random advice. It's SO :/ – sehe Nov 22 '14 at 00:08
  • I don't know what is ODR, TU and SO. – facetus Nov 22 '14 at 00:12
  • ODR= One-defintion rule. TU=Translation Unit (~= .cpp after preprocessing) and SO is [SO]. In the mean time, I've found the solution. Updating answer. The difference with MSVC is broken 2-phase lookup. – sehe Nov 22 '14 at 00:18
  • How ODR would be different if it were a custom type? Also it seems weird to have two versions of program_options inside one application. – facetus Nov 22 '14 at 00:19
  • @noxmetus because you'd create different strong types in different TUs. It's not that strange to have different program options: consider a multi-tool CLI application (like openssl, zfs, busybox, image-mack, git, bazaar...). Also, don't forget that `program_options` does more than _just_ parse command lines. Anyways, don't sweat it. It's just something I think of from experience. In this case it seems like a UDT might be required so we can have ADL (builtin primitive types do not have an associated namespace for argument dependent lookup) – sehe Nov 22 '14 at 00:22
  • Did you notice I found the solution? We got a bit side tracked. Sleep well – sehe Nov 22 '14 at 00:30