I need to parse "title" from the next hls tag
Pattern of the tag: #EXTINF:<duration>[,<title>]
For example of real tag:
#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
I wrote the next code
double duration;
std::string title;
boost::spirit::qi::rule<Iterator, std::string()> quoutedString;
quoutedString %= lexeme[+(char_)];
bool r = parse(first, last,
("#EXTINF:" >> double_[ref(duration) = _1] >> -(',' >> quoutedString[ref(title) = _1] ) )
);
if (!r || first != last) {
addMinorProblem(stateObj, _("Cannot parse information from #EXTINF tag"));
return false;
}
But I got the next error in compilation process:
error: call of overloaded ‘ref(std::__cxx11::string&)’ is ambiguous
("#EXTINF:" >> double_[ref(duration) = _1] >> -(',' >> quoutedString[ref(title) = _1] ) )
Please help me. What am I doing wrong?
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:
#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, "")
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:
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, "")