使用 boost::spirit::qi (error_invalid_expression) 为 glm::vec3 解析 3 个浮点数
parsing 3 floats for glm::vec3 using boost::spirit::qi (error_invalid_expression)
我可以解析一个浮点数并打印出来。 (test1
, test2
)
不知何故,我无法建立一个解析三个浮点数的规则。
我的最终目标是解析三个浮点数并将它们保存到 glm::vec3
.
- 我的规则似乎不正确:
qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_]
- 我不确定我是否正确使用
BOOST_FUSION_ADAPT_STRUCT
这里是展示我到目前为止的想法的来源:
#include <iostream>
#include <string>
#include <glm\glm.hpp>
#include <boost\spirit\include\qi.hpp>
#include <boost\fusion\include\adapt_struct.hpp>
namespace qi = boost::spirit::qi;
void test1()
{
std::string test = "1.2";
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), qi::float_, qi::space) && (it == test.end()))
std::cout << "test 1 ok" << std::endl;
else
std::cout << "test 1 not ok" << std::endl;
}
void test2()
{
std::string test = "1.2";
auto it = test.begin();
float f;
if (qi::phrase_parse(it, test.end(), qi::float_, qi::space, f) && (it == test.end()))
std::cout << "test 2 ok " << f << std::endl;
else
std::cout << "test 2 not ok" << std::endl;
}
void test3()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_];
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space) && (it == test.end()))
std::cout << "test 3 ok " << std::endl;
else
std::cout << "test 3 not ok" << std::endl;
}
BOOST_FUSION_ADAPT_STRUCT(
glm::vec3,
(float, x)
(float, y)
(float, z)
)
void test4()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_];
glm::vec3 result;
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space, result) && (it == test.end()))
{
std::cout << "test 4 ok (" << result.x << ", " << result.y << ", " << result.z << ")"<< std::endl;
}
else
std::cout << "test 4 not ok" << std::endl;
}
int main(int argc, char ** argv)
{
test1();
test2();
test3();
test4();
}
test4
函数包含我尝试完成的所有内容。
编辑:
根据建议需要更改两件事:
void test4()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, glm::vec3(), qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ >> ' ' >> qi::float_ >> ' ' >> qi::float_];
glm::vec3 result;
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space, result) && (it == test.end()))
{
std::cout << "test 4 ok (" << result.x << ", " << result.y << ", " << result.z << ")"<< std::endl;
}
else
std::cout << "test 4 not ok" << std::endl;
}
对于简单明了的事情来说,似乎需要做大量的工作。
#include <iostream>
#include <string>
#include <sstream>
int main() {
std::string test = "1.2 2.2 3.3";
float f1, f2, f3;
std::istringstream in(test);
in >> f1 >> f2 >> f3;
return 0;
}
sequence parser operator 是 >>
,在您的规则中用它代替 <<
VEC3 = qi::lexeme[qi::float_ >> ' ' >> qi::float_ >> ' ' >> qi::float_];
您在规则中指定了白色 space 船长,因此可以通过删除 lexeme
指令并让船长自动跳过 spaces 来进一步简化它(除非您要确保输入之间有一个 space 字符)。
VEC3 = qi::float_ >> qi::float_ >> qi::float_;
此外,如果您希望 rule 成为 return 一个值,您需要将该签名添加到规则类型
qi::rule<std::string::iterator, glm::vec3(), qi::space_type> VEC3;
这与您看到的编译错误无关,但在您的 #include
指令中使用正斜杠,它适用于所有平台。
#include <boost/spirit/include/qi.hpp>
我同意皮特·贝克尔的观点。 通常越简单越好。
现在既然要用精神了,那我们看看有什么可以更简单的:
使用c++11适配:
BOOST_FUSION_ADAPT_STRUCT(glm::vec3, x, y, z)
你可以不用 adapt(演示中的simpler
)
如果您正在执行顶层 lexeme[]
指令,您可以(应该)从规则声明中删除船长 ¹。参见 Whosebug。com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965
更好的是,这里没有可读性规则。实际上,您会有一个包含多个规则的更大的语法。 一定不要每次解析都实例化语法(为了效率)。
演示测试平台Live On Coliru
//#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <glm/glm.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
glm::vec3 simpler(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (parse(it, test.end(),
float_ >> ' ' >> float_ >> ' ' >> float_ >> eoi,
result.x, result.y, result.z))
{
return result;
}
throw std::runtime_error("invalid input");
}
glm::vec3 use_skipper(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (phrase_parse(it, test.end(),
float_ >> float_ >> float_ >> eoi,
space, result.x, result.y, result.z))
{
return result;
}
throw std::runtime_error("invalid input");
}
BOOST_FUSION_ADAPT_STRUCT(glm::vec3, x, y, z)
glm::vec3 with_adapt(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (phrase_parse(it, test.end(), float_ >> float_ >> float_ >>eoi, space, result))
{
return result;
}
throw std::runtime_error("invalid input");
}
int main() {
struct { glm::vec3 (&f)(std::string const&); std::string name; } tests[] = {
{simpler, "simpler"},
{use_skipper, "use_skipper"},
{with_adapt, "with_adapt"}
};
for (auto t : tests) try {
glm::vec3 v = t.f("1.2 2.2 3.3");
std::cout << t.name << " ok (" << v.x << ", " << v.y << ", " << v.z << ")\n";
} catch(std::exception const& e) {
std::cout << t.name << " fail (" << e.what() << ")\n";
}
}
你可以轻一点Using x3。工作量并没有减少,但编译器的负担肯定减轻了。
¹ 细微差别 w.r.t。 pre/post 跳过,但如果有包含 skipper
的包含语法,这种差异就会消失
我可以解析一个浮点数并打印出来。 (test1
, test2
)
不知何故,我无法建立一个解析三个浮点数的规则。
我的最终目标是解析三个浮点数并将它们保存到 glm::vec3
.
- 我的规则似乎不正确:
qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_]
- 我不确定我是否正确使用
BOOST_FUSION_ADAPT_STRUCT
这里是展示我到目前为止的想法的来源:
#include <iostream>
#include <string>
#include <glm\glm.hpp>
#include <boost\spirit\include\qi.hpp>
#include <boost\fusion\include\adapt_struct.hpp>
namespace qi = boost::spirit::qi;
void test1()
{
std::string test = "1.2";
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), qi::float_, qi::space) && (it == test.end()))
std::cout << "test 1 ok" << std::endl;
else
std::cout << "test 1 not ok" << std::endl;
}
void test2()
{
std::string test = "1.2";
auto it = test.begin();
float f;
if (qi::phrase_parse(it, test.end(), qi::float_, qi::space, f) && (it == test.end()))
std::cout << "test 2 ok " << f << std::endl;
else
std::cout << "test 2 not ok" << std::endl;
}
void test3()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_];
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space) && (it == test.end()))
std::cout << "test 3 ok " << std::endl;
else
std::cout << "test 3 not ok" << std::endl;
}
BOOST_FUSION_ADAPT_STRUCT(
glm::vec3,
(float, x)
(float, y)
(float, z)
)
void test4()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ << ' ' << qi::float_ << ' ' << qi::float_];
glm::vec3 result;
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space, result) && (it == test.end()))
{
std::cout << "test 4 ok (" << result.x << ", " << result.y << ", " << result.z << ")"<< std::endl;
}
else
std::cout << "test 4 not ok" << std::endl;
}
int main(int argc, char ** argv)
{
test1();
test2();
test3();
test4();
}
test4
函数包含我尝试完成的所有内容。
编辑:
根据建议需要更改两件事:
void test4()
{
std::string test = "1.2 2.2 3.3";
qi::rule<std::string::iterator, glm::vec3(), qi::space_type> VEC3;
//error_invalid_expression
VEC3 = qi::lexeme[qi::float_ >> ' ' >> qi::float_ >> ' ' >> qi::float_];
glm::vec3 result;
auto it = test.begin();
if (qi::phrase_parse(it, test.end(), VEC3, qi::space, result) && (it == test.end()))
{
std::cout << "test 4 ok (" << result.x << ", " << result.y << ", " << result.z << ")"<< std::endl;
}
else
std::cout << "test 4 not ok" << std::endl;
}
对于简单明了的事情来说,似乎需要做大量的工作。
#include <iostream>
#include <string>
#include <sstream>
int main() {
std::string test = "1.2 2.2 3.3";
float f1, f2, f3;
std::istringstream in(test);
in >> f1 >> f2 >> f3;
return 0;
}
sequence parser operator 是 >>
,在您的规则中用它代替 <<
VEC3 = qi::lexeme[qi::float_ >> ' ' >> qi::float_ >> ' ' >> qi::float_];
您在规则中指定了白色 space 船长,因此可以通过删除 lexeme
指令并让船长自动跳过 spaces 来进一步简化它(除非您要确保输入之间有一个 space 字符)。
VEC3 = qi::float_ >> qi::float_ >> qi::float_;
此外,如果您希望 rule 成为 return 一个值,您需要将该签名添加到规则类型
qi::rule<std::string::iterator, glm::vec3(), qi::space_type> VEC3;
这与您看到的编译错误无关,但在您的 #include
指令中使用正斜杠,它适用于所有平台。
#include <boost/spirit/include/qi.hpp>
我同意皮特·贝克尔的观点。 通常越简单越好。
现在既然要用精神了,那我们看看有什么可以更简单的:
使用c++11适配:
BOOST_FUSION_ADAPT_STRUCT(glm::vec3, x, y, z)
你可以不用 adapt(演示中的
simpler
)如果您正在执行顶层
lexeme[]
指令,您可以(应该)从规则声明中删除船长 ¹。参见 Whosebug。com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965更好的是,这里没有可读性规则。实际上,您会有一个包含多个规则的更大的语法。 一定不要每次解析都实例化语法(为了效率)。
演示测试平台Live On Coliru
//#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <glm/glm.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
glm::vec3 simpler(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (parse(it, test.end(),
float_ >> ' ' >> float_ >> ' ' >> float_ >> eoi,
result.x, result.y, result.z))
{
return result;
}
throw std::runtime_error("invalid input");
}
glm::vec3 use_skipper(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (phrase_parse(it, test.end(),
float_ >> float_ >> float_ >> eoi,
space, result.x, result.y, result.z))
{
return result;
}
throw std::runtime_error("invalid input");
}
BOOST_FUSION_ADAPT_STRUCT(glm::vec3, x, y, z)
glm::vec3 with_adapt(std::string const& test) {
auto it = test.begin();
glm::vec3 result;
using namespace qi;
if (phrase_parse(it, test.end(), float_ >> float_ >> float_ >>eoi, space, result))
{
return result;
}
throw std::runtime_error("invalid input");
}
int main() {
struct { glm::vec3 (&f)(std::string const&); std::string name; } tests[] = {
{simpler, "simpler"},
{use_skipper, "use_skipper"},
{with_adapt, "with_adapt"}
};
for (auto t : tests) try {
glm::vec3 v = t.f("1.2 2.2 3.3");
std::cout << t.name << " ok (" << v.x << ", " << v.y << ", " << v.z << ")\n";
} catch(std::exception const& e) {
std::cout << t.name << " fail (" << e.what() << ")\n";
}
}
你可以轻一点Using x3。工作量并没有减少,但编译器的负担肯定减轻了。
¹ 细微差别 w.r.t。 pre/post 跳过,但如果有包含 skipper
的包含语法,这种差异就会消失