2

Consider this code using Boost Spirit X3 (conceptually same goes for Boost Spirit Qi):

  string command;
  string value;
  x3::parse(command.begin(), command.end(), "float:" >> x3::double_, value);

Why is this code not generating any error during compilation? Shouldn't "float:" >> x3::double_ parser have attribute of type double and therefore not accept std::string as 4th argument to parse?

BTW, I know I could do this:

string value;
auto assign = [](auto& ctx){value = _attr(ctx)};
x3::parse(command.begin(), command.end(), "float:" >> x3::double_[assign], value)

which would generate an error, but it is more complicated than necessary.

As a last resort: any there any well-known replacements for sscanf that would be type safe (possibly in boost)?

Tomasz Grobelny
  • 2,666
  • 3
  • 33
  • 43

2 Answers2

2

It's because automatic attribute propagation into a container is very flexible.

And std::string is a container.

Perhaps if we do the following experiment it makes a bit more sense:

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <iostream>
namespace x3 = boost::spirit::x3;

int main() {
    //std::string command = "f: hello world";
    std::string command = "f: 104 101 108 108 111 32 119 111 114 108 100";
    std::string value;
    x3::phrase_parse(
            command.begin(),
            command.end(),
            "f:" >> *x3::double_, x3::blank,
            value);

    std::cout << value << "\n";
}

As you might surmise, it prints:

hello world

For strings this seems surprising. It would be much less of a surprise if the target was std::vector<float> - but it would require precisely the same amount of conversions.

It's fair for each element to enjoy propagation conversions. What if you were parsing Ast nodes like:

struct Operation {
     Operator op;
     std::vector<Expression> operands;
}

You would hate if operands didn't implicitly convert into Expression from Unary, FunctionCall, StringLiteral etc.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • In the off-chance you were **really** looking for a way to synthesize the source text _as matched_, use `raw[]`: http://coliru.stacked-crooked.com/a/34635951fbabe59d – sehe Jul 19 '20 at 22:43
  • "It's because automatic attribute propagation into a container is very flexible." - is it possible to limit this flexibility using some boost spirit settings? I am asking because in my use case I would like to just have type-safe `sscanf` replacement. And I would expect that this simple mistake (value of type string, not double) would be detected by the compiler. – Tomasz Grobelny Jul 20 '20 at 08:06
  • That's not what spirit is. It's not geared to be a primitives type token scanner. If you look at the name `scanf` you can glean as much. Maybe you'd like a scanner (Spirit Lex) or a much simpler pattern expression library like Boost Xpression. But I think what you're describing doesn't yet exist (and would be the dual complement of libfmt). Perhaps there merit in starting an open source initiative;) – sehe Jul 20 '20 at 09:25
  • Boost Xpression doesn't have the notion of `double`/`float`, Lex seems too complicated, and libfmt uses format string which is not any better than `sscanf`. I'll probably stick to good old `sscanf`. Anyway, seems compilers have good support for checking mismatched format strings. – Tomasz Grobelny Jul 20 '20 at 10:34
  • @sehe, it is **very** "surprising" that the parsed value is "hello world". I don't see how any user, not **very** intimate with the initricacies of x3 attribute propagation, would not also be **very** surprised. IMHO, this makes x3 attribute propagation a liability rather than an asset, and I agree that there should be "an open source initiative" to remedy this. – user1681377 Jul 20 '20 at 12:24
  • @user1681377 I know right. I believe that's exactly what I've been saying. I also explained exactly why it still makes sense. If you don't like it, don't look at me. I didn't write any of it - I just use it. If you run a lot of PoC parsers to ASTs (not primitive types) you ***will*** come to appreciate this. It's a static type mapping so a test coverage suffices to know that it does what you want/expct. – sehe Jul 20 '20 at 12:41
  • By the way, your claim about libfmt (_"and libfmt uses format string which is not any better than sscanf"_) is [wrong](https://fmt.dev/latest/index.html#safety). People don't give generics the credit it deserves. – sehe Jul 20 '20 at 12:44
  • 1
    @sehe, I now understand your answer after reading and responding to Joel's answer [here](https://github.com/boostorg/spirit/issues/618#issuecomment-662407786). In essense, each time x3::double_ is parsed, the resulting double is converted to a char and that char is push_backed onto the `std::string value`. – user1681377 Jul 22 '20 at 12:45
  • I thought I had made that "clear" by literally leaving the comment _`//std::string command = "f: hello world";`_ right above the line that shows those letters in ASCII ordinals :) Sorry for assuming that was clear – sehe Jul 22 '20 at 20:13
  • @sehe , your help in reaching a consensus in the [spirit issue #618](https://github.com/boostorg/spirit/issues/618) would be welcome. – user1681377 Jul 23 '20 at 00:06
0

@tomasz-grobelny, using the explicit_convert_parser_type from here, and with #defined REPRODUCE_SO_62986317, correctly compile-time errors when passing anything but a double as the attribute.

HTH,

Larry

user1681377
  • 93
  • 1
  • 8
  • OOPS, when the grammar argument changed to `"float:">>explicit_convert_parser_type{}, x3::blank`, the last test no longer compiles. Maybe it has something to do with the comment: "sequence collapsing activates for sequence length of one" made [here](https://github.com/boostorg/spirit/issues/618#issuecomment-662419631); however, I'm not sure because I'm not real familiar with the "intricacies" of spirit x3 attribute collapsing :( . – user1681377 Jul 25 '20 at 13:20
  • The problem has been corrected [here](http://coliru.stacked-crooked.com/a/ab1802d65b65b8b7). @sehe, your requirements are shown satisfied by running test and tomasz-grobelny, your requirements (for diagnosing type error) are shown by running test when defined(REPRODUCE_SO_62986317). Or am I missing something? – user1681377 Jul 27 '20 at 09:39