在我将语法分成推荐的语法后,我遇到了 boostspirit x3 的奇怪行为parser.hpp
, parser_def.hpp
, parser.cpp
文件。
我的示例语法解析某种简单的枚举:
enum = "enum" > identifier > "{" > identifier % "," > "}
这是我的枚举语法。
当我不将枚举和标识符解析器拆分为推荐的文件时,一切正常,尤其是字符串"enum {foo, bar}"
正如预期的那样,抛出预期失败。
这个例子可以在这里找到:未分割的工作示例
但是当我将完全相同的语法分成不同的文件时,解析器会抛出
terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
尝试解析相同的字符串"enum {foo, bar}"
这个例子可以在这里找到:分裂奇怪的例子
-
ast.hpp
#pragma once
#include <vector>
#include <string>
#include <boost/fusion/include/adapt_struct.hpp>
namespace ast{
namespace x3 = boost::spirit::x3;
struct Enum {
std::string _name;
std::vector<std::string> _elements;
};
}
BOOST_FUSION_ADAPT_STRUCT(ast::Enum, _name, _elements)
-
配置文件
#pragma once
#include <boost/spirit/home/x3.hpp>
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;
}
-
enum.cpp
#include "enum_def.hpp"
#include "config.hpp"
namespace parser { namespace impl {
BOOST_SPIRIT_INSTANTIATE(enum_type, iterator_type, context_type)
}}
namespace parser {
const impl::enum_type& enum_parser()
{
return impl::enum_parser;
}
}
-
枚举_def.hpp
#pragma once
#include "identifier.hpp"
#include "enum.hpp"
#include "ast.hpp"
namespace parser{ namespace impl{
namespace x3=boost::spirit::x3;
const enum_type enum_parser = "enum";
namespace{
const auto& identifier = parser::identifier();
}
auto const enum_parser_def =
"enum"
> identifier
> "{"
> identifier % ","
>"}";
BOOST_SPIRIT_DEFINE(enum_parser)
}}
-
enum.hpp
#pragma once
#include <boost/spirit/home/x3.hpp>
#include "ast.hpp"
namespace parser{ namespace impl{
namespace x3=boost::spirit::x3;
typedef x3::rule<class enum_class, ast::Enum> enum_type;
BOOST_SPIRIT_DECLARE(enum_type)
}}
namespace parser{
const impl::enum_type& enum_parser();
}
-
标识符.cpp
#include "identifier_def.hpp"
#include "config.hpp"
namespace parser { namespace impl {
BOOST_SPIRIT_INSTANTIATE(identifier_type, iterator_type, context_type)
}}
namespace parser {
const impl::identifier_type& identifier()
{
return impl::identifier;
}
}
-
标识符_def.hpp
#pragma once
#include <boost/spirit/home/x3.hpp>
#include "identifier.hpp"
namespace parser{ namespace impl{
namespace x3=boost::spirit::x3;
const identifier_type identifier = "identifier";
auto const identifier_def = x3::lexeme[
((x3::alpha | '_') >> *(x3::alnum | '_'))
];
BOOST_SPIRIT_DEFINE(identifier)
}}
-
标识符.hpp
#pragma once
#include <boost/spirit/home/x3.hpp>
namespace parser{ namespace impl{
namespace x3=boost::spirit::x3;
typedef x3::rule<class identifier_class, std::string> identifier_type;
BOOST_SPIRIT_DECLARE(identifier_type)
}}
namespace parser{
const impl::identifier_type& identifier();
}
-
main.cpp
#include <boost/spirit/home/x3.hpp>
#include "ast.hpp"
#include "enum.hpp"
namespace x3 = boost::spirit::x3;
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 = x3::phrase_parse(in, end, p, x3::ascii::space, attr);
ret &= (in == end);
return ret;
}
int main(){
ast::Enum attr;
test("enum foo{foo,bar}", parser::enum_parser(), attr);
test("enum {foo,bar}", parser::enum_parser(), attr);
}
这是一个错误,我错过了什么,还是这是一个预期的行为?
EDIT: here是我的存储库,其中包含一个抛出异常的示例std::logic_error
而不是expectation_failure
我已经找到了错误的原因。
该错误在于,expect 指令按值获取主题解析器,该值位于parser::impl::identifier
初始化程序运行。
为了形象化,想象一下静态初始化器parser::impl::enum_parser
之前运行parser::impl::identifier
。这对于编译器来说是有效的。
因此,该副本具有未初始化的name
场,一旦期望点试图构建x3::expectation_failure
与which_
成员,因为构建一个std::string
from a nullptr
是非法的。
总而言之,我担心这里的根本原因是静态初始化顺序惨败。我会看看是否可以修复它并提交 PR。
解决方法:
一个直接的解决方法是反向列出源文件的顺序,以便在定义之后使用:
set(SOURCE_FILES
identifier.cpp
enum.cpp
main.cpp
)
请注意,如果这在您的编译器上修复了它(它在我的编译器上修复了),那么它是实现定义的。该标准没有指定跨编译单元的静态初始化的顺序。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)