Parsing nested lists with Boost.Spirit - c++

I am trying to parse nested lists of numbers with Boost.Spirit. This is what I have so far:
//define a test string
std::string string = "[[\t1 , 2 ], [3, 4, 65.4]]";
auto it = string.begin();
//parse the string
std::vector<std::vector<double>> vector;
auto listRule = "[" >> (qi::double_ % ",") >> "]";
auto list2Rule = "[" >> (listRule % ",") >> "]";
bool match = qi::phrase_parse(it, string.end(), list2Rule, ascii::space, vector);
//check if we have a match
std::cout << "matched: " << std::boolalpha << match << '\n';
if (it != string.end())
std::cout << "unmatched part: " << std::string{it, string.end()} << '\n';
//print the result
std::cout << "result\n";
for (const auto& v : vector) {
std::cout << "[";
for (double i : v)
std::cout << i << ",";
std::cout << "]\n";
}
The above works wonderfully and prints:
matched: true
result
[1,2,]
[3,4,65.4,]
The problem I am facing is that it does not accept empty lists. For example, by changing the string like so:
std::string string = "[[\t1 , 2 ], [3, 4, 65.4], []]";
Then I have no match (that is match == false and it == string.begin()). The vector still gets populated, apparently, but the last empty list is missing. Can anyone provide an explanation on why this is the case, and how to fix it?

You're using auto for proto expression templates in the Qi domain - that's undefined behaviour 99.9% of the time:
undefined behaviour somewhere in boost::spirit::qi::phrase_parse
Now, while you fix that, also make the list body optional:
Live On Coliru
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main() {
using It = std::string::const_iterator;
using Skipper = qi::space_type;
for(std::string const input : { "[[\t1 , 2 ], [3, 4, 65.4]]", "[[\t1 , 2 ], [3, 4, 65.4], []]", "[]" })
{
std::cout << " ---- '" << input << "' ----\n";
auto it = input.begin();
//parse the string
using doubles = std::vector<double>;
using vectors = std::vector<doubles>;
qi::rule<It, doubles(), Skipper> doubles_ = "[" >> -(qi::double_ % ",") >> "]";
qi::rule<It, vectors(), Skipper> vectors_ = "[" >> -(doubles_ % ",") >> "]";
vectors data;
bool match = qi::phrase_parse(it, input.end(), vectors_, qi::space, data);
//check if we have a match
std::cout << "matched: " << std::boolalpha << match << '\n';
if (it != input.end())
std::cout << "unmatched part: " << std::string{it, input.end()} << '\n';
//print the result
std::cout << "result\n";
for (const auto& v : data) {
std::cout << "[";
for (double i : v)
std::cout << i << ",";
std::cout << "]\n";
}
}
}
Prints
---- '[[ 1 , 2 ], [3, 4, 65.4]]' ----
matched: true
result
[1,2,]
[3,4,65.4,]
---- '[[ 1 , 2 ], [3, 4, 65.4], []]' ----
matched: true
result
[1,2,]
[3,4,65.4,]
[]
---- '[]' ----
matched: true
result

Related

How can I change map 's second component?

