boost phoenix 绑定具有多个参数和 return 值的语义操作
boost phoenix bind a semantic action with multiple parameter and a return value
我对 C++ 和 Boost 精神都是新手。
我现在被困在这个问题上一天了。
我想解析由点分隔的两个字符串。
基本上,我需要将以下字符串解析为整数。
例如:[字段] --> 整数 // 有效
eg2: [instance.field] --> 整数 // 不工作
对于第二个,我需要将两个字符串作为参数并计算它们和 return 相关的整数值。
我肯定漏掉了一个基本的东西,但我想不通。
请让我知道我的代码中的错误或更好的方法。
需要调用方法并获取值。我无法改变这一点。
这是密码和平。
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_utree.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace spirit = boost::spirit;
namespace phx = boost::phoenix;
int fieldVal(std::vector<char> fieldName) {
std::string fieldValue(fieldName.begin(), fieldName.end());
std::cout << "Field Name Recieved.::: " << fieldValue << std::endl;
int i = 50; //just for test
return i;
}
int instanceFieldVal(std::string instance, std::string fieldName) {
std::cout << "Recieved ::: " << instance <<" : "<< fieldName << std::endl;
int i = 60; //just for test
return i;
}
namespace client
{
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ascii::space_type, int()>
{
calculator() : calculator::base_type(instanceFieldValue)
/*change base type to "field" and comment everything relevant to
"instanceFieldValue", then it's working */
{
using qi::int_;
using qi::_val;
using qi::_1;
using qi::char_;
using qi::lexeme;
field = lexeme[
'['
>> +(~char_(".]["))
>> ']'
][qi::_val = phx::bind(&fieldVal, qi::_1)]; // this is working
instanceField = '['
>> +(~char_(".]["))
>> '.'
>> +(~char_(".]["))
>> ']';
instanceFieldValue
= instanceField[qi::_val = phx::bind(&instanceFieldVal, qi::_1)];
// how ^this line should be changed??
}
qi::rule<Iterator, ascii::space_type, int()> field, instanceFieldValue;
qi::rule<Iterator, ascii::space_type, std::string(), std::string()>instanceField;
};
}
int main()
{
std::cout << "Type an expression...or [q or Q] to quit\n\n";
using boost::spirit::ascii::space;
using boost::spirit::utree;
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
calculator calc; // Our grammar
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
int val;
bool r = phrase_parse(iter, end, calc, space, val);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded: " << val << "\n";
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}
好吧,你说它是一个有多个参数的函数,但你只传递了一个参数:_1
.
这怎么行?它不能。它也完全是您想要传递的内容,因为第二个参数可能来自 instanceField
表达式,但第一个参数是......神奇的上下文。
像往常一样,我会尽量减少状态和语义操作。事实上,我建议关注点分离:
- 先解析为
std:vector<std::string>
- 成功解析后,对其进行评估
这会导致像这样的语法
template <typename Iterator>
struct path : qi::grammar<Iterator, std::deque<std::string>()> {
path() : path::base_type(start) {
using namespace qi;
name = +(graph - char_(".][")); // not matching spaces please
qualifiedName = name % '.';
start = skip(ascii::space) ['[' >> qualifiedName >> ']'];
BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
}
private:
qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
qi::rule<Iterator, std::string()> name;
qi::rule<Iterator, std::deque<std::string>()> start;
};
你当然可以如果你坚持的话,当然可以使用语义动作将它组合起来:
template <typename Iterator>
struct property : qi::grammar<Iterator, int()> {
property() : property::base_type(start) {
using namespace qi;
name = +(graph - char_(".][")); // not matching spaces please
qualifiedName = name % '.';
start = skip(ascii::space) ['[' >> qualifiedName >> ']']
[_val = phx::bind(lookup, phx::cref(sample), _1) ]
;
BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
}
private:
qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
qi::rule<Iterator, std::string()> name;
qi::rule<Iterator, int()> start;
};
现在,只需实现一个功能
int lookup(Node const& context, std::deque<std::string> path);
举个例子,让我们想象一个示例上下文:
using Leaf = int;
using Node = boost::make_recursive_variant< Leaf, std::map<std::string, boost::recursive_variant_> >::type;
using SubTree = std::map<std::string, Node>;
static Node sample = SubTree {
{ "simple", 100 },
{ "compound", SubTree {
{ "first", 200 },
{ "second", 300 },
}, },
{ "deep", SubTree {
{ "nested", SubTree {
{ "compound", SubTree {
{ "buried", 400 },
{ "secrets", 500 },
}, }, }, }, }, }
};
现在,lookup
的实现可以像这样简单:
int lookup(Node const& context, std::deque<std::string> path) {
if (path.empty())
return boost::get<Leaf>(context);
auto& sub = boost::get<SubTree>(context);
auto element = path.front();
path.pop_front();
try {
return lookup(sub.at(element), std::move(path));
} catch(std::out_of_range const& e) {
throw std::runtime_error("'" + element + "' not found");
}
}
完整演示
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <map>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
using Leaf = int;
using Node = boost::make_recursive_variant< Leaf, std::map<std::string, boost::recursive_variant_> >::type;
using SubTree = std::map<std::string, Node>;
static Node sample = SubTree {
{ "simple", 100 },
{ "compound", SubTree {
{ "first", 200 },
{ "second", 300 },
}, },
{ "deep", SubTree {
{ "nested", SubTree {
{ "compound", SubTree {
{ "buried", 400 },
{ "secrets", 500 },
},
},
},
},
},
}
};
int lookup(Node const& context, std::deque<std::string> path) {
if (path.empty())
return boost::get<Leaf>(context);
auto& sub = boost::get<SubTree>(context);
auto element = path.front();
path.pop_front();
try {
return lookup(sub.at(element), std::move(path));
} catch(std::out_of_range const& e) {
throw std::runtime_error("'" + element + "' not found");
}
}
namespace parser {
template <typename Iterator>
struct property : qi::grammar<Iterator, int()> {
property() : property::base_type(start) {
using namespace qi;
name = +(graph - char_(".][")); // not matching spaces please
qualifiedName = name % '.';
start = skip(ascii::space) ['[' >> qualifiedName >> ']']
[_val = phx::bind(lookup, phx::cref(sample), _1) ]
;
BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
}
private:
qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
qi::rule<Iterator, std::string()> name;
qi::rule<Iterator, int()> start;
};
}
int main() {
using It = std::string::const_iterator;
parser::property<It> calc;
for (std::string const str : {
"[simple]",
"[compound.first]",
"[compound.second]",
"[deep.nested.compound.buried]",
"[deep.nested.compound.secrets]",
// whitespace is ok
" [ compound.\tfirst ]",
// failing:
"[]",
"[missing]",
"[deep.missing.compound.buried]",
// whitespace not ok inside names
" [ compound.\tfi rst ]",
})
try {
std::cout << " ===== Input: '" << str << "'\n";
It iter = str.begin(), end = str.end();
int val;
bool r = parse(iter, end, calc, val);
if (r) {
std::cout << "Parsing succeeded: " << val << "\n";
} else {
std::cout << "Parsing failed\n";
}
if (iter != end) {
std::cout << " - Remaining unparsed input: '" << std::string(iter, end) << "'\n";
}
} catch(std::exception const& e) {
std::cout << "Exception: " << e.what() << "\n";
}
}
打印:
===== Input: '[simple]'
Parsing succeeded: 100
===== Input: '[compound.first]'
Parsing succeeded: 200
===== Input: '[compound.second]'
Parsing succeeded: 300
===== Input: '[deep.nested.compound.buried]'
Parsing succeeded: 400
===== Input: '[deep.nested.compound.secrets]'
Parsing succeeded: 500
===== Input: ' [ compound. first ]'
Parsing succeeded: 200
===== Input: '[]'
Parsing failed
- Remaining unparsed input: '[]'
===== Input: '[missing]'
Exception: 'missing' not found
===== Input: '[deep.missing.compound.buried]'
Exception: 'missing' not found
===== Input: ' [ compound. fi rst ]'
Parsing failed
- Remaining unparsed input: ' [ compound. fi rst ]'
我对 C++ 和 Boost 精神都是新手。
我现在被困在这个问题上一天了。 我想解析由点分隔的两个字符串。 基本上,我需要将以下字符串解析为整数。
例如:[字段] --> 整数 // 有效
eg2: [instance.field] --> 整数 // 不工作
对于第二个,我需要将两个字符串作为参数并计算它们和 return 相关的整数值。
我肯定漏掉了一个基本的东西,但我想不通。
请让我知道我的代码中的错误或更好的方法。 需要调用方法并获取值。我无法改变这一点。
这是密码和平。
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_utree.hpp>
#include <iostream>
#include <string>
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace spirit = boost::spirit;
namespace phx = boost::phoenix;
int fieldVal(std::vector<char> fieldName) {
std::string fieldValue(fieldName.begin(), fieldName.end());
std::cout << "Field Name Recieved.::: " << fieldValue << std::endl;
int i = 50; //just for test
return i;
}
int instanceFieldVal(std::string instance, std::string fieldName) {
std::cout << "Recieved ::: " << instance <<" : "<< fieldName << std::endl;
int i = 60; //just for test
return i;
}
namespace client
{
template <typename Iterator>
struct calculator : qi::grammar<Iterator, ascii::space_type, int()>
{
calculator() : calculator::base_type(instanceFieldValue)
/*change base type to "field" and comment everything relevant to
"instanceFieldValue", then it's working */
{
using qi::int_;
using qi::_val;
using qi::_1;
using qi::char_;
using qi::lexeme;
field = lexeme[
'['
>> +(~char_(".]["))
>> ']'
][qi::_val = phx::bind(&fieldVal, qi::_1)]; // this is working
instanceField = '['
>> +(~char_(".]["))
>> '.'
>> +(~char_(".]["))
>> ']';
instanceFieldValue
= instanceField[qi::_val = phx::bind(&instanceFieldVal, qi::_1)];
// how ^this line should be changed??
}
qi::rule<Iterator, ascii::space_type, int()> field, instanceFieldValue;
qi::rule<Iterator, ascii::space_type, std::string(), std::string()>instanceField;
};
}
int main()
{
std::cout << "Type an expression...or [q or Q] to quit\n\n";
using boost::spirit::ascii::space;
using boost::spirit::utree;
typedef std::string::const_iterator iterator_type;
typedef client::calculator<iterator_type> calculator;
calculator calc; // Our grammar
std::string str;
while (std::getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;
std::string::const_iterator iter = str.begin();
std::string::const_iterator end = str.end();
int val;
bool r = phrase_parse(iter, end, calc, space, val);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded: " << val << "\n";
std::cout << "-------------------------\n";
}
else
{
std::string rest(iter, end);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << rest << "\"\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}
好吧,你说它是一个有多个参数的函数,但你只传递了一个参数:_1
.
这怎么行?它不能。它也完全是您想要传递的内容,因为第二个参数可能来自 instanceField
表达式,但第一个参数是......神奇的上下文。
像往常一样,我会尽量减少状态和语义操作。事实上,我建议关注点分离:
- 先解析为
std:vector<std::string>
- 成功解析后,对其进行评估
这会导致像这样的语法
template <typename Iterator>
struct path : qi::grammar<Iterator, std::deque<std::string>()> {
path() : path::base_type(start) {
using namespace qi;
name = +(graph - char_(".][")); // not matching spaces please
qualifiedName = name % '.';
start = skip(ascii::space) ['[' >> qualifiedName >> ']'];
BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
}
private:
qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
qi::rule<Iterator, std::string()> name;
qi::rule<Iterator, std::deque<std::string>()> start;
};
你当然可以如果你坚持的话,当然可以使用语义动作将它组合起来:
template <typename Iterator>
struct property : qi::grammar<Iterator, int()> {
property() : property::base_type(start) {
using namespace qi;
name = +(graph - char_(".][")); // not matching spaces please
qualifiedName = name % '.';
start = skip(ascii::space) ['[' >> qualifiedName >> ']']
[_val = phx::bind(lookup, phx::cref(sample), _1) ]
;
BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
}
private:
qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
qi::rule<Iterator, std::string()> name;
qi::rule<Iterator, int()> start;
};
现在,只需实现一个功能
int lookup(Node const& context, std::deque<std::string> path);
举个例子,让我们想象一个示例上下文:
using Leaf = int;
using Node = boost::make_recursive_variant< Leaf, std::map<std::string, boost::recursive_variant_> >::type;
using SubTree = std::map<std::string, Node>;
static Node sample = SubTree {
{ "simple", 100 },
{ "compound", SubTree {
{ "first", 200 },
{ "second", 300 },
}, },
{ "deep", SubTree {
{ "nested", SubTree {
{ "compound", SubTree {
{ "buried", 400 },
{ "secrets", 500 },
}, }, }, }, }, }
};
现在,lookup
的实现可以像这样简单:
int lookup(Node const& context, std::deque<std::string> path) {
if (path.empty())
return boost::get<Leaf>(context);
auto& sub = boost::get<SubTree>(context);
auto element = path.front();
path.pop_front();
try {
return lookup(sub.at(element), std::move(path));
} catch(std::out_of_range const& e) {
throw std::runtime_error("'" + element + "' not found");
}
}
完整演示
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <iostream>
#include <map>
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
using Leaf = int;
using Node = boost::make_recursive_variant< Leaf, std::map<std::string, boost::recursive_variant_> >::type;
using SubTree = std::map<std::string, Node>;
static Node sample = SubTree {
{ "simple", 100 },
{ "compound", SubTree {
{ "first", 200 },
{ "second", 300 },
}, },
{ "deep", SubTree {
{ "nested", SubTree {
{ "compound", SubTree {
{ "buried", 400 },
{ "secrets", 500 },
},
},
},
},
},
}
};
int lookup(Node const& context, std::deque<std::string> path) {
if (path.empty())
return boost::get<Leaf>(context);
auto& sub = boost::get<SubTree>(context);
auto element = path.front();
path.pop_front();
try {
return lookup(sub.at(element), std::move(path));
} catch(std::out_of_range const& e) {
throw std::runtime_error("'" + element + "' not found");
}
}
namespace parser {
template <typename Iterator>
struct property : qi::grammar<Iterator, int()> {
property() : property::base_type(start) {
using namespace qi;
name = +(graph - char_(".][")); // not matching spaces please
qualifiedName = name % '.';
start = skip(ascii::space) ['[' >> qualifiedName >> ']']
[_val = phx::bind(lookup, phx::cref(sample), _1) ]
;
BOOST_SPIRIT_DEBUG_NODES((start)(qualifiedName)(name))
}
private:
qi::rule<Iterator, std::deque<std::string>(), qi::ascii::space_type> qualifiedName;
qi::rule<Iterator, std::string()> name;
qi::rule<Iterator, int()> start;
};
}
int main() {
using It = std::string::const_iterator;
parser::property<It> calc;
for (std::string const str : {
"[simple]",
"[compound.first]",
"[compound.second]",
"[deep.nested.compound.buried]",
"[deep.nested.compound.secrets]",
// whitespace is ok
" [ compound.\tfirst ]",
// failing:
"[]",
"[missing]",
"[deep.missing.compound.buried]",
// whitespace not ok inside names
" [ compound.\tfi rst ]",
})
try {
std::cout << " ===== Input: '" << str << "'\n";
It iter = str.begin(), end = str.end();
int val;
bool r = parse(iter, end, calc, val);
if (r) {
std::cout << "Parsing succeeded: " << val << "\n";
} else {
std::cout << "Parsing failed\n";
}
if (iter != end) {
std::cout << " - Remaining unparsed input: '" << std::string(iter, end) << "'\n";
}
} catch(std::exception const& e) {
std::cout << "Exception: " << e.what() << "\n";
}
}
打印:
===== Input: '[simple]'
Parsing succeeded: 100
===== Input: '[compound.first]'
Parsing succeeded: 200
===== Input: '[compound.second]'
Parsing succeeded: 300
===== Input: '[deep.nested.compound.buried]'
Parsing succeeded: 400
===== Input: '[deep.nested.compound.secrets]'
Parsing succeeded: 500
===== Input: ' [ compound. first ]'
Parsing succeeded: 200
===== Input: '[]'
Parsing failed
- Remaining unparsed input: '[]'
===== Input: '[missing]'
Exception: 'missing' not found
===== Input: '[deep.missing.compound.buried]'
Exception: 'missing' not found
===== Input: ' [ compound. fi rst ]'
Parsing failed
- Remaining unparsed input: ' [ compound. fi rst ]'