2

The original problem I tried to solve when stumbled upon this was to select parse_impl version:

  • if the parser (of type U) provides a field named "skp", use that field;
  • if not, use a default value.

I came up with the following code:

// This variant compiles for parsers requiring a skipper:
template <typename I, typename U, typename A,
          typename = typename std::enable_if<
              not std::is_same<
                  typename std::remove_reference<U>::type::skipper_type,
                  qi::unused_type
              >::value
          >::type,
          typename = void > // avoid redefinition (1 more overload not shown)
bool parse_impl(I & start, I end, U && parser, A & attr)
{
    // qi::space by default:
    return qi::phrase_parse(start, end, parser, qi::space, attr);
}

// This variant compiles for parsers providing skipper via 'skp' member:
template <typename I, typename U, typename A,
          typename = typename std::enable_if<
              not std::is_same<
                  typename std::remove_reference<U>::type::skipper_type,
                  qi::unused_type
              >::value
              && (sizeof(U::skp) != 0)
          >::type,
          typename = void, typename = void > // avoid redefinition
bool parse_impl(I & start, I end, U && parser, A & attr)
{
    // parser.skp is available:
    return qi::phrase_parse(start, end, parser, parser.skp, attr);
}

The call site look like this:

pr.is_ok = parse_impl(pr.position, input.cend(), parser, pr.attr);

and this is called both for types having skp and ones that haven't.

And it compiles (on gcc4.7), but I don't understand why: when skp is present, expressions in both enable_ifs should evaluate to true (skipper_type is obviously not equal to unused_type then), and the call should be ambiguous. Where am I mistaken?

ildjarn
  • 62,044
  • 9
  • 127
  • 211
vines
  • 5,160
  • 1
  • 27
  • 49
  • [A reduced test case](http://liveworkspace.org/code/5a6093ff1c940b48071231ec1d1d12bf) atleast is ambiguous and I can't see if I did anything different there. I can only guess that your third overload, the one with `skp`, never actually gets called for some reason, if this does indeed compile for you. Maybe put some test print statements in them? Lastly, I recommend reading [this blog post](http://rmartinho.github.com/2012/06/01/almost-static-if.html) aswell as [this](http://stackoverflow.com/q/12654067/500104) question and [this](http://stackoverflow.com/a/9154394/500104) answer. :) – Xeo Oct 16 '12 at 22:48
  • @Xeo: I've finally nailed it. There is difference, actually: `sizeof(NoRef::skp)` in your code vs `(sizeof(U::skp) != 0)` in mine. `NoRef` has finally brought the ambiguity in! *HUGE* thanks for help and the links! – vines Oct 17 '12 at 00:58
  • Ah, so your problem was that when you pass an lvalue (and `U` is deduced as a reference), you get your SFINAE going. :) And indeed, [that's the problem](http://liveworkspace.org/code/f35132f82751e262fd6a64515e11bebd). – Xeo Oct 17 '12 at 03:35
  • Since there hasn't been any action from your side, should I just post an answer? – Xeo Oct 24 '12 at 13:08

1 Answers1

1

The problem here is, as concluded in the comments, that when using just U::skp, it might be that U is deduced as a reference type (i.e., when passing an lvalue parser). When this happens, you get SFINAE going, as a reference type obviously has no nested anything.

The fix is to remove the reference from U with std::remove_reference so that you have (sizeof(std::remove_reference<T>::type::skp) != 0). Note that typename is not needed here, since ::skp indicates that type has to be a typename.

Xeo
  • 129,499
  • 52
  • 291
  • 397