How to use spirit X3 parse into a class with constructor containing parameters? - c++14

I am a new man on using spirit x3, I read some document from official site or from other github repositories. But, I can not find how to parse into a class with parameters. I referred to the former question: Boost-Spirit (X3) parsing without default constructors
I wrote a sample to test it, I will present my codes in the following area. My pain is how to use x3::_attr, and how to pass parsed parameters to the class constructor?
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <vector>
struct MyPair {
MyPair(int x, int y) : mx(x), my(y) {};
int mx;
int my;
};
class MyDu {
public:
MyDu() {};
MyDu(int x, int y) : mx(x), my(y) {};
int mx;
int my;
};
int main()
{
namespace x3 = boost::spirit::x3;
using x3::int_;
std::vector<MyPair> pairs;
MyDu myDu;
char const *first = "11:22", *last = first + std::strlen(first);
//auto pair = x3::rule<struct pair_, std::vector<MyPair> >{}
// = (int_ >> ':' >> int_)
// [([&](auto& ctx) {
// auto& attr = x3::_attr(ctx);
// using boost::fusion::at_c;
// return x3::_val(ctx).emplace_back(at_c<0>(attr), at_c<1>(attr));
// })]
//;
auto pair = x3::rule<class MyDu_, MyDu >{}
= (int_ >> ':' >> int_)
[([&](auto& ctx) {
auto& attr = x3::_attr(ctx);
using boost::fusion::at_c;
//return x3::_val(ctx)(at_c<0>(attr), at_c<1>(attr));
ctx = MyDu(at_c<0>(attr), at_c<1>(attr));
return x3::_val(ctx);
})]
;
//bool parsed_some = parse(first, last, pair % ',', pairs);
bool parsed_some = parse(first, last, pair, myDu);
if (parsed_some) {
std::cout << "Parsed the following pairs" << std::endl;
//for (auto& p : pairs) {
// std::cout << p.mx << ":" << p.my << std::endl;
//}
std::cout<<myDu.mx<<","<<myDu.my<<std::endl;
}
system("pause");
}
Any one who can fix my error, and parse into a class in my code ? Thanks!

Perhaps you were missing the way to assign to the rule's value using _val:
Live On Coliru
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <vector>
struct MyDu {
MyDu(int x, int y) : mx(x), my(y){};
int mx;
int my;
};
int main() {
namespace x3 = boost::spirit::x3;
using x3::int_;
MyDu myDu{1,2};
std::string const s = "11:22";
auto assign = [](auto& ctx) {
using boost::fusion::at_c;
auto& attr = x3::_attr(ctx);
x3::_val(ctx) = MyDu(at_c<0>(attr), at_c<1>(attr));
};
auto pair = x3::rule<class MyDu_, MyDu>{} = (int_ >> ':' >> int_)[assign];
if (parse(begin(s), end(s), pair, myDu)) {
std::cout << "Parsed: " << myDu.mx << ", " << myDu.my << "\n";
}
}
Prints
Parsed: 11, 22

Oh, fantastic! Many thanks, sehe, you help me solve the problem bothering me for some while.
In fact I can not find document on spirit how to use attr, i only find a doc from "Ruben-Van-Boxem-Parsing-CSS-in-C-with-Boost-Spirit-X3",
_val :A reference to the attribute of the innermost rule invoking _where :the parser Iterator range to the input stream
_attr : A reference to the a˛ribute of the parser
_pass: A reference to a bool flag that can be used to force the parser to fail
could you share some info on these parameters. Many thanks again!

Related

Creating a C++ template function that allows multiple types of array containers

