You are using namespaces too much. Also, ADL pulls in std::ref
for std::string
argument regardless, unless ref
is parenthesized or namespace qualified.
Over-use of using namespace
is is never a good idea (see e.g. Why is "using namespace std;" considered bad practice?) and in this case the message spells out the confusion between std::ref
and boost::phoenix::ref
(and potentially others, but you didn't include the full message).
Just say no:
Live On Coliru
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/optional.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
#define addMinorProblem(...) do {} while (0)
boost::optional<std::pair<double, std::string>> parse(std::string_view input)
{
using Iterator = std::string_view::const_iterator;
Iterator first = begin(input), last = end(input);
double duration;
std::string title;
boost::spirit::qi::rule<Iterator, std::string()> quoutedString;
quoutedString %= qi::lexeme[+(qi::char_)];
bool r = parse(first, last,
("#EXTINF:" >> qi::double_[px::ref(duration) = qi::_1] >>
-(',' >> quoutedString[px::ref(title) = qi::_1])));
if (!r || first != last) {
addMinorProblem(stateObj,
_("Cannot parse information from #EXTINF tag"));
return {};
}
return std::make_pair(duration, title);
}
int main()
{
for (std::string const input : {
"#EXTINF:10,Title of the segment", // => I need "Title of the
// segment" phrase
"#EXTINF:20,Title", // => I need "Title" phrase
"#EXTINF:12", // => I need "" phrase
}) {
if (auto result = parse(input)) {
std::cout << "Parsed: (" << result->first << ", " << std::quoted(result->second) << ")\n";
} else {
std::cout << "Cannot parse " << std::quoted(input) << "\n";
}
}
}
Prints
Parsed: (10, "Title of the segment")
Parsed: (20, "Title")
Parsed: (12, "")
Improving, Little Things
With some judicious local using declarations you can make it "shorter" again:
using namespace qi::labels;
using px::ref;
bool r = parse(first, last,
("#EXTINF:" >> qi::double_[ref(duration) = _1] >>
-(',' >> quoutedString[(ref)(title) = _1])));
I personally find this more obscure than clear (picture yourself explaining (ref)(title)
in a code review?)
The operator %=
is not meaningful without semantic actions
qi::lexeme[]
is meaningless without a skipper (see Boost spirit skipper issues)
quoutedString
[sic] is a misnomer (for now?) because it doesn't parse quotes
Why not use automatic attribute propagation instead of painful semantics actions? They just increase compile times, and, as you are discovering, development times as well. See Boost Spirit: "Semantic actions are evil"?
Also, instead of tediously checking first == last
after the parse, simply match >> qi::eoi
in the expression?
All of the above simplifies into the following:
Live On Coliru
boost::optional<std::pair<double, std::string>> parse(std::string_view input)
{
namespace qi = boost::spirit::qi;
double duration;
std::string title;
if (qi::parse(
begin(input), end(input), //
("#EXTINF:" >> qi::double_ >> -(',' >> +qi::char_) >> qi::eoi),
duration, title))
{
return std::make_pair(duration, title);
}
addMinorProblem(stateObj, _("Cannot parse information from #EXTINF tag"));
return {};
}
No more phoenix
, semantic actions, what not. Still printing:
Parsed: (10, "Title of the segment")
Parsed: (20, "Title")
Parsed: (12, "")