2

Hello I am new to Boost Spirit, and I am having trouble with the qi::symbol object.

#include <iostream>
#include <vector>

#define BOOST_SPIRIT_DEBUG
#define BOOST_SPIRIT_DEBUG_OUT std::cerr

#include <boost/config/warning_disable.hpp>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp> 
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_symbols.hpp>
#include <boost/spirit/include/qi_nonterminal.hpp>
struct Thing
{
    Thing() = default; 

    Thing(std::string s, std::string t)
    :
        one(s),
        two(t)
    {
    }


    std::string one;
    std::string two;
};

BOOST_FUSION_ADAPT_STRUCT(
    Thing,
    (std::string, one)
    (std::string, two)
)

namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
namespace ascii = boost::spirit::ascii;

template <typename Iterator>
struct parse_things : qi::grammar<Iterator, Thing(), ascii::space_type>
{
    parse_things() : parse_things::base_type(keyword)
    {
        qi::symbols<char, Thing> keywords;
        keywords.add
            ("One", Thing("ThingOne","ThingTwo"))
            ("Two", Thing("ThingTwo","ThingOne"));

        keyword %= keywords;
        BOOST_SPIRIT_DEBUG_NODE(keyword);
    }

    qi::rule<Iterator, Thing(), ascii::space_type> keyword;
};

int main(int argc, const char *argv[])
{
    Thing t;
    std::string s("One");
    std::string s2("Two");
    parse_things<std::string::const_iterator> parser;

    bool r = qi::phrase_parse(
            std::cbegin(s),
            std::cend(s),
            parser,
            ascii::space,
            t);

    assert(r == true);
    assert(t.one == "ThingOne");
    assert(t.two == "ThingTwo");
    assert(t.two == "ThingOne");
    assert(t.one == "ThingTwo");

    return 0;
}

This compiles for me on Visual Studio 2014 Update 4, but throws an Access Violation while parsing. What am I doing wrong?

Jeremy Wright
  • 200
  • 1
  • 6

1 Answers1

2

You should make

qi::symbols<char, Thing> keywords;

a member of the struct, instead of a temporary local in the constructor. Your parser will have stale references to the trie data structure in the symbols object.

A slightly simplified program with more tests:

Live On Coliru

//#define BOOST_SPIRIT_DEBUG
#define BOOST_SPIRIT_DEBUG_OUT std::cerr

#include <boost/fusion/adapted/struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_symbols.hpp>
#include <iostream>
#include <vector>

struct Thing
{
    std::string one;
    std::string two;
};

BOOST_FUSION_ADAPT_STRUCT(
    Thing,
    (std::string, one)
    (std::string, two)
)

namespace qi    = boost::spirit::qi;
namespace phx   = boost::phoenix;
namespace ascii = boost::spirit::ascii;

template <typename Iterator>
struct parse_things : qi::grammar<Iterator, Thing(), ascii::space_type>
{
    parse_things() : parse_things::base_type(keyword)
    {
        keywords.add
            ("One", Thing{"ThingOne","ThingTwo"})
            ("Two", Thing{"ThingTwo","ThingOne"});

        keyword %= keywords;
        BOOST_SPIRIT_DEBUG_NODE(keyword);
    }

    qi::symbols<char, Thing> keywords;
    qi::rule<Iterator, Thing(), ascii::space_type> keyword;
};

int main()
{
    const parse_things<std::string::const_iterator> parser;

    for (std::string const s : { "One", "Two", "Three" })
    {
        auto f = begin(s), l = end(s);
        Thing data;

        bool ok = qi::phrase_parse(f,l,parser,ascii::space,data);

        std::cout << s << ": " << std::boolalpha << ok << ", " << boost::fusion::as_vector(data) << '\n';
        if (f != l)
            std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
    }
}

Prints

One: true, (ThingOne ThingTwo)
Two: true, (ThingTwo ThingOne)
Three: false, ( )
Remaining unparsed: 'Three'
sehe
  • 374,641
  • 47
  • 450
  • 633
  • It seems obvious now! Thank you very much! – Jeremy Wright Feb 21 '15 at 17:53
  • Thank you, you helpend me with a similar problem. Can you please explain why symbols<> parsers have to be declared at member scope while other multichar parsers (such as "qi::uint_type uint_;" in the grammar of [this tutorial](http://www.boost.org/doc/libs/1_61_0/libs/spirit/example/qi/compiler_tutorial/calc5.cpp)) can be declared temporary local in the parser constructor? – die_hoernse Jul 25 '16 at 15:59
  • @die_hoernse Symbols is stateful and is used "by-ref". I'm not _actually convinced_ the usage in that tutorial link is completely safe. I think it might technically UB unless somehow the stateless parsers aren't taken by-ref . If you would not make the symbols a member you'll get this: http://stackoverflow.com/questions/30744000/parsing-into-stdvectorstring-with-spirit-qi-getting-segfaults-or-assert-fai/30744484#30744484 – sehe Jul 25 '16 at 16:02
  • @sehe I implore you to tell me you are kidding regarding UB in the most basic official boost spirit tutorials. But thanks for pointing that out. Feels like a mine field though. – die_hoernse Jul 26 '16 at 06:55