I created a grammar using boost spirit x3. While testing my resulting parser, i recognized that there is a case where the parser throws the following exeption:
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
I didnt know this could happen using boost spirit x3, I thought the parse method eighter returns false or throws a boost::spirit::x3::expectation_failure
, what am I doing wrong in my grammar, because this should not happen here.
I uploaded my mcve HERE, if you execute the binary the std::logic error will bw thrown.
example.cpp
#include "example_def.hpp" #include "config.hpp" namespace client { namespace parser { BOOST_SPIRIT_INSTANTIATE(var_dec_type, iterator_type, context_type) }}
main.cpp
#include <iostream> //#define SINGLE_TU #if defined(SINGLE_TU) #pragma message "yesdef(SINGLE_TU)" #else #pragma message "notdef(SINGLE_TU)" #endif #ifdef SINGLE_TU #include "types_def.hpp" #include "example_def.hpp" #else #include "example.hpp" #endif template<typename Parser, typename Attribute> bool test(const std::string& str, Parser&& p, Attribute&& attr) { using iterator_type = std::string::const_iterator; iterator_type in = str.begin(); iterator_type end = str.end(); bool ret = boost::spirit::x3::phrase_parse(in, end, p, boost::spirit::x3::ascii::space, attr); ret &= (in == end); return ret; } int main() { client::ast::VariableDec attr; std::cout << test("var foo : foo<bar, baz<bahama>>", client::var_dec() , attr); return 0; }
types.cpp
#include "config.hpp" #include "types_def.hpp" #include <iostream> namespace client {namespace parser { BOOST_SPIRIT_INSTANTIATE(type_type, iterator_type, context_type); BOOST_SPIRIT_INSTANTIATE(class_type_type, iterator_type, context_type); #define PARSE_RULE_BODY #if defined(PARSE_RULE_BODY) #pragma message "yesdef(PARSE_RULE_BODY)" //This causes the parse_rule generated by //BOOST_SPIRIT_DEFINE for type //to *not* be used. #include <boost/fusion/iterator/deref.hpp> #else #pragma message "notdef(PARSE_RULE_BODY)" //This causes the parse_rule generated by //BOOST_SPIRIT_DEFINE for type //to be used. #endif #define SEHE_TYPES_DEF_HPP_PASTE #define EXPLICT_SPECIALIZATION_FROM_LINKER_ERROR_MSG #if defined(SEHE_TYPES_DEF_HPP_PASTE) #pragma message "yesdef(SEHE_TYPES_DEF_HPP_PASTE)" //Source: // The following code was copy&pasted&reformatted from lines 38-53 of: // https://github.com/sehe/linker_error_example/blob/explicit_instantiation/types_def.hpp // on about 2016-11-15. // with minor modifications: // * reformatting // * Added body with: // * printing first..last. //======================= namespace { template <typename Seq, size_t N> using field_at = boost::fusion::basic_iterator < boost::fusion::struct_iterator_tag , boost::fusion::random_access_traversal_tag , Seq , N >; template <typename Seq, size_t N, size_t M> using fields = boost::fusion::iterator_range<field_at<Seq, N>, field_at<Seq, M> >; using Attributes = fields<client::ast::VariableDec, 1, 2>; using Context = x3::context < x3::skipper_tag , x3::char_class<boost::spirit::char_encoding::ascii, x3::space_tag> const , x3::unused_type >; } template #ifdef PARSE_RULE_BODY <> #endif bool parse_rule < std::string::const_iterator , Context , Attributes >( decltype(type) rule, std::string::const_iterator &first, std::string::const_iterator const &last, Context const &context, Attributes &attr) #ifndef PARSE_RULE_BODY ; #else { std::cout<<"***in function="<<__func__<<"\n"; std::string::const_iterator beg=first; std::cout<<":input=\n{"; for(; beg!=last; ++beg) std::cout<<*beg; std::cout<<"}\n"; using required_t=type_type::attribute_type; required_t required_v=boost::fusion::deref(attr.first); //attr type required by parse_rule generated by: // BOOST_SPIRIT_INSTANTIATE(type_type, iterator_type, context_type) ; bool result=parse_rule(rule,first,last,context,required_v) //This should call the parse_rule generated by: // BOOST_SPIRIT_INSTANTIATE(type_type, iterator_type, context_type) ; std::cout<<":result="<<result<<"\n"; return result; } #endif #elif defined(EXPLICT_SPECIALIZATION_FROM_LINKER_ERROR_MSG) #pragma message "yesdef(EXPLICT_SPECIALIZATION_FROM_LINKER_ERROR_MSG)" template #ifdef PARSE_RULE_BODY <> #endif //The following simply copied&pasted from an earlier linker error //message, and then reformatted to clarify what //was being specialized. bool parse_rule < __gnu_cxx::__normal_iterator < char const* , std::__cxx11::basic_string < char , std::char_traits<char> , std::allocator<char> > > , boost::spirit::x3::context < boost::spirit::x3::skipper_tag , boost::spirit::x3::char_class < boost::spirit::char_encoding::ascii , boost::spirit::x3::space_tag > const , boost::spirit::x3::unused_type > , boost::fusion::iterator_range < boost::fusion::basic_iterator < boost::fusion::struct_iterator_tag , boost::fusion::random_access_traversal_tag , client::ast::VariableDec , 1 > , boost::fusion::basic_iterator < boost::fusion::struct_iterator_tag , boost::fusion::random_access_traversal_tag , client::ast::VariableDec , 2 > > > ( boost::spirit::x3::rule//==decltype(type) where types from types_def.hpp:16 < client::parser::type_class//types.hpp:15 , boost::spirit::x3::variant < client::ast::nil , boost::spirit::x3::forward_ast < client::ast::LambdaType > , boost::spirit::x3::forward_ast < client::ast::ClassType > > , false > , __gnu_cxx::__normal_iterator//==std::string::const_iterator < char const* , std::__cxx11::basic_string < char , std::char_traits<char> , std::allocator<char> > >& first , __gnu_cxx::__normal_iterator//==std::string::const_iterator < char const* , std::__cxx11::basic_string < char , std::char_traits<char> , std::allocator<char> > > const& last , boost::spirit::x3::context//=?Context from #if defined(SEHE_TYPES_DEF_HPP_PASTE) < boost::spirit::x3::skipper_tag , boost::spirit::x3::char_class < boost::spirit::char_encoding::ascii , boost::spirit::x3::space_tag > const , boost::spirit::x3::unused_type > const& context , boost::fusion::iterator_range//=?Attributes from #if defined(SEHE_TYPES_DEF_HPP_PASTE) < boost::fusion::basic_iterator < boost::fusion::struct_iterator_tag , boost::fusion::random_access_traversal_tag , client::ast::VariableDec , 1 > , boost::fusion::basic_iterator < boost::fusion::struct_iterator_tag , boost::fusion::random_access_traversal_tag , client::ast::VariableDec , 2 > >& attr ) #ifndef PARSE_RULE_BODY ; #else { std::cout<<"***in function="<<__func__<<"\n"; std::string::const_iterator beg=first; std::cout<<":input=\n{"; for(; beg!=last; ++beg) std::cout<<*beg; std::cout<<"}\n"; return false; } #endif//PARSE_RULE_BODY #endif//(SEHE_TYPES_DEF_HPP_PASTE) }}
ast.hpp
// // Created by lukas on 11.11.16. // #ifndef LINKER_ERROR_EXAMPLE_AST_HPP_HPP #define LINKER_ERROR_EXAMPLE_AST_HPP_HPP #include <boost/fusion/adapted/struct.hpp> #include <boost/spirit/home/x3.hpp> #include <boost/spirit/home/x3/support/ast/variant.hpp> #include <vector> #include <string> namespace client { namespace ast { namespace x3 = boost::spirit::x3; struct LambdaType; struct ClassType; struct nil{}; typedef x3::variant < nil, x3::forward_ast<LambdaType>, x3::forward_ast<ClassType> > Type; struct LambdaType { std::vector<Type> parameters_; Type return_type_; }; struct ClassType { std::vector<std::string> name_; std::vector<Type> template_args_; }; struct VariableDec { std::string _name; Type _type; }; }} BOOST_FUSION_ADAPT_STRUCT(client::ast::LambdaType, parameters_, return_type_) BOOST_FUSION_ADAPT_STRUCT(client::ast::ClassType, name_, template_args_) BOOST_FUSION_ADAPT_STRUCT(client::ast::VariableDec, _name, _type) #endif //LINKER_ERROR_EXAMPLE_AST_HPP_HPP
config.hpp
#ifndef LINKER_ERROR_EXAMPLE_CONFIG_HPP #define LINKER_ERROR_EXAMPLE_CONFIG_HPP #include <boost/spirit/home/x3.hpp> namespace client{ namespace parser{ namespace x3 = boost::spirit::x3; typedef std::string::const_iterator iterator_type; typedef x3::phrase_parse_context<x3::ascii::space_type>::type context_type; } } #endif //LINKER_ERROR_EXAMPLE_CONFIG_HPP
example_def.hpp
#ifndef LINKER_ERROR_EXAMPLE_EXAMPLE_DEF_HPP #define LINKER_ERROR_EXAMPLE_EXAMPLE_DEF_HPP #include "ast.hpp" #include "example.hpp" #include "types.hpp" namespace client { namespace parser { #ifndef SINGLE_TU namespace { const auto& type = client::type(); } #endif const var_dec_type var_dec = "var_dec"; #define EXAMPLE_DEF_LINK_ERR #if defined(EXAMPLE_DEF_LINK_ERR) #pragma message "yesdef(EXAMPLE_DEF_LINK_ERR)" #else #pragma message "notdef(EXAMPLE_DEF_LINK_ERR)" #endif auto const var_dec_def = x3::lexeme["var "] > +x3::alnum > ":" #ifdef EXAMPLE_DEF_LINK_ERR >> type //<- this gets linker error. #else > type //<- This links. #endif > ";"; BOOST_SPIRIT_DEFINE( var_dec ) }} namespace client { const parser::var_dec_type& var_dec() { return parser::var_dec; } } #endif //LINKER_ERROR_EXAMPLE_EXAMPLE_DEF_HPP
example.hpp
#ifndef LINKER_ERROR_EXAMPLE_EXAMPLE_HPP #define LINKER_ERROR_EXAMPLE_EXAMPLE_HPP #include <boost/spirit/home/x3.hpp> #include "ast.hpp" namespace client { namespace parser { namespace x3 = boost::spirit::x3; class var_dec_class {}; typedef x3::rule<var_dec_class, ast::VariableDec> var_dec_type; BOOST_SPIRIT_DECLARE(var_dec_type) }} namespace client { const parser::var_dec_type& var_dec(); } #endif //LINKER_ERROR_EXAMPLE_EXAMPLE_HPP
types_def.hpp
#ifndef KYLE_TYPES_DEF_HPP #define KYLE_TYPES_DEF_HPP #include "types.hpp" namespace client { namespace parser { namespace x3 = boost::spirit::x3; typedef x3::rule<struct lambda_type_class, ast::LambdaType> lambda_type_type; const class_type_type class_type = "class_type"; const lambda_type_type lambda_type = "lambda_type"; const type_type type = "type"; auto const identifier = +x3::alnum; auto const type_def = (lambda_type | class_type); auto const lambda_type_def = ("(" > -(type % ",") > ")" > "=>" > type) | (x3::repeat(1)[class_type] >> "=>" > type); auto const class_type_def = (identifier % "::") >> -("<" > type % "," > ">"); BOOST_SPIRIT_DEFINE( lambda_type, class_type, type ) }} namespace client { const parser::class_type_type& class_type() { return parser::class_type; } const parser::type_type& type() { return parser::type; } } #endif //KYLE_TYPES_DEF_HPP
types.hpp
#ifndef KYLE_PARSER_TYPES_HPP #define KYLE_PARSER_TYPES_HPP #include <boost/spirit/home/x3.hpp> #include "ast.hpp" namespace client { namespace parser { namespace x3 = boost::spirit::x3; struct class_type_class; struct type_class; typedef x3::rule<class_type_class, ast::ClassType> class_type_type; typedef x3::rule<type_class, ast::Type> type_type; BOOST_SPIRIT_DECLARE(class_type_type, type_type) }} namespace client { const parser::class_type_type& class_type(); const parser::type_type& type(); } #endif