In modern C++ you can create arrays by three primary methods shown below.
// Traditional method
int array_one[] = {1, 2, 3, 4}
// Vector container
std::vector<int> array_two = {1, 2, 3, 4}
// array container
std::array<int, 4> array_three = {1, 2, 3, 4}
While each array method contains the same data, they are inherently different containers. I am writing a very simple Unit Test class with template functions to make it easier to pass multiple data types. I have an example shown below for the .hpp and .cpp calling file. The one method shown in the file takes a std::vector and compares it to another std::vector indice by indice to ensure that each value is within a certain tolerance of the other.
// main.cpp
#include <iostream>
#include <string>
#include <vector>
#include <array>
#include "unit_test.hpp"
int main(int argc, const char * argv[]) {
int array_one[] = {1, 2, 3, 4};
std::vector<int> array_two = {1, 2, 3, 4};
std::vector<float> array_four = {0.99, 1.99, 2.99, 3.99};
std::array<int, 4> array_three {1, 2, 3, 4};
std::string c ("Vector Test");
UnitTest q;
double unc = 0.1;
q.vectors_are_close(array_two, array_four, unc, c);
return 0;
}
and
#ifndef unit_test_hpp
#define unit_test_hpp
#endif /* unit_test_hpp */
#include <string>
#include <typeinfo>
#include <iostream>
#include <cmath>
class UnitTest
{
public:
template <class type1, class type2>
void vectors_are_close(const std::vector<type1> &i, const std::vector<type2> &j,
double k, std::string str);
private:
template <class type1, class type2>
void is_close(type1 &i, type2 &j, double k);
};
template <class type1, class type2>
void UnitTest::vectors_are_close(const std::vector<type1> &i, const std::vector<type2> &j,
double k, std::string str)
{
unsigned long remain;
remain = 50 - str.length();
if (i.size() != j.size()) {
std::cout << str + std::string(remain, '.') +
std::string("FAILED") << std::endl;
}
else {
try {
for (int a = 0; a < i.size(); a++){
is_close(i[a], j[a], k);
}
std::cout << str + std::string(remain, '.') +
std::string("PASSED") << std::endl;
} catch (const char* msg) {
std::cout << str + std::string(remain, '.') +
std::string("FAILED") << std::endl;
}
}
}
template <class type1, class type2>
void UnitTest::is_close(type1 &i, type2 &j, double k)
{
double percent_diff = abs((j - i) / ((i + j) / 2.0));
if (percent_diff > k) {
throw "Number not in Tolerance";
}
}
In this example the code compares two vectors; however, if I want to compare std::array containers I will have to crate a whole new function to do that, and if I want to compare two generic arrays, I will have to yet again create another function to do that. In addition, if I want to compare data in a std::array container to a std::vector container, again, I will have to create another function. I would like to create a single templated member function that I can pass any type of container to the function and have it compare it against any other type of container. In other words instead of;
void UnitTest::vectors_are_close(const std::vector<type1> &i, const std::vector<type2> & j);
I would like a simpler function such as;
void UnitTest::arrays_are_close(const type1, const type2);
where type1 and type2 do not just refer to the data in the container, but also the type of container as well. In this way I could pass a std::vector to type1 and std::array to type, or other combinations of the traditional way of creating arrays, array containers and vector containers. Is there any way to facilitate this behavior?
With a few changes to your implementation it is possible to do that:
template <class container1, class container2>
void UnitTest::vectors_are_close(const container1 &i, const container2 &j,
double k, std::string str)
{
unsigned long remain;
remain = 50 - str.length();
if (std::size(i) != std::size(j)) {
std::cout << str + std::string(remain, '.') +
std::string("FAILED") << std::endl;
}
else {
try {
for (int a = 0; a < std::size(i); a++){
is_close(i[a], j[a], k);
}
std::cout << str + std::string(remain, '.') +
std::string("PASSED") << std::endl;
} catch (const char* msg) {
std::cout << str + std::string(remain, '.') +
std::string("FAILED") << std::endl;
}
}
}
This function should work for std::vector, std::array and C-style arrays.

Line-based parser with empty lines and attributes, how to ignore attribute