#include <iostream>
#include <map>
using namespace std;
int main() {
map<int, int> ma;
// 원소를 추가 하자!!
ma.insert(make_pair(100, 2)); // key 값 : 1 , value : 3
ma.insert(make_pair(101, 3)); // key 값 : 3, value : 13
ma.insert(make_pair(102, 2)); // key 값 : 3, value : 13
ma.insert(make_pair(103, 3)); // key 값 : 3, value : 13
ma.insert(make_pair(104, 1)); // key 값 : 3, value : 13
// make_pair 형식으로 저장 했으므로
// key 값은 fisrt 로 접근 value 값은 second 로 접근한다.
for (auto iter : ma) {
cout << "key : " << iter.first << " value : " << iter.second << '\n';
iter.second = iter.second + 1;
}
cout << "\n" << "\n";
for (auto iter : ma) {
cout << "key : " << iter.first << " value : " << iter.second << '\n';
}
cout << "\n" << "\n";
return 0;
}
Actually , I want to change the value of second component of pair.
iter.second = iter.second + 1;
This code can't change the thing...
How can I change ???
The auto keyword in C++ always tries to infer a non-reference type, so in your case it infers iter as being type std::pair<const int, int>. This is not a reference type, so the values from the map get copied into the iter variable, and so any changes made to iter are not reflected in the map itself.
You can write auto& instead to force an lvalue-reference type:
for (auto& iter : ma) { // <-- Notice ampersand here
iter.second = iter.second + 1;
}
Your main problem is that you called the variable "iter" so you must think it is an iterator of some sort. If it were an iterator, your code would work fine:
#include <iostream>
#include <map>
int main() {
std::map<int, int> map {
{100, 2}, {101, 3}, {102, 2}, {103, 3}, {104, 1},
};
for (auto iter = map.begin(), end = map.end(); iter != end; ++iter) {
std::cout << "key : " << iter->first << " value : " << iter->second << '\n';
++(iter->second);
}
std::cout << "\n\n";
for (auto iter = map.begin(), end = map.end(); iter != end; ++iter) {
std::cout << "key : " << iter->first << " value : " << iter->second << '\n';
}
std::cout << "\n\n";
}
(See online)
But since you are using a range-based for loop you are accessing the values themselves, not an iterator to them. In that case, if you use auto you are getting a copy of the elements, and any changes you make are done the copies, not the elements themselves. If you instead use auto& you will get a reference instead, which is what you want:
#include <iostream>
#include <map>
int main() {
std::map<int, int> map {
{100, 2}, {101, 3}, {102, 2}, {103, 3}, {104, 1},
};
for (auto& [key, val] : map) {
std::cout << "key : " << key << " value : " << val << '\n';
++val;
}
std::cout << "\n\n";
for (auto& [key, val] : map) {
std::cout << "key : " << key << " value : " << val << '\n';
}
std::cout << "\n\n";
}
(See online)
Do note the use of structured bindings to simplify the code.

How to display a map of a map (C++)?

