MultiIndex Container equal_range .. get output in reverse order - c++11

I have a boost::multiIndex container.
say :
typedef boost::multi_index<....
> OrderSet;
OrderSet orderSet_;
int main()
{
const auto it = orderSet_.get<0>().equal_range(boost::make_tuple(/* Some Values*/))
if(it.first != it.second)
while(it.second != it.first) { /* Somethings to do */;
--it.second;
}
}
the program crash with offset_ptr error.
for the program it has value on increasing order. so I want highest value first then so-on..
Is any a over-loaded function of boost::equal_range(..., [&](const auto a, const auto b)-> decltype bool{return a > b;});
Something like this. it's like reverse_iterator of equal_range()
UPDATE: multi-Index container is accessed by one producer and many consumer. while consumer is finding equal_range, producer add or delete elements and consumer throw the error... this is only happens when i am reverse iterating...

You're leaving out the essential part: the chosen index type.
Different index types afford different interfaces.
Then again, equal_range implies a ordered or unordered map. And because reversing the order on an unordered map is non-sense, I'm going to assume an ordered map.
Next, with make_tuple there I'm guessing composite keys.
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>
struct X {
int i;
std::string name;
};
namespace bmi = boost::multi_index;
typedef boost::multi_index_container<X,
bmi::indexed_by<
bmi::ordered_non_unique<
bmi::tag<struct by_i>,
bmi::composite_key<X,
bmi::member<X, int, &X::i>,
bmi::member<X, std::string, &X::name>
>
>
>
> OrderSet;
Then here's the native order:
#include <iostream>
int main()
{
std::cout << std::unitbuf;
OrderSet orderSet_ {
{ 1, "one" }, { 1, "two" }, { 2, "three" }, { 3, "four" }, { 2, "five" }
};
auto const range = orderSet_/*.get<0>()*/.equal_range(boost::make_tuple(2));
for (auto& el : boost::make_iterator_range(range))
std::cout << el.i << "," << el.name << "; ";
// ...
And I'd use Boost Range to get the reverse order:
#include <boost/range/adaptor/reversed.hpp>
// ...
// traverse these in reverse
std::cout << "\nNow in reverse:\n";
for (auto& el : range | boost::adaptors::reversed)
std::cout << el.i << "," << el.name << "; ";
}
BONUS TAKE
Of course you can write the same manually:
std::cout << "\nAlso in reverse:\n";
auto it = orderSet_.get<0>().equal_range(boost::make_tuple(2));
while(it.second != it.first) {
--it.second;
auto& el = *it.second;
std::cout << el.i << "," << el.name << "; ";
}
Live Demo
Live On Coliru
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/range/adaptor/reversed.hpp>
struct X {
int i; // = [] { static int gen = 0; return ++gen; }();
std::string name;
};
namespace bmi = boost::multi_index;
typedef boost::multi_index_container<X,
bmi::indexed_by<
bmi::ordered_non_unique<
bmi::tag<struct by_i>,
bmi::composite_key<X,
bmi::member<X, int, &X::i>,
bmi::member<X, std::string, &X::name>
>
>
>
> OrderSet;
#include <iostream>
int main()
{
std::cout << std::unitbuf;
OrderSet orderSet_ {
{ 1, "one" }, { 1, "two" }, { 2, "three" }, { 3, "four" }, { 2, "five" }
};
{
auto const range = orderSet_/*.get<0>()*/.equal_range(boost::make_tuple(2));
for (auto& el : boost::make_iterator_range(range))
std::cout << el.i << "," << el.name << "; ";
// traverse these in reverse
std::cout << "\nNow in reverse:\n";
for (auto& el : range | boost::adaptors::reversed)
std::cout << el.i << "," << el.name << "; ";
}
{
std::cout << "\nAlso in reverse:\n";
auto it = orderSet_.get<0>().equal_range(boost::make_tuple(2));
while(it.second != it.first) {
auto& el = *(--it.second);
std::cout << el.i << "," << el.name << "; ";
}
}
}
Prints
2,five; 2,three;
Now in reverse:
2,three; 2,five;
Also in reverse:
2,three; 2,five;