I'm using boost spirit to parse a line-based format, where empty lines are allowed. For this, i'm using something similar to the following grammar:
struct parser_type : public qi::grammar<std::string::iterator, qi::ascii::blank_type, std::vector<int>()>
{
typedef std::string::iterator Iterator;
parser_type() : parser_type::base_type(main)
{
element = qi::int_;
line %= element | qi::eps;
main %= +(line >> qi::eol);
}
qi::rule<Iterator, int()> element;
qi::rule<Iterator, qi::ascii::blank_type, int()> line;
qi::rule<Iterator, qi::ascii::blank_type, std::vector<int>()> main;
} parser;
This works fine, since the qi::eps together with the qi::eol matches empty lines. Nice (though i am open to other, perhaps better approaches to parse line-based formats with empty lines). However, the attribute of the line parser is an int, which is obviously not present on empty lines. Therefore, for an input of
1
4
the parser creates a vector with the content { 1, 0, 0, 4 }.
I want the line totally ignored, that is, i don't want any dummy object to be constructed to match the attribute of the line. Can this be done? Is there a better way to parse lines?
Here is a complete minimum example (the program needs a input file called "input", you can use my example above):
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
struct parser_type : public qi::grammar<std::string::iterator, qi::ascii::blank_type, std::vector<int>()>
{
typedef std::string::iterator Iterator;
parser_type() : parser_type::base_type(main)
{
element = qi::int_;
line = element | qi::eps;
main %= +(line >> qi::eol);
}
qi::rule<Iterator, int()> element;
qi::rule<Iterator, qi::ascii::blank_type, int()> line;
qi::rule<Iterator, qi::ascii::blank_type, std::vector<int>()> main;
} parser;
int main()
{
std::ifstream file("input");
std::stringstream buffer;
buffer << file.rdbuf();
std::string str = buffer.str();
auto iter = str.begin();
std::vector<int> lines;
bool r = qi::phrase_parse(iter, str.end(), parser, qi::ascii::blank, lines);
if (r && iter == str.end())
{
std::cout << "parse succeeded\n";
for(auto e : lines)
{
std::cout << e << '\n';
}
}
else
{
std::cout << "parse failed. Remaining unparsed: " << std::string(iter, str.end()) << '\n';
}
}
This rule:
line = element | eps;
causes you to loose the information you need. By accepting no-match (eps), you force it to just return the value-initialized attribute you declared (int in the rul signature).
So, drop that, and then I usually write this kind of a repeat using the list-operator (%):
line = element;
main = -line % qi::eol;
This works:
Live On Coliru
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
struct parser_type : public qi::grammar<std::string::iterator, qi::ascii::blank_type, std::vector<int>()>
{
typedef std::string::iterator Iterator;
parser_type() : parser_type::base_type(main)
{
element = qi::int_;
line = element;
main = -line % qi::eol;
}
qi::rule<Iterator, int()> element;
qi::rule<Iterator, qi::ascii::blank_type, int()> line;
qi::rule<Iterator, qi::ascii::blank_type, std::vector<int>()> main;
} parser;
int main()
{
std::ifstream file("input");
std::stringstream buffer;
buffer << file.rdbuf();
std::string str = buffer.str();
auto iter = str.begin();
std::vector<int> lines;
bool r = qi::phrase_parse(iter, str.end(), parser, qi::ascii::blank, lines);
if (r && iter == str.end())
{
std::cout << "parse succeeded\n";
for(auto e : lines)
{
std::cout << e << '\n';
}
}
else
{
std::cout << "parse failed. Remaining unparsed: " << std::string(iter, str.end()) << '\n';
}
}
Prints
parse succeeded
1
4

Boost Mem_fn and accessing member function of derived class