I made the following map to store data from a .log :
map<string,pair(int,map<string,int>)>.
I manage to store the data but not to retrieve it. What I do is:
cout<< "first string: "<< debut->first
<< " pair (first int): " << debut->second.first << endl;
(debut is a constant iterator of a map)
With that I get the first string and the int of the pair, but I don't know how to get the content of the map. I tried different syntaxes as debut->second.second->first or debut->second.second.first but of them work.
Does someone have an idea?
I don't know how to get the content of the map.
You can print out the content of the innermost map as shown below([DEMO]):
auto beg = debut->second.second.cbegin();
auto end = debut->second.second.cend();
while(beg!=end) //can also use for loop
{
std::cout<<beg->first<<" ";
std::cout<<beg->second<<std::endl;
++beg;
}
Here is a more complete example for printing the all the content of the maps, just for demonstration:
#include <iostream>
#include <map>
int main()
{
std::map<std::string,std::pair<int,std::map<std::string,int>>> m{ { "first", { 5, {{"a", 10}, {"b", 20}} }} ,
{ "second", { 6, {{"c", 100}, {"d", 200},{"e", 300} }}},
{ "third", { 7, {{"f", 400}} } }};
//////////////////////////////////////////////////////////////////
//PRINTING THE CONTENT OF THE MAP
auto debutBegin = m.cbegin(); //const iterator to beginning of outer map
auto debutEnd = m.cend(); //const iterator to end of outer map
while(debutBegin!=debutEnd)
{
auto beg = debutBegin->second.second.cbegin(); //const iterator to beginning of inner map
auto end = debutBegin->second.second.cend();//const iterator to end of inner map
std::cout<<debutBegin->first<<" ---- "<<debutBegin->second.first<<std::endl;
while(beg!=end)
{
std::cout<<beg->first<<" ";
std::cout<<beg->second<<std::endl;
++beg;
}
debutBegin++;
}
return 0;
}
Output
The output of the above program is:
first ---- 5
a 10
b 20
second ---- 6
c 100
d 200
e 300
third ---- 7
f 400
You probably need to iterate over the second map as well. Here's one way to do this:
map<string,pair<int,map<string,int>>> mp1;
map<string, int> mp2;
mp2["str2 inside mp1"] = 1;
mp1["str1 (key)"] = {2, mp2};
for(auto pair1 : mp1) {
cout << "First string: " << pair1.first << "\n"; // str1 (key)
cout << "First int: " << pair1.second.first << "\n"; // First int: 2
for(auto pair2 : pair1.second.second) {
cout << "Second string: " << pair2.first << "\n"; // Second string: str2 inside mp1
cout << "Second int: " << pair2.second << "\n"; // Second int: 1
}
}
However (as the comments suggest) this is a complicated approach, maybe you need to submit a new question explaining your usecase. My thought would be to seperate this into to seperate maps somehow.
I would recommend to:
split your printing into functions,
print one map at a time, and
use whatever number of variables/references you need to make your code clearer.
The code below is just an example that tries to follow that intent.
[Demo]
#include <iostream> // cout
#include <map>
#include <string>
#include <utility> // pair
using inner_map_t = std::map<std::string, int>;
using outer_map_t = std::map<std::string, std::pair<int, inner_map_t>>;
std::ostream& operator<<(std::ostream& os, const inner_map_t& m)
{
os << "\t{\n";
bool first_elem{true};
for (auto& [key, value] : m)
{
auto& second_string{ key };
auto& second_int{ value };
os
<< "\t\t" << (first_elem ? "" : ", ") << "second string: " << second_string << ", "
<< "second int: " << second_int << "\n";
first_elem = false;
}
os << "\t}\n";
return os;
}
std::ostream& operator<<(std::ostream& os, const outer_map_t& m)
{
os << "{\n";
bool first_elem{ true };
for (auto& [key, value] : m)
{
auto& first_string{ key };
auto& first_int{ value.first };
auto& inner_map{ value.second };
os
<< "\t" << (first_elem ? "" : ", ") << "first string: " << first_string << ", "
<< "first int: " << first_int << "\n"
<< inner_map;
first_elem = false;
}
os << "}\n";
return os;
}
int main()
{
const outer_map_t m{
{ "one", { 1, {{"uno", 10}, {"cien", 100}} } },
{ "two", { 2, {{"dos", 20}} } },
{ "three", { 3, {{"tres", 30}} } }
};
std::cout << m << "\n";
}
// Outputs:
//
// {
// first string: one, first int: 1
// {
// second string: cien, second int: 100
// , second string: uno, second int: 10
// }
// , first string: three, first int: 3
// {
// second string: tres, second int: 30
// }
// , first string: two, first int: 2
// {
// second string: dos, second int: 20
// }
// }
And another implementation, probably a bit neater.
[Demo]
#include <iostream> // cout
#include <map>
#include <string>
#include <utility> // pair
using inner_map_t = std::map<std::string, int>;
using pair_t = std::pair<int, inner_map_t>;
using outer_map_t = std::map<std::string, pair_t>;
template <typename T, typename U>
std::ostream& print_map(std::ostream& os, const std::map<T,U> m, size_t level_of_indent)
{
std::string indent(4*level_of_indent, ' ');
std::string next_indent(4*(level_of_indent + 1), ' ');
os << "{\n";
bool first{ true };
for (auto& [key, value] : m)
{
os << (first ? "" : ",\n") << next_indent << "'" << key << "' : " << value;
first = false;
}
os << "\n" << indent << "}";
return os;
}
std::ostream& operator<<(std::ostream& os, const inner_map_t& m) {
return print_map(os, m, 1);
}
std::ostream& operator<<(std::ostream& os, const pair_t& p) {
return os << "(" << p.first << ", " << p.second << ")";
}
std::ostream& operator<<(std::ostream& os, const outer_map_t& m) {
return print_map(os, m, 0);
}
int main()
{
const outer_map_t m{
{ "Alien", { 1, {{"speed", 90}, {"power", 90}} } },
{ "Jabba", { 2, {{"speed", 10}} } },
{ "T1000", { 3, {{"speed", 70}} } }
};
std::cout << m << "\n";
}
// Outputs:
//
// {
// 'Alien' : (1, {
// 'power' : 90,
// 'speed' : 90
// }),
// 'Jabba' : (2, {
// 'speed' : 10
// }),
// 'T1000' : (3, {
// 'speed' : 70
// })
// }

How can I print the elements of a vector as a ordered list using a range-based loop

Ex:
vector<string> myVec = {apple, banana, grape}
How can I print these elements as an ordered list using a range-based loop
Output:
1 apple
2 banana
3 grape
A variation of Jeffrey's answer, but without additional variable:
for (const auto& s : myVec)
{
std::cout << &s - &myVec[0] << " " << s << "\n";
}
This, of course, prints a "correct" 0-based index. Feel free to add 1 to it :)
Using boost ranges really simplifies things Live Demo
using namespace boost::adaptors;
std::vector<std::string> myVec = {"apple", "banana", "grape"};
for (const auto& element : myVec | indexed(1))
{
std::cout << element.index() << " " << element.value() << "\n";
}
Produces
1 apple
2 banana
3 grape
You are looking for
size_t position = 1;
for(const auto& s: myVec)
{
std::cout << position << " " << s << "\n";
position++;
}
as in
#include <iostream>
#include <string>
#include <vector>
using std::vector;
using std::string;
vector<string> myVec = {"apple", "banana", "grape"};
int main()
{
size_t position = 1;
for(const auto& s: myVec)
{
std::cout << position << " " << s << "\n";
position++;
}
}
With range-v3, you could write:
for (auto [i, val] : myVec | ranges::views::enumerate)
{
std::cout << i << ' ' << val << "\n";
}
Here's a demo.
This would be a good issue for the original for loop:
const size_t quantity = myVec.size();
for (unsigned int i = 0; i < quantity; ++i)
{
cout << (i + 1) << " " << myVec[i] << "\n";
}
Simple, effective. Don't knock the old stuff. :-)

