Boost Spirit - Parser Capturing Unwanted Text - boost

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

Related

c++ : unordered map with pair of string_viewes

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;
^~~~

Spirit error handling: expectation point does not return the expected "what" value

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";
}
}
};

Continue appending [to stream] in for-loop without local variable access

Imagine you have the following code where logDebug() is expensive or is not appropriate to call more than once:
QDebug d = logDebug();
d << __FUNCTION__ << ":";
d << "positions separated with \" --- \":";
for (const auto& str : positions)
{
d << "---" << str;
}
A macro (just to replace the function name correctly) already exists which replaces the first 2 lines:
#define LOG_FUNCTION this->logDebug() << __FUNCTION__ << ":"
It creates the local variable by calling logDebug(). Once called, you can only use the operator<< onto the macro.
The problem is you can't attach the for loop body to logger.
Q: Is there a way I could use the macro for pasting all the positions (without calling logDebug again?
I would guess this should be possible using lambdas, but I quite don't know how to.
Please help, the shortest answer wins!
Q: Is there a way I could use the macro for pasting all the positions (without calling logDebug again? I would guess this should be possible using lambdas, but I quite don't know how to.
I suppose it's possible with something as follows (used std::cout instead of logDebug())
#include <iostream>
#define LOG_FUNCTION std::cout << __FUNCTION__ << ": "
#define LOG_DEB(ps) \
[](auto & s, auto const & _ps) { for ( auto const & p : _ps ) s << p; } \
(LOG_FUNCTION, ps)
int main ()
{
int a[] { 0, 1, 2, 3, 4 };
LOG_DEB(a);
}
I've used a couple of auto as types of the lambda arguments and this works only starting from C++14.
In C++11 you have to replace they with the correct types.
Well the macro can be coerced to return your debug object:
#define LOG_FUNCTION() this->logDebug() << __FUNCTION__ << ":"
Then use it like this:
auto& d = LOG_FUNCTION();
d << "positions separated with \" --- \":";
for (const auto& str : positions)
{
d << "---" << str;
}

Can someone explain me the output ? (C++11)

int main(){
auto func1 = [](int y) {
cout << y << " ";
};
auto func2 = [](int y) {
cout << y * y << " ";
};
cout << "func1 is : " << typeid(func1).name() << endl;
cout << "func2 is : " << typeid(func2).name() << endl;
cout << "main is : " << typeid(main).name() << endl;
}
OSX output:
func1 is : Z4mainE3$_0
func2 is : Z4mainE3$_1
main is : FivE
Can someone explain the output ??
Thanks, I am just exploring some c++11 features.
What you are seeing here is the name mangled name of each of these symbols. This is what typeid.name() for your compiler implementation returns. There is no requirement that the mangled name precisely relates directly to your code. Using the mangled symbol names already present in the object files for linking is a convenient implementation choice.
You can unmangle names using the c++filt tool:
Thus:
$ c++filt _FivE
yields
int ()
In other words, a function returning an int. Remember that what you are asking for here is the type of the function and not its name.
If you were to apply this to a class
class foo
{
};
cout << "foo is : " << typeid(foo).name() << endl;
You will find output is 3foo and the unmanged name foo.
The two lambdas don't unmangle. This is because they are anonymous functions, so don't need an external name.
Furthermore, compiler generates a functor class for each lambda. The first of which would look like
struct Z4mainE3
{
void operator()(int y)
{
cout << y << " ";
}
}
This means that each one is a distinct type. The name is synthetic, and generated by the compiler such that is won't collide with anything else.
The typeid operator will operate on the functor struct and not the apparent return and argument type of the lambda itself, hence the two of them are a different type despite apparently being functions having the same signature.
The long-standing advice about typeid().name() operator is that it is not portable; you should not rely on the values returned.

Generate string if boolean attribute is true (karma counterpart to qi::matches)

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()

Resources