I made a simple example to test boost bind's interaction with derived classes.
I created two subclasses with different getarea functions. I expected
g1 = boost::bind(boost::mem_fn(&Shape::getarea), Rec)
to print the area of Rectangle(10,20) but instead it printed '1'. I get the same when I instead write Rectangle::getarea. It prints the same even when I input other functions eg. member of Rectangle
double sum(double h,double w){return h+w; }
and use
g1 = boost::bind(boost::mem_fn(&Rectangle::sum), Rec,2,3)
Question 1: Why does it return '1'?Is that a default response for error?
My second problem is to do the same of printing g2 but now Rec is replaced by **iter, i.e. an object of some derived class type from a list of objects. Since getarea is a virtual fcn, once I get the above working it should be fine to just write:
g2= boost::bind(boost::mem_fn(& Shape::getarea , &(**iter));
Question 2: However, I was wondering if there is a way to return the classtype of **iter eg. classof(**iter) and then put it in g2 i.e.
g2= boost::bind(boost::mem_fn(& classof(**iter)::getarea , &(**iter));
When I ran g2 by writing Shape::getarea, I got '1' again for all iter.
#include <memory>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <boost/bind.hpp>
using namespace std;
class Shape {
public:
Shape(double h, double w) :height(h), width(w) {};
virtual double getarea() = 0;
double height;
double width; };
class Rectangle: public Shape {
public:
Rectangle(double h, double w): Shape(h,w) {};
double getarea() override { return height*width; } };
class Triangle : public Shape {
public:
Triangle(double h, double w) :Shape(h,w) {};
double getarea() { return height*width*0.5; }};
int main() {
//create objects
Rectangle Rec(10, 20);
Triangle Tri(2, 3);
//create boost bind function
boost::function<double(double, double)> g1;
g1 = boost::bind(boost::mem_fn(&Shape::getarea), Rec);
//print area and g
cout << Rec.getarea()<<" should be equal to " << g1<< '\n';
//create list
vector<shared_ptr<Shape>> Plist;
Plist.push_back(make_shared<Rectangle>(Rec));
Plist.push_back(make_shared<Triangle>(Tri));
//print each element from the vector list
for (auto iter = Plist.begin(); iter != Plist.end(); iter ++ ) {
boost::function<double(double, double)> g2;
g2= boost::bind(boost::mem_fn(& .... , &(**iter));
//where in dots we need Classtype_of_**iter::getarea
cout << (**iter).getarea()<<"should be equal to " << g2<< '\n';
}
}
You... forget to invoke the functions...
for (auto iter = Plist.begin(); iter != Plist.end(); iter++) {
boost::function<double()> g2;
g2 = boost::bind(&Shape::getarea, iter->get());
cout << (*iter)->getarea() << " should be equal to " << g2() << '\n';
}
What you saw what the implicit conversion to bool (http://www.boost.org/doc/libs/1_60_0/doc/html/boost/function.html#idm45507164686720-bb)
Note also I fixed the signature of g1 and g2: Live On Coliru.
Some further improvements (remove the need for the g2 in the loop?):
auto getarea = boost::mem_fn(&Shape::getarea);
for (auto iter = Plist.begin(); iter != Plist.end(); iter++) {
cout << (*iter)->getarea() << " should be equal to " << getarea(**iter) << '\n';
}
Or, indeed in c++11:
for (auto& s : Plist)
cout << s->getarea() << " should be equal to " << getarea(*s) << '\n';
By this time, you'd wonder why you have this accessor when you can just use the member.

boost::shared_ptr<std::vector<something>> usage of operator[]

I have a struct that looks like this.
typedef struct superCellBoxStruct {
float_tt cmx,cmy,cmz; /* fractional center of mass coordinates */
float_tt ax,by,cz;
boost::shared_ptr<std::vector<atom>> atoms; /* contains all the atoms within the super cell */
} superCellBox;
now when I want to access atoms[i] I get
error: invalid use of ‘boost::detail::sp_array_access >::type {aka void}’
What is the proper way of passing around a shared vector in my application, or what is the correct way to access its operator[]?
Pick one:
(*atoms)[i]
atoms->operator[](i);
I usually go with the first, but they are all equivalent.
As a side note, in my experience a shared_ptr<vector> like that is usually a symptom of a bad design, maybe you want to put the entire superCellBox in a shared_ptr?
Also, this is not C, use struct name {}; instead typedef struct tagName {} name;
Prefer unique_ptr<T[]> if you can, because you get operator[] for free (§ 20.7.1.3.3):
Quick demo:
Live On Coliru
#include <memory>
#include <iostream>
int main() {
std::unique_ptr<int[]> p(new int[3] { 1,2,3 });
std::cout << "Before: " << p[0] << ", " << p[1] << ", " << p[2] << ";\n";
p[1] = 42;
std::cout << "After: " << p[0] << ", " << p[1] << ", " << p[2] << ";\n";
}
Prints:
Before: 1, 2, 3;
After: 1, 42, 3;
UPDATE
In response to the comment, just make a small wrapper:
Live On Coliru
#include <memory>
template <typename RAContainer> struct shared_randomaccess_container
{
template <typename... A> shared_randomaccess_container(A&&... args)
: _ptr(new RAContainer{ std::forward<A>(args)... })
{ }
template <typename T> shared_randomaccess_container(std::initializer_list<T> init)
: _ptr(std::make_shared<RAContainer>(init))
{ }
auto begin() const -> typename RAContainer::const_iterator { return _ptr->begin(); }
auto end () const -> typename RAContainer::const_iterator { return _ptr->end (); }
auto begin() -> typename RAContainer::iterator { return _ptr->begin(); }
auto end () -> typename RAContainer::iterator { return _ptr->end (); }
template <typename Idx>
typename RAContainer::value_type const& operator[](Idx i) const { return (*_ptr)[i]; }
template <typename Idx>
typename RAContainer::value_type& operator[](Idx i) { return (*_ptr)[i]; }
template <typename Idx>
typename RAContainer::value_type const& at(Idx i) const { return _ptr->at(i); }
template <typename Idx>
typename RAContainer::value_type& at(Idx i) { return _ptr->at(i); }
protected:
using Ptr = std::shared_ptr<RAContainer>;
Ptr _ptr;
};
////////////////////////////////////////////////////
// demo intances
#include <vector>
template <typename... Ts> using shared_vector = shared_randomaccess_container<std::vector<Ts...> >;
You can use it like:
shared_vector<int> sv {1,2,3};
std::cout << "Before: ";
for (auto i : sv) std::cout << i << " ";
sv[1] = 42;
std::cout << "\nAfter: ";
for (auto i : sv) std::cout << i << " ";
Prints:
Before: 1 2 3
After: 1 42 3
Bonus
Let's also support aggregate initializing containers with the same technique
Live On Coliru
Output:
void test() [with With = std::vector<int>]
Before: 1 2 3
After: 1 42 3
void test() [with With = std::array<int, 3ul>]
Before: 1 2 3
After: 1 42 3
void test() [with With = shared_randomaccess_container<std::vector<int>, false>]
Before: 1 2 3
After: 1 42 3
void test() [with With = shared_randomaccess_container<std::array<int, 3ul>, true>]
Before: 1 2 3
After: 1 42 3

Is it possible to have several edge weight property maps for one graph?

How would I create a graph, such that the property map (weight of edges) is different in each property map? Is it possible to create such a property map?
Like an array of property maps?
I have not seen anyone on the Internet using it, could I have an example?
Graph g(10); // graph with 10 nodes
cin>>a>>b>>weight1>>weight2>>weight3>>weight4;
and put each weight in a property map.
You can compose a property map in various ways. The simplest approach would seem something like:
Using C++11 lambdas with function_property_map
Live On Coliru
#include <boost/property_map/function_property_map.hpp>
#include <iostream>
struct weights_t {
float weight1, weight2, weight3, weight4;
};
using namespace boost;
int main() {
std::vector<weights_t> weight_data { // index is vertex id
{ 1,2,3,4 },
{ 5,6,7,8 },
{ 9,10,11,12 },
{ 13,14,15,16 },
};
auto wmap1 = make_function_property_map<unsigned, float>([&weight_data](unsigned vertex_id) { return weight_data.at(vertex_id).weight1; });
auto wmap2 = make_function_property_map<unsigned, float>([&weight_data](unsigned vertex_id) { return weight_data.at(vertex_id).weight2; });
auto wmap3 = make_function_property_map<unsigned, float>([&weight_data](unsigned vertex_id) { return weight_data.at(vertex_id).weight3; });
auto wmap4 = make_function_property_map<unsigned, float>([&weight_data](unsigned vertex_id) { return weight_data.at(vertex_id).weight4; });
for (unsigned vertex = 0; vertex < weight_data.size(); ++vertex)
std::cout << wmap1[vertex] << "\t" << wmap2[vertex] << "\t" << wmap3[vertex] << "\t"<< wmap4[vertex] << "\n";
}
Using C++03 with transform_value_property_map
This is mainly much more verbose:
Live On Coliru
#include <boost/property_map/transform_value_property_map.hpp>
#include <iostream>
struct weights_t {
float weight1, weight2, weight3, weight4;
weights_t(float w1, float w2, float w3, float w4)
: weight1(w1), weight2(w2), weight3(w3), weight4(w4)
{ }
template <int which> struct access {
typedef float result_type;
float operator()(weights_t const& w) const {
BOOST_STATIC_ASSERT(which >= 1 && which <= 4);
switch (which) {
case 1: return w.weight1;
case 2: return w.weight2;
case 3: return w.weight3;
case 4: return w.weight4;
}
}
};
};
using namespace boost;
int main() {
std::vector<weights_t> weight_data; // index is vertex id
weight_data.push_back(weights_t(1,2,3,4));
weight_data.push_back(weights_t(5,6,7,8));
weight_data.push_back(weights_t(9,10,11,12));
weight_data.push_back(weights_t(13,14,15,16));
boost::transform_value_property_map<weights_t::access<1>, weights_t*, float> wmap1 = make_transform_value_property_map(weights_t::access<1>(), &weight_data[0]);
boost::transform_value_property_map<weights_t::access<2>, weights_t*, float> wmap2 = make_transform_value_property_map(weights_t::access<2>(), &weight_data[0]);
boost::transform_value_property_map<weights_t::access<3>, weights_t*, float> wmap3 = make_transform_value_property_map(weights_t::access<3>(), &weight_data[0]);
boost::transform_value_property_map<weights_t::access<4>, weights_t*, float> wmap4 = make_transform_value_property_map(weights_t::access<4>(), &weight_data[0]);
for (unsigned vertex = 0; vertex < weight_data.size(); ++vertex)
std::cout << wmap1[vertex] << "\t" << wmap2[vertex] << "\t" << wmap3[vertex] << "\t"<< wmap4[vertex] << "\n";
}
Output
Both samples output
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

Resources