Even further out on a limb, since you said offset_ptr you might be using Boost Interprocess allocators.
Here's a demo that shows how you'd do the above correctly in a memory mapped/shared memory area:
Live On Coliru
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
namespace bmi = boost::multi_index;
namespace bip = boost::interprocess;
//namespace Shared { struct X; }
//namespace std { template<typename Alloc> struct uses_allocator<Shared::X, Alloc> : std::true_type {}; }
namespace Shared {
using Segment = bip::managed_mapped_file;
template <typename T> using Alloc = bip::allocator<T, Segment::segment_manager>;
using String = boost::interprocess::basic_string<char, std::char_traits<char>, Alloc<char> >;
struct X {
using allocator_type = Alloc<char>;
template <typename Alloc>
X(int i, std::string const& s, Alloc alloc) : i(i), name(s.begin(), s.end(), alloc) {}
int i;
String name;
};
typedef boost::multi_index_container<X,
bmi::indexed_by<
bmi::ordered_non_unique<
bmi::tag<struct by_i>,
bmi::composite_key<X,
bmi::member<X, int, &X::i>,
bmi::member<X, String, &X::name>
>
>
>,
Alloc<X>
> OrderSet;
}
#include <iostream>
int main()
{
std::cout << std::unitbuf;
Shared::Segment mmf(bip::open_or_create, "test.data", 10u << 10);
auto& orderSet_ = *mmf.find_or_construct<Shared::OrderSet>("set")(mmf.get_segment_manager());
if (orderSet_.empty()) {
// can't get multi-index to use the scoped allocator "magically" :(
orderSet_.emplace(1, "one", orderSet_.get_allocator());
orderSet_.emplace(1, "one", orderSet_.get_allocator());
orderSet_.emplace(1, "two", orderSet_.get_allocator());
orderSet_.emplace(2, "three", orderSet_.get_allocator());
orderSet_.emplace(3, "four", orderSet_.get_allocator());
orderSet_.emplace(2, "five", orderSet_.get_allocator());
}
{
auto const range = orderSet_/*.get<0>()*/.equal_range(boost::make_tuple(2));
for (auto& el : boost::make_iterator_range(range))
std::cout << el.i << "," << el.name << "; ";
// traverse these in reverse
std::cout << "\nNow in reverse:\n";
for (auto& el : range | boost::adaptors::reversed)
std::cout << el.i << "," << el.name << "; ";
}
{
std::cout << "\nAlso in reverse:\n";
auto it = orderSet_.get<0>().equal_range(boost::make_tuple(2));
while(it.second != it.first) {
auto& el = *(--it.second);
std::cout << el.i << "," << el.name << "; ";
}
}
std::cout << "\nBye\n";
}
Prints
2,five; 2,three;
Now in reverse:
2,three; 2,five;
Also in reverse:
2,three; 2,five;
Bye
Twice (proving all data was correctly in the shared memory segment).
BONUS TAKE
Apparently, Boost Multi-Index doesn't quite play completely well with the scoped allocator, because it would be more elegant with that:
Live On Coliru
#include <set>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/container/set.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/iterator_range.hpp>
namespace bip = boost::interprocess;
namespace Shared {
using Segment = bip::managed_mapped_file;
template <typename T> using Alloc = bip::allocator<T, Segment::segment_manager>;
using String = boost::interprocess::basic_string<char, std::char_traits<char>, Alloc<char> >;
struct X {
using allocator_type = Alloc<char>;
template <typename Alloc>
X(int i, std::string const& s, Alloc alloc) : i(i), name(s.begin(), s.end(), alloc) {}
bool operator<(X const& other) const { return i < other.i; }
bool operator<(int other) const { return i < other; }
int i;
String name;
};
template <typename T>
using Multiset = boost::container::multiset<
T,
std::less<T>,
boost::container::scoped_allocator_adaptor<Alloc<X> >
>;
using OrderSet = Multiset<X>;
}
#include <iostream>
int main()
{
std::cout << std::unitbuf;
Shared::Segment mmf(bip::open_or_create, "test.data", 10u << 10);
auto& orderSet_ = *mmf.find_or_construct<Shared::OrderSet>("set")(mmf.get_segment_manager());
if (orderSet_.empty()) {
// scoped-allocator automatically propagates to the string
orderSet_.emplace(1, "one");
orderSet_.emplace(1, "one");
orderSet_.emplace(1, "two");
orderSet_.emplace(2, "three");
orderSet_.emplace(3, "four");
orderSet_.emplace(2, "five");
}
{
auto const range = orderSet_.equal_range({2, "", orderSet_.get_allocator()});
for (auto& el : boost::make_iterator_range(range))
std::cout << el.i << "," << el.name << "; ";
// traverse these in reverse
std::cout << "\nNow in reverse:\n";
for (auto& el : range | boost::adaptors::reversed)
std::cout << el.i << "," << el.name << "; ";
}
{
std::cout << "\nAlso in reverse:\n";
auto it = orderSet_.equal_range({2, "", orderSet_.get_allocator()});
while(it.second != it.first) {
auto& el = *(--it.second);
std::cout << el.i << "," << el.name << "; ";
}
}
std::cout << "\nBye\n";
}