Support BOOST_FUSION_ADAPT_STRUCT for objects with fixed arrays?

Assume I already have a struct that looks like this:
struct LETTER
{
double one;
char[12] two;
double three;
char[12] four;
};
And my inputs are comma separated, for example:
"32,CATSANDDOGS,42,WHAT"
"43,BATANDZEBRAS,23,PARROT"
I've been trying to adapt this example (Spirit Qi : rule for char [5]) to to roll through BOOST_FUSION_ADAPT_STRUCT but haven't had any luck. I tried using std::array as shown here (http://www.boost.org/doc/libs/1_64_0/libs/spirit/example/qi/boost_array.cpp) but I haven't been able to make it work in a struct. Is what I'm trying to do even possible? What am I doing wrong here? I would think this would be the most obvious use case.
Is what I'm trying to do even possible?
I'm going to assume you want to write idiomatic C++ code (which is obviously the target domain for Spirit Qi), so you can use std::string:
Live On Coliru
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
namespace qi = boost::spirit::qi;
struct Letter {
double one;
std::string two;
double three;
std::string four;
};
BOOST_FUSION_ADAPT_STRUCT(Letter, one, two, three, four)
template <typename Iterator> struct LETTERParser : qi::grammar<Iterator, Letter()> {
LETTERParser() : LETTERParser::base_type(start) {
using namespace qi;
_11c = repeat(11) [char_];
start = skip(space) [ "LETTER" >> double_ >> _11c >> double_ >> _11c ];
}
private:
qi::rule<Iterator, Letter()> start;
qi::rule<Iterator, std::string()> _11c;
};
int main() {
const std::string input("LETTER 42 12345678901 +Inf abcdefghijk ");
using It = std::string::const_iterator;
LETTERParser<It> parser;
Letter example;
It f = input.begin(), l = input.end();
if (phrase_parse(f, l, parser, qi::ascii::space, example)) {
std::cout << "parsed: " << boost::fusion::as_vector(example) << "\n";
std::cout << " example.one: " << example.one << "\n";
std::cout << " example.two: '" << example.two << "'\n";
std::cout << " example.three: " << example.three << "\n";
std::cout << " example.four: '" << example.four << "'\n";
} else {
std::cout << "couldn't parse '" << input << "'\n";
}
if (f != l)
std::cout << "Remaining unparsed input: '" << std::string(f,l) << "'\n";
}
Prints
parsed: (42 12345678901 inf abcdefghijk)
example.one: 42
example.two: '12345678901'
example.three: inf
example.four: 'abcdefghijk'

Result of parsing a boost::spirit grammar with sub-expressions

I'm trying to get into boost spirit 2, but the following code does not work as expected:
template<typename Iterator>
struct my_grammar : qi::grammar<Iterator, std::string()>
{
my_grammar() : my_grammar::base_type(time_literal)
{
using ascii::char_;
using ascii::digit;
time_literal = digit >> -digit >> char_(':') >> digit >> digit >> -(char_(':') >> digit >> digit);
}
qi::rule<Iterator, std::string()> time_literal;
};
void main()
{
my_grammar<std::string::iterator> g;
std::string input("01:02:03");
std::string::iterator begin = input.begin();
std::string::iterator iter = begin;
std::string::iterator end = input.end();
std::string result;
bool matched = phrase_parse(iter, end, g, ascii::space, result);
std::cout << (matched ? "matched "+std::string(begin, iter) : "no match") << std::endl;
if (iter != end)
std::cout << "remaining: " << std::string(iter, end) << std::endl;
else
std::cout << "result: " << result << std::endl;
std::cout << std::endl;
}
This prints:
matched: 01:02:03
result: 01:02:
But I expected to see:
matched: 01:02:03
result: 01:02:03
So where did those last two digits go and how can I get them back?