I have been wrestling with Boost.Spirit for a week. Forgive me for my ignorance about C 's template metaprogramming, but I can't help to ask: How do you know what can you do with a variable if you don't know its god damn type? For example:
namespace qi = boost::spirit::qi;
qi::on_error<qi::fail>(rule,
[](auto& args, auto& context, auto& r) {
// what to do with args, context and r?
}
);
It's not like Python, where you can use dir()
or help()
to get information about some variable. I know I can use typeid(..).name()
to find out the type of a variable, but usually it turns out to be some long unreadable text like:
struct boost::fusion::vector<class boost::spirit::lex::lexertl::iterator<class boost::spirit::lex::lexertl::functor<struct boost::spirit::lex::lexertl::token<class std::_String_iterator<class std::_String_val<struct std::_Simple_types<char> > >,struct boost::mpl::vector<__int64,struct String,struct Symbol,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na>,struct boost::mpl::bool_<1>,unsigned __int64>,class boost::spirit::lex::lexertl::detail::data,class std::_String_iterator<class std::_String_val<struct std::_Simple_types<char> > >,struct boost::mpl::bool_<1>,struct boost::mpl::bool_<1> > > & __ptr64,class boost::spirit::lex::lexertl::iterator<class boost::spirit::lex::lexertl::functor<struct boost::spirit::lex::lexertl::token<class std::_String_iterator<class std::_String_val<struct std::_Simple_types<char> > >,struct boost::mpl::vector<__int64,struct String,struct Symbol,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na>,struct boost::mpl::bool_<1>,unsigned __int64>,class boost::spirit::lex::lexertl::detail::data,class std::_String_iterator<class std::_String_val<struct std::_Simple_types<char> > >,struct boost::mpl::bool_<1>,struct boost::mpl::bool_<1> > > const & __ptr64,class boost::spirit::lex::lexertl::iterator<class boost::spirit::lex::lexertl::functor<struct boost::spirit::lex::lexertl::token<class std::_String_iterator<class std::_String_val<struct std::_Simple_types<char> > >,struct boost::mpl::vector<__int64,struct String,struct Symbol,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na,struct boost::mpl::na>,struct boost::mpl::bool_<1>,unsigned __int64>,class boost::spirit::lex::lexertl::detail::data,class std::_String_iterator<class std::_String_val<struct std::_Simple_types<char> > >,struct boost::mpl::bool_<1>,struct boost::mpl::bool_<1> > > const & __ptr64,struct boost::spirit::info const & __ptr64>
So there is only one method left: refer to the documentation. But here's the problem: Spirit's documentation is incomplete. In this example, It only say a few words about these arguments in the "Error Handling Tutorial":
on_error declares our error handler:
on_error<Action>(rule, handler)
...
handler is the actual error handling function. It expects 4 arguments:
Arg Description first The position of the iterator when the rule with the handler was entered. last The end of input. error-pos The actual position of the iterator where the error occurred. what What failed: a string describing the failure.
But as you can see, on_error
's second argument is not a 4-arguments function, but a 3-arguments function (args, context and r). Is the documentation wrong? Where can I find out the methods on these arguments? Does Spirit has a per-funcion API Documentation?
CodePudding user response:
The simplest way:
qi::on_error<qi::fail>(rule,
[](auto& args, auto& context, auto& r) {
std::cerr << __PRETTY_FUNCTION__ << std::endl;
}
);
On e.g. GCC this prints the full signature including deduced type arguments.
Is the documentation wrong?
Note that it DOES expect 4 arguments:
using namespace qi::labels;
qi::on_error<qi::error_handler_result::fail>(rule, f_(_1, _2, _3, _4));
When you install a handler like f_
:
struct F {
using result_type = void;
template <typename... Args>
void operator()(Args&&...) const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
boost::phoenix::function<F> f_{};
It prints (Live):
"expected" -> true
void Parser::F::operator()(Args&& ...) const [with Args = {const char*&, const char* const&, const char* const&, const boost::spirit::info&}]
"unexpected" -> false
As you can see, the documentation is not wrong or incomplete. Your expectations were, because you didn't get that handler
represent a deferred function aka. Phoenix Actor. I guess the "devious" thing the documentation did was assume you'd see that from the actual example, which immediately precedes the documentation you quoted:
on_error<fail> ( xml , std::cout << val("Error! Expecting ") << _4 // what failed? << val(" here: \"") << construct<std::string>(_3, _2) // iterators to error-pos, end << val("\"") << std::endl );
Here, it seems quite obvious that the expression std::cout << val("Error! Expecting ") << _4 << val(" here: \"") << construct<std::string>(_3, _2) << val("\"") << std::endl
is not literally "a function taking 4 arguments". It is a Phoenix expression defining an actor that takes 4 arguments, though.
You did find out that an actor is in fact still implemented in terms of a callable object. And it "takes" 3 arguments, which are the technical implementation details for Phoenix actors and the Spirit context arguments.
The problem was a disconnect of abstraction levels. The documentation documents the library interface, you accidentally looked at implementation details.
TL;DR
A lambda is not a deferred Phoenix actor.
Learn more about how deferred actors work e.g. with semantic actions:
Some answers on [SO] that highlight the relation between Actor signature, context and implementation signatures:
CodePudding user response:
It will generate a compile time error, but if you have a class template declaration like
template <typename T> struct get_type;
Then trying to create a get_type
object with a provided type will generate an error and in the error message it will tell you what the type provide to get_type
is. For example using
int main()
{
auto lambda = [](auto first, auto second, auto third)
{
get_type<decltype(first)>{};
get_type<decltype(second)>{};
get_type<decltype(third)>{};
};
lambda(A{}, B{}, int{});
}
produces an error message like
main.cpp:20:9: error: invalid use of incomplete type 'struct get_type<A>'
20 | get_type<decltype(first)>{};
| ^~~~~~~~
main.cpp:14:30: note: declaration of 'struct get_type<A>'
14 | template <typename T> struct get_type;
| ^~~~~~~~
main.cpp:21:9: error: invalid use of incomplete type 'struct get_type<B>'
21 | get_type<decltype(second)>{};
| ^~~~~~~~
main.cpp:14:30: note: declaration of 'struct get_type<B>'
14 | template <typename T> struct get_type;
| ^~~~~~~~
main.cpp:22:9: error: invalid use of incomplete type 'struct get_type<int>'
22 | get_type<decltype(third)>{};
| ^~~~~~~~
main.cpp:14:30: note: declaration of 'struct get_type<int>'
14 | template <typename T> struct get_type;
| ^~~~~~~~
which tells use first
is an A
, second is a B
and third
is an int
as seen in this live example.