Related

How to have a tuple as both the key and value of an unordered map using a hash function or boost?

I am trying to use a tuple<string, string, string> that will be placed as both the key and value of the unordered map as such:
unordered_map < tuple<string, string, string>, tuple<string, string, string>, hash_tuple> songInfo;
I understand I'm supposed to define a hash function because C++ does not have a hasher for std::tuple. However, I'm struggling to implement this through the explanations I've been reading on the other stack overflow posts. This is what my code has so far:
#include <iostream>
#include <unordered_map>
#include <vector>
#include <fstream>
#include <tuple>
using namespace std;
typedef std::tuple<string, string, string> t;
struct hash_tuple : public unary_function <t, size_t> {
size_t operator()(const t& x) const
{
return get<0>(x) ^ get<1>(x) ^ get<2>(x);
}
};
typedef unordered_map <t, t, hash_tuple> songInfo;
int main() {
string filePath = "../data/songInfo.txt";
fstream songFile;
songFile.open("songInfo.txt");
string line;
string name, artist, date, duration, genre, mood;
vector <string> store;
while (getline(songFile, line, ',')) {
store.push_back(line);
tuple <string, string, string> iD(store[0], store[1], store[2]);
tuple <string, string, string> info(store[3], store[4], store[5]);
songInfo[iD] = info;
}
for (auto it = songInfo.begin(); it != songInfo.end(); it++)
cout << "[" << get<0>(it->first) << ", " << get<1>(it->first) << ", " <<
get<2>(it->first) << "] ==>" << get<0>(it->second) << ", " << get<1>(it-
>second) << ", " << get<2>(it->second) << endl;
}
Okay "fixing" the problem is easy: you wanted to XOR-combine hashes, not strings:
struct hash_tuple {
size_t operator()(const t& x) const {
std::hash<std::string> h{};
return h(get<0>(x)) ^ h(get<1>(x)) ^ h(get<2>(x));
}
};
Note that using namespace std is frowned upon, using declarations trump typedefs and unary_function<> is deprecated. Next up there were some syntax errors.
Combining fixes for these: Live On Coliru
#include <fstream>
#include <iostream>
#include <tuple>
#include <unordered_map>
#include <vector>
using t = std::tuple<std::string, std::string, std::string>;
struct hash_tuple {
size_t operator()(const t& x) const {
std::hash<std::string> h{};
return h(get<0>(x)) ^ h(get<1>(x)) ^ h(get<2>(x));
}
};
using SongInfo = std::unordered_map<t, t, hash_tuple>;
int main() {
std::string filePath = "songInfo.txt";
std::fstream songFile;
songFile.open("songInfo.txt");
std::string line;
std::string name, artist, date, duration, genre, mood;
std::vector<std::string> store;
SongInfo songInfo;
while (getline(songFile, line, ',')) {
store.push_back(line);
std::tuple<std::string, std::string, std::string> iD(store[0], store[1], store[2]);
std::tuple<std::string, std::string, std::string> info(store[3], store[4], store[5]);
songInfo[iD] = info;
}
for (auto it = songInfo.begin(); it != songInfo.end(); it++)
std::cout << "[" << get<0>(it->first) << ", " << get<1>(it->first)
<< ", " << get<2>(it->first) << "] ==>" << get<0>(it->second)
<< ", " << get<1>(it->second) << ", " << get<2>(it->second)
<< std::endl;
}

