如何找出lambda的自动类型参数中的哪些方法?

How to find out what methods in auto-type arguments of lambda?

我已经与 Boost.Spirit 搏斗了一个星期。请原谅我对 C++ 的模板元编程的无知,但我忍不住要问:如果你不知道一个该死的变量类型,你怎么知道你能用它做什么?例如:

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?
  }
);

它不像 Python,您可以在其中使用 dir()help() 来获取有关某些变量的信息。我知道我可以使用 typeid(..).name() 来找出变量的类型,但通常结果是一些长的不可读的文本,例如:

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>

所以只剩下一种方法了:参考文档。但问题是:Spirit 的文档不完整。在这个例子中,它只对 "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.

但是如你所见,on_error的第二个参数不是一个4参数函数,而是一个3参数函数(args、context和r)。文档有错吗?我在哪里可以找到这些参数的方法? Spirit 有每个函数的 API 文档吗?

最简单的方法:

qi::on_error<qi::fail>(rule,
  [](auto& args, auto& context, auto& r) {
       std::cerr << __PRETTY_FUNCTION__ << std::endl;
  }
);

例如GCC 这会打印完整的签名,包括推导的类型参数。

文档有误吗?

请注意,它确实需要 4 个参数:

using namespace qi::labels;
qi::on_error<qi::error_handler_result::fail>(rule, f_(_1, _2, _3, _4));

当您安装像 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_{};

它打印 (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

如您所见,文档没有错误或不完整。你的期望是,因为你没有得到 handler 代表一个 延迟函数 又名。 Phoenix Actor。我猜文档所做的“不正当”事情是假设您从实际示例中看到,它紧接在您引用的文档之前:

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

在这里,表达式 std::cout << val("Error! Expecting ") << _4 << val(" here: \"") << construct<std::string>(_3, _2) << val("\"") << std::endl 似乎很明显 而不是 字面上的“一个有 4 个参数的函数”。不过,它是一个 Phoenix 表达式,定义了一个接受 4 个参数的 actor。

做过发现演员实际上仍然是根据可调用对象实现的。它“接受”了 3 个参数,分别是 Phoenix actors 和 Spirit 上下文参数的技术实现细节。

问题是抽象层次的脱节。文档记录了库接口,你不小心看到了实现细节。

TL;DR

lambda 不是延迟的 Phoenix actor。

详细了解延迟演员的工作原理,例如具有语义动作:

关于 [SO] 的一些答案突出了 Actor 签名、上下文和实现签名之间的关系:

  • How to avoid boost::phoenix when generating with boost::spirit::karma

  • boost spirit semantic action parameters

它会产生一个编译时错误,但是如果你有一个 class 模板声明,比如

template <typename T> struct get_type;

然后尝试使用提供的类型创建 get_type 对象将产生错误,并且在错误消息中它会告诉您提供给 get_type 的类型是什么。例如使用

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{});
}

产生类似

的错误信息
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;
      |                              ^~~~~~~~

告诉 use firstA,第二个是 Bthirdint,如 live example.