I have the following parsing rule:
filter = (input >> (qi::repeat(0,2)[char_(';') >> input]))
input is a rule that returns a std::vector<int>, vector that I will just call vec for short.
The question is: What compound attribute would the filter rule return?
I tried:
fusion::vector <vec,std::vector <fusion::vector <char,vec> > >
But it fails and I don't know why.
The attribute types resulting of the parser expressions are quite well-documented. But that can be disorienting and timeconsuming.
Here's a trick: send in a sentinel to detect the attribute type:
struct Sniffer
{
typedef void result_type;
template <typename T>
void operator()(T const&) const { std::cout << typeid(T).name() << "\n"; }
};
then using the folliing parser expression
(input >> (qi::repeat(0,2)[qi::char_(';') >> input])) [ Sniffer() ]
will dump:
N5boost6fusion7vector2ISt6vectorIsSaIsEES2_INS1_IcS4_EESaIS5_EEEE
which c++filt -1 will tell you represents:
boost::fusion::vector2<
std::vector<short, std::allocator<short> >,
std::vector<boost::fusion::vector2<char, std::vector<short, std::allocator<short> > >,
std::allocator<boost::fusion::vector2<char, std::vector<short, std::allocator<short> > >
> >
>
See it live on Coliru: http://coliru.stacked-crooked.com/view?id=3e767990571f8d0917aae745bccfa520-5c1d29aa57205c65cfb2587775d52d22
boost::fusion::vector2<std::vector<short, std::allocator<short> >, std::vector<std::vector<short, std::allocator<short> >, std::allocator<std::vector<short, std::allocator<short> > > > >
It might be so surprisingly complicated, in part, because char_(";") could have been ';' (or more explicitely lit(';')). Constrast with this (Coliru):
boost::fusion::vector2<
std::vector<short, ... >,
std::vector<std::vector<short, std::allocator<short> >, ... > >
This should answer your question.
Sidenotes: parsing things
Don't underestimate automatic attribute propagation in Spirit. Frequently, you don't have to bother with the exact exposed types of attributes. Instead, rely on the (many) attribute transformations that Spirit uses to assign them to your supplied attribute references.
I trust you know the list-operator (%) in spirit? I'll show you how you can use it without further ado:
vector<vector<short>> data;
qi::parse(f, l, qi::short_ % ',' % ';', data);
Now, if you need to enforce the fact that it may be 1-3 elements, you might employ an eps with a Phoenix action to assert the maximum size:
const string x = "1,2,3;2,3,4;3,4,5";
auto f(begin(x)), l(end(x));
if (qi::parse(f, l,
(qi::eps(phx::size(qi::_val) < 2) > (qi::short_ % ',')) % ';'
, data))
{
cout << karma::format(karma::short_ % ',' % ';', data) << "\n";
}
cout << "remaining unparsed: '" << std::string(f,l) << "'\n";
Prints:
1,2,3;2,3,4
remaining unparsed: ';3,4,5'
Related
Here is a code snippet I have :
struct PairHasher {
size_t operator()(const std::pair<std::string_view, std::string_view>& stop_stop) const {
return hasher(stop_stop.first) + 37*hasher(stop_stop.second);
}
std::hash<std::string_view> hasher;
};
BOOST_FIXTURE_TEST_CASE(unordered_map_string_view_pair_must_be_ok, TestCaseStartStopMessager)
{
const std::vector<std::string> from_stops = {"from_0", "from_1", "from_2"};
const std::vector<std::string> to_stops = {"to_0", "to_1", "to_2"};
std::unordered_map<std::pair<std::string_view, std::string_view>, std::int32_t, TransportCatalogue::PairHasher> distance_between_stops;
for ( std::size_t idx = 0; idx < from_stops.size(); ++idx) {
std::cout << from_stops[idx] << " : " << to_stops[idx] << std::endl;
distance_between_stops[std::pair(from_stops[idx], to_stops[idx])] = idx;
}
std::cout << "MAP CONTENT :" << std::endl;
for (auto const& x : distance_between_stops)
{
std::cout << x.first.first << " : " << x.first.second << std::endl;
}
}
I expect to see 3 pairs inside the container, but there is only 1 concerning to the output :
MAP CONTENT :
from_2 : to_2
So, where are two more pair lost? What am I doing wrong?
Moving my comment to an answer.
This is pretty sneaky. I noticed in Compiler Explorer that changing:
distance_between_stops[std::pair(from_stops[idx], to_stops[idx])] = idx;
to
distance_between_stops[std::pair(std::string_view{from_stops[idx]}, std::string_view{to_stops[idx]})] = idx;
fixes the bug. This hints that the problem lies in some implicit string -> string_view conversion. And indeed that is the case, but it is hidden behind one extra layer.
std::pair(from_stops[idx], to_stops[idx]) creates a std::pair<std::string, std::string>, but distance_between_stops requires a std::pair<std::string_view, std::string_view>. When we insert values into the map, this conversion happens implicitly via overload #5 here:
template <class U1, class U2>
constexpr pair(pair<U1, U2>&& p);
Initializes first with std::forward<U1>(p.first) and second with std::forward<U2>(p.second).
This constructor participates in overload resolution if and only if std::is_constructible_v<first_type, U1&&> and std::is_constructible_v<second_type, U2&&> are both true.
This constructor is explicit if and only if std::is_convertible_v<U1&&, first_type> is false or std::is_convertible_v<U2&&, second_type> is false.
(For reference, std::is_constructible_v<std::string_view, std::string&&> and std::is_convertible_v<std::string&&, std::string_view> are both true, so we know this overload is viable and implicit.)
See the problem yet? When we use the map's operator[], it has to do an implicit conversion to create a key with the proper type. This implicit conversion constructs a pair of string_views that are viewing the temporary memory from the local pair of strings, not the underlying strings in the vector. In other words, it is conceptually similar to:
std::string_view foo(const std::string& s) {
std::string temp = s + " foo";
return temp;
}
int main() {
std::string_view sv = foo("hello");
std::cout << sv << "\n";
}
Clang emits a warning for this small example, but not OP's full example, which is unfortunate:
warning: address of stack memory associated with local variable 'temp' returned [-Wreturn-stack-address]
return temp;
^~~~
I'm using the Spirit error handling code from the article "Dispatching on Expectation Point Failures" at http://boost-spirit.com/home/2011/02/28/dispatching-on-expectation-point-failures/comment-page-1/, the last example there. My "diagnostics" and "error_handler_impl" classes are pretty much the same as is found in the article, but I can post them if someone thinks it is necessary.
In the code below, the start rule in question
comma = lit(',');
comma.name(",");
start = lit("begin") >> ident > comma >> ident;
has an expectation point after the "begin" keyword. In accordance with the article, I expected that a missing comma would cause error handler to be passed the qi::_4 value to be ",", which is the name of the comma rule. Instead, it is passing "sequence", causing the error handler to print a default message rather than the informative "Missing comma after begin keyword".
An idea what I am missing?
template <typename Iterator, typename Skipper = ascii::space_type>
class Grammar1 : boost::spirit::qi::grammar<Iterator, Skipper>
{
public:
typedef boost::spirit::qi::rule<Iterator, Skipper> rule_nil_T;
typedef boost::spirit::qi::rule<Iterator, string()> rule_str_T;
// structs from Rob Stewart's above-mentioned article
diagnostics<10> d1;
boost::phoenix::function<error_handler_impl> error_handler;
rule_str_T ident;
rule_nil_T comma;
rule_nil_T start;
Grammar1(void) : Grammar1::base_type(start)
{
ident %= lexeme [ qi::raw [ (qi::alpha | '_') >> *(qi::alnum | '_') ] ];
comma = lit(',');
comma.name(",");
start = lit("begin") >> ident > comma >> ident;
d1.add(",", "Missing comma after begin keyword");
on_error<fail>(start,
error_handler(ref(d1), _1, _2, _3, _4));
}
~Grammar1(void) { };
void parseInputFile(Iterator itr, Iterator itr_end)
{
bool r = phrase_parse(itr, itr_end, start, ascii::space);
if (r && itr == itr_end)
{
std::cout << "Parsing succeeded\n";
} else
{
string rest(itr, itr_end);
std::cout << "stopped at: \": " << rest << "\"\n";
}
}
};
Let's say I have a struct like this:
struct Something{
string name;
int code;
};
And a set of Something type:
set<Something> myset;
myset.insert({"aaa",123,});
myset.insert({"bbb",321});
myset.insert({"ccc",213});
What's wrong with this?
for (auto sth : myset){
cout << sth.name;
cout << sth.code;
}
Along the same lines... why can't I modify an element (even when the set contains plain int items) using something like this?
for (auto &sth : myset){
sth=[some value];
}
I know I can do this with vectors and maps. Why not sets?
Thanks!
Modifying an element of a set implies its position in the set's order can change. Because your compiler cannot know what exactly a particular set uses to determine its element's orders. Well, it could, theoretically, but even then it would be nearly impossible to keep track of the rearrangements while iterating through the container. It would make no sense.
What you can do, if you want to modify the elements of a set in such a way that you know will not change their order in a set, you can make the non-ordering members of your struct mutable. Note that if you make a mistake and the set's order is disturbed, any other operations on the set (like a binary search) will give incorrect results after that faulty modification. If you don't want to make members mutable, const_cast is an option, with the same caveats.
To elaborate on my answer above, an example:
#include <iostream>
#include <set>
struct bla
{
std::string name;
int index;
};
bool operator<(const bla& left, const bla& right) { return left.index < right.index; }
int main()
{
std::set<bla> example{{"har", 1}, {"diehar", 2}};
// perfectly fine
for(auto b : example)
std::cout << b.index << ' ' << b.name << '\n';
// perfectly fine - name doesn't influence set order
for(auto& b : example) // decltype(b) == const bla&
const_cast<std::string&>(b.name) = "something";
// better than first loop: no temporary copies
for(const auto& b : example)
std::cout << b.index << ' ' << b.name << '\n';
// using a "universal reference auto&&", mostly useful in template contexts
for(auto&& b : example) // decltype(b) == const bla&
std::cout << b.index << ' ' << b.name << '\n';
// destroying order of the set here:
for(auto& b : example)
const_cast<int&>(b.index) = -b.index;
// anything here relying on an ordered collection will fail
// This includes std::set::find, all the algorithms that depend on uniqueness and/or ordering
// This is pretty much all that will still work, although it may not even be guaranteed
for(auto&& b : example)
std::cout << b.index << ' ' << b.name << '\n';
}
Live code on Coliru.
Note the first const_cast is only ok because the underlying example isn't const in the first place.
Imagine we want to parse and generate simple C++ member function declarations with Boost.Spirit.
The Qi grammar might look like this:
function_ %= type_ > id_ > "()" > matches["const"];
That means, whether the function is const is stored in a bool.
How to write the corresponding generator with Karma?
function_ %= type_ << ' ' << id_ << "()" << XXX[" const"];
Here, we want a directive that consumes a boolean attribute, executes the embedded generator if the attribute is true and does nothing otherwise. We want something that makes the following tests succeed.
test_generator_attr("abc", XXX["abc"], true);
test_generator_attr("", XXX["abc"], false);
Is such a directive already available in Boost.Spirit?
The first thing that enters my mind at the moment is
bool const_qualifier = true;
std::cout << karma::format(
karma::omit[ karma::bool_(true) ] << " const" | "",
const_qualifier);
It feels a bit... clumsy. I'll have a look later what I'm forgetting :)
UPDATE Here's a slightly more elegant take using karma::symbols<>:
#include <boost/spirit/include/karma.hpp>
namespace karma = boost::spirit::karma;
int main()
{
karma::symbols<bool, const char*> const_;
const_.add(true, "const")(false, "");
for (bool const_qualifier : { true, false })
{
std::cout << karma::format_delimited("void foo()" << const_, ' ', const_qualifier) << "\n";
}
}
Prints:
void foo() const
void foo()
I have a simple struct
// in namespace client
struct UnaryExpression
{
std::string key;
SomeEnums::CompareType op;
};
SomeEnums::CompareType is an enum where I define a symbol table as such:
struct UnaryOps : bsq::symbols<char, SomeEnums::CompareType>
{
UnaryOps() : bsq::symbols<char, SomeEnums::CompareType>(std::string("UnaryOps"))
{
add("exists", SomeEnums::Exists)
("nexists", SomeEnums::NotExists);
}
};
I have two different ways I want to parse the struct, which I asked about in another thread and got to work (mostly).
My grammar looks as follows:
template<typename Iterator>
struct test_parser : bsq::grammar<Iterator, client::UnaryExpression(), bsq::ascii::space_type>
{
test_parser()
: test_parser::base_type(unaryExp, std::string("Test"))
{
using bsq::no_case;
key %= bsq::lexeme[bsq::alnum >> +(bsq::alnum | bsq::char_('.'))];
unaryExp %= unaryE | unaryF;
unaryE %= key >> no_case[unaryOps];
unaryF %= no_case[unaryOps] >> '(' >> key >> ')';
};
UnaryOps unaryOps;
bsq::rule<Iterator, std::string(), bsq::ascii::space_type> key;
bsq::rule<Iterator, client::UnaryExpression(), bsq::ascii::space_type> unaryExp;
bsq::rule<Iterator, client::UnaryExpression(), bsq::ascii::space_type> unaryE;
bsq::rule<Iterator, client::UnaryFunction(), bsq::ascii::space_type> unaryF;
};
And I'm parsing the code using the following logic:
bool r = phrase_parse(iter, end, parser, bsq::ascii::space, exp);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "key: " << exp.key << "\n";
std::cout << "op : " << exp.op << "\n";
std::cout << "-------------------------\n";
}
This all works fine if I do the input like foo exists and exp.key equals "foo" and exp.op equals the corresponding enum value (in this case 0). Something like foo1 nexists also works.
However, that second rule doesn't work like I expect. If I give it input of nexists(foo) then I get the following output:
-------------------------
Parsing succeeded
key: nexistsfoo
op : 1
-------------------------
It seems that the enum value is getting set appropriately but I can't figure out why the "nexsts" is getting prepended to the key string. Can someone please tell me how I can fix my rule so that the key would equal just 'foo' with the second rule?
I have posted a copy of the stripped down code that illustrates my problem here: http://pastebin.com/402M9iTS