Begin was not declared in the scope for Generic Array Template Class

#include <iostream>
#include <string>
template <typename T, int N>
class Array {
int size{N};
T values{N};
friend std::ostream &operator<<(std::ostream &os, const Array<T, N> &arr) {
os << "[";
for (const auto &val: arr.values)
os << val << " ";
os << "]" << std::endl;
return os;
}
public:
Array() = default;
Array(T value_added) {
for(auto &value: values)
value = value_added;
}
void fill(T data_value) {
for (auto &value: values)
value = data_value;
}
int get_size() const {
return size;
}
T &operator[](int index) {
return values[index];
}
};
int main () {
Array<int,2> nums;
std::cout << "The size of nums is: " << nums.get_size() << std::endl;
std::cout << nums << std::endl;
}
I am trying to create a generic array template class but I get the error Begin was not declared in the scope and it says it is due to the for (const auto &val: arr.values) loop.

How to make recursive Spirit X3 parser with a separate visitor class

A parser application where I’m working on calls for recursive rules. Besides looking into the Recursive AST tutorial examples of Boost Spirit X3 which can be found here:
https://www.boost.org/doc/libs/develop/libs/spirit/doc/x3/html/index.html, I was looking for a solution with a std::variant of some types as well as a std::vector of that same
variant type.
In the StackOverflow post titled: Recursive rule in Spirit.X3, I found the code from the answer from sehe a decent starting point for my parser.
I have repeated the code here but I have limited the input strings to be tested. Because the full list from the original is not relevant for this question here.
//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <string>
#include <vector>
#include <variant>
struct value: std::variant<int,float,std::vector<value>>
{
using base_type = std::variant<int,float,std::vector<value>>;
using base_type::variant;
friend std::ostream& operator<<(std::ostream& os, base_type const& v) {
struct {
std::ostream& operator()(float const& f) const { return _os << "float:" << f; }
std::ostream& operator()(int const& i) const { return _os << "int:" << i; }
std::ostream& operator()(std::vector<value> const& v) const {
_os << "tuple: [";
for (auto& el : v) _os << el << ",";
return _os << ']';
}
std::ostream& _os;
} vis { os };
return std::visit(vis, v);
}
};
namespace parser {
namespace x3 = boost::spirit::x3;
x3::rule<struct value_class, value> const value_ = "value";
x3::rule<struct o_tuple_class, std::vector<value> > o_tuple_ = "tuple";
x3::real_parser<float, x3::strict_real_policies<float> > float_;
const auto o_tuple__def = "tuple" >> x3::lit(':') >> ("[" >> value_ % "," >> "]");
const auto value__def
= "float" >> (':' >> float_)
| "int" >> (':' >> x3::int_)
| o_tuple_
;
BOOST_SPIRIT_DEFINE(value_, o_tuple_)
const auto entry_point = x3::skip(x3::space) [ value_ ];
}
int main()
{
for (std::string const str : {
"float: 3.14",
"int: 3",
"tuple: [float: 3.14,int: 3]",
"tuple: [float: 3.14,int: 3,tuple: [float: 4.14,int: 4]]"
}) {
std::cout << "============ '" << str << "'\n";
//using boost::spirit::x3::parse;
auto first = str.begin(), last = str.end();
value val;
if (parse(first, last, parser::entry_point, val))
std::cout << "Parsed '" << val << "'\n";
else
std::cout << "Parse failed\n";
if (first != last)
std::cout << "Remaining input: '" << std::string(first, last) << "'\n";
}
}
However I would like to use a traditional visitor class rather than making ostream a friend in the variant class. You know just a struct/class with a bunch of function objects for each type you encounter in the variant and a "for loop" for the vector that calls std::visit for each
element.
My goal for the traditional visitor class is to be able to maintain a state machine during printing.
My own attempts to write this visitor class did fail because I ran into an issue with my GCC 8.1 compiler. With GCC during compilation std::variant happens to be std::variant_size somehow and I got the following error:
error: incomplete type 'std::variant_size' used in nested name specifier
More about this here:
Using std::visit on a class inheriting from std::variant - libstdc++ vs libc++
Is it possible giving this constraint on GCC to write a visitor class for the code example I included, so that the ostream stuff can be removed?
Is it possible giving this constraint on GCC to write a visitor class for the code example I included, so that the ostream stuff can be removed?
Sure. Basically, I see three approaches:
1. Add the template machinery
You can specialize the implementation details accidentally required by GCC:
struct value: std::variant<int,float,std::vector<value>> {
using base_type = std::variant<int,float,std::vector<value>>;
using base_type::variant;
};
namespace std {
template <> struct variant_size<value> :
std::variant_size<value::base_type> {};
template <size_t I> struct variant_alternative<I, value> :
std::variant_alternative<I, value::base_type> {};
}
See it live on Wandbox (GCC 8.1)
2. Don't (again live)
Extending the std namespace is fraught (though I think it's legal for
user-defined types). So, you can employ my favorite pattern and hide th
estd::visit dispatch in the function object itself:
template <typename... El>
void operator()(std::variant<El...> const& v) const { std::visit(*this, v); }
Now you can simply call the functor and it will automatically dispatch
on your own variant-derived type because that operator() overload does
NOT have the problems that GCC stdlib has:
if (parse(first, last, parser::entry_point, val))
{
display_visitor display { std::cout };
std::cout << "Parsed '";
display(val);
std::cout << "'\n";
}
3. Make things explicit
I like this the least, but it does have merit: there's no magic and no
tricks:
struct value: std::variant<int,float,std::vector<value>> {
using base_type = std::variant<int,float,std::vector<value>>;
using base_type::variant;
base_type const& as_variant() const { return *this; }
base_type& as_variant() { return *this; }
};
struct display_visitor {
void operator()(value const& v) const { std::visit(*this, v.as_variant()); }
// ...
Again, live
SUMMARY
After thinking a bit more, I'd recommend the last approach, due to the relative simplicity. Clever is often a code-smell :)
Full listing for future visitors:
//#define BOOST_SPIRIT_X3_DEBUG
#include <iostream>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <string>
#include <vector>
#include <variant>
struct value: std::variant<int,float,std::vector<value>> {
using base_type = std::variant<int,float,std::vector<value>>;
using base_type::variant;
base_type const& as_variant() const { return *this; }
base_type& as_variant() { return *this; }
};
struct display_visitor {
std::ostream& _os;
void operator()(value const& v) const { std::visit(*this, v.as_variant()); }
void operator()(float const& f) const { _os << "float:" << f; }
void operator()(int const& i) const { _os << "int:" << i; }
void operator()(std::vector<value> const& v) const {
_os << "tuple: [";
for (auto& el : v) {
operator()(el);
_os << ",";
}
_os << ']';
}
};
namespace parser {
namespace x3 = boost::spirit::x3;
x3::rule<struct value_class, value> const value_ = "value";
x3::rule<struct o_tuple_class, std::vector<value> > o_tuple_ = "tuple";
x3::real_parser<float, x3::strict_real_policies<float> > float_;
const auto o_tuple__def = "tuple" >> x3::lit(':') >> ("[" >> value_ % "," >> "]");
const auto value__def
= "float" >> (':' >> float_)
| "int" >> (':' >> x3::int_)
| o_tuple_
;
BOOST_SPIRIT_DEFINE(value_, o_tuple_)
const auto entry_point = x3::skip(x3::space) [ value_ ];
}
int main()
{
for (std::string const str : {
"float: 3.14",
"int: 3",
"tuple: [float: 3.14,int: 3]",
"tuple: [float: 3.14,int: 3,tuple: [float: 4.14,int: 4]]"
}) {
std::cout << "============ '" << str << "'\n";
//using boost::spirit::x3::parse;
auto first = str.begin(), last = str.end();
value val;
if (parse(first, last, parser::entry_point, val))
{
display_visitor display { std::cout };
std::cout << "Parsed '";
display(val);
std::cout << "'\n";
}
else
std::cout << "Parse failed\n";
if (first != last)
std::cout << "Remaining input: '" << std::string(first, last) << "'\n";
}
}

Spirit.X3 : using push_back_container traits with list parser

I have a class having public ctor and some add() method:
class object
{
object() {}
template <typename>
void add(T&& val) { // some adding here}
}
The main question I'm faced is how can I adopt spirit.x3 list parser to use object::add() method instead of std::vector<>::push_back ?
I was easily able to achieve what I need with simple
x3::int_ % ','
parser (live demo) using the following code :
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <vector>
namespace x3 = boost::spirit::x3;
namespace parse_to_object
{
struct object
{
using value_type = int;
object() { std::cout << "object::object() - invoked" << std::endl; }
void add(value_type val) { _data.push_back(val); }
std::vector<value_type> _data;
};
const x3::rule<struct Test, object> r_ints("r_ints");
const auto r_ints_def = x3::int_ % ',';
BOOST_SPIRIT_DEFINE(r_ints);
}
namespace boost { namespace spirit { namespace x3 { namespace traits {
template<>
struct push_back_container<parse_to_object::object>
{
template<typename T>
static bool call(parse_to_object::object& obj, T&& val)
{
obj.add(std::move(val));
return true;
}
};
}}}}
int main()
{
const std::string text("1,2,3,4");
auto begin = std::begin(text);
const auto end = std::end(text);
parse_to_object::object result;
const auto ok = x3::phrase_parse(begin, end, parse_to_object::r_ints, x3::space, result);
std::cout << "ok = " << std::boolalpha << (ok && begin == end) << std::endl;
std::copy(result._data.begin(), result._data.end(), std::ostream_iterator<int>(std::cout, " "));
return 0;
}
But unfortunately, when I tried more compilcated example like
'{' >> x3::int_ >> ':' >> x3::int_ >> '}') % ','
I'm getting the compilation error (live demo) :
/opt/wandbox/boost-1.67.0/clang-head/include/boost/spirit/home/x3/support/traits/container_traits.hpp:102:45: error: no type named 'iterator' in 'parse_to_object::object'
: mpl::identity {};
Could somebody assist with spirit.x3 traits and give some example how to abopt custom class to be used instead of std::vector<> for list parser ?
In the end it's down to a missing include:
#include <boost/fusion/adapted/std_pair.hpp>
std::pair isn't adapted by default.
Side note: std::move should be std::forward<T> with "universal references" (or perfect forwarding)
Live On Coliru
#define BOOST_SPIRIT_X3_DEBUG
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <vector>
namespace x3 = boost::spirit::x3;
namespace parse_to_object
{
struct object
{
using value_type = std::pair<int,int>;
object() { std::cout << "object::object() - invoked" << std::endl; }
void add(value_type val) { _data.push_back(std::move(val)); }
std::vector<std::pair<int,int>> _data;
};
const x3::rule<struct Test, object> r_ints("r_ints");
const auto r_ints_def = ('{' >> x3::int_ >> ':' >> x3::int_ >> '}') % ',';
BOOST_SPIRIT_DEFINE(r_ints)
}
namespace boost { namespace spirit { namespace x3 { namespace traits {
template<> struct push_back_container<parse_to_object::object>
{
template<typename T>
static bool call(parse_to_object::object& obj, T&& val)
{
obj.add(std::forward<T>(val));
return true;
}
};
}}}}
int main()
{
const std::string text("{1:2},{3:4}");
auto begin = std::begin(text), end = std::end(text);
parse_to_object::object result;
auto ok = phrase_parse(begin, end, parse_to_object::r_ints >> x3::eoi, x3::space, result);
std::cout << "ok = " << std::boolalpha << ok << "\n";
for (auto& p : result._data)
std::cout << "{" << p.first << ", " << p.second << "} ";
std::cout << "\n";
}
Prints
object::object() - invoked
<r_ints>
<try>{1:2},{3:4}</try>
<success></success>
<attributes></attributes>
</r_ints>
ok = true
{1, 2} {3, 4}

boost spirit debug rule with locals

I fail to compile code in debug mode (code with BOOST_SPIRIT_DEBUG_NODE(my_rule)) when my_rule has some local variable of custom type.
First version with rule qi::locals<std::string> is OK
Second version with rule qi::locals<std::string,int> is still OK
Current version with rule qi::locals<std::string,std::vector<int> > does not compile.
error: no match for operator<< (operand types are std::basic_ostream<char> and const std::vector<int>)
I declare streaming operator<< :
std::ostream& > operator<< (std::ostream& os, std::vector<int> const& art)
But It still does not compile.
I use boost 1_64_0. Here is the smallest complete code:
#define BOOST_SPIRIT_DEBUG
#if !defined(BOOST_SPIRIT_DEBUG_OUT)
#define BOOST_SPIRIT_DEBUG_OUT std::cerr
#endif
#include <tuple>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <iostream>
#include <string>
#include <vector>
// To solve the pb of declaration of grammar with locals
#include <typeinfo>
std::ostream&
operator<< (std::ostream& os, std::vector<int> const& art)
{
os << "[";
for( auto it = art.begin(); it != art.end() ; it++ ) {
os << *it << ",";
}
os << "]";
return os;
}
namespace client
{
namespace phoenix = boost::phoenix;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using phoenix::val;
using namespace qi::labels;
using qi::_val;
using qi::_1;
// Our number list parser
template <typename Iterator>
struct mini_wkart_grammar
// first version: : qi::grammar<Iterator, int(), qi::locals<std::string>, ascii::space_type>
// second version: : qi::grammar<Iterator, int(), qi::locals<std::string,int>, ascii::space_type>
: qi::grammar<Iterator, std::vector<int>(), qi::locals<std::string,std::vector<int> >, ascii::space_type>
{
mini_wkart_grammar() : mini_wkart_grammar::base_type(start,"numbers")
{
using phoenix::push_back;
// first version: start= (qi::int_ >> qi::char_(',') >> qi::int_)[_val=_1+_3];
// second version: start= (qi::int_[_b=_1] >> qi::char_(',') >> qi::int_[_b+=_1])[_val=_b];
start= (qi::int_[push_back(_b,_1)] >> qi::char_(',') >> qi::int_[push_back(_b,_1)])[_val=_b];
BOOST_SPIRIT_DEBUG_NODE(start);
}
// first version OK: qi::rule<Iterator, int(), qi::locals<std::string>, ascii::space_type> start;
// second version OK: qi::rule<Iterator, int(), qi::locals<std::string,int>, ascii::space_type> start;
qi::rule<Iterator, std::vector<int>(), qi::locals<std::string,std::vector<int> >, ascii::space_type> start;
};
}
////////////////////////////////////////////////////////////////////////////
// Main program
////////////////////////////////////////////////////////////////////////////
int
main()
{
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "\t\tA comma separated list parser for Spirit...\n\n";
std::cout << "/////////////////////////////////////////////////////////\n\n";
std::cout << "Give me a comma separated list of numbers.\n";
std::cout << "Type [q or Q] to quit\n\n";
// std::string result;
// first ans second version: int result;
std::vector<int> result;
std::string str;
using boost::spirit::ascii::space;
client::mini_wkart_grammar<std::string::const_iterator> wkart_grammar;
while (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();
// if (client::parse_numbers(str.begin(), str.end()))
if (boost::spirit::qi::phrase_parse(iter, end, wkart_grammar, space, result))
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << result << " Parses OK: " << std::endl;
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
}
}
std::cout << "Bye... :-) \n\n";
return 0;
}
I think miss something in the operator declaration?
Thanks for any help..
First Off...
What are you trying to achieve anyways? That whole grammar could be start = qi::int_ % ','; and still have the exact same effect. See Boost Spirit: “Semantic actions are evil”?
Your Question:
Sadly you need to make that operator<< ADL-enabled. (http://en.cppreference.com/w/cpp/language/adl)
Since the element type is primitive, there is no associated namespace. So the only namespace that will be tried is namespace ::std which declared std::vector<>.
namespace std {
std::ostream &operator<<(std::ostream &os, vector<int> const &art) {
os << "[";
for (auto it = art.begin(); it != art.end(); it++) {
os << *it << ",";
}
os << "]";
return os;
}
}
That might have undesired side effects, you you may want to force the issue with a hack:
namespace ADL_Hack {
template <typename T>
struct allocator : std::allocator<T> { };
}
template <typename T>
using Vector = std::vector<T, ADL_Hack::allocator<T> >;
namespace ADL_Hack {
template <typename... Ts>
std::ostream &operator<<(std::ostream &os, std::vector<Ts...> const &art) {
os << "[";
for (auto it = art.begin(); it != art.end(); it++) {
os << *it << ",";
}
os << "]";
return os;
}
}
See it Live On Wandbox

Resources