Col*_*ett 1 c++ python boost boost-python
我正在尝试通过Python在我的游戏引擎中创建一个很好的界面来进行HTTP调用,但是我遇到了一些问题.
我有一个函数,get_async它启动指定URL 的curl请求.该函数在http_manager类中定义,如下所示:
struct http_manager
{
typedef function<void(boost::shared_ptr<http_response>)> response_callback_type;
typedef function<void(size_t)> write_callback_type;
void get_async(
const string& url,
const http_headers_type& headers = http_headers_type(),
const http_data_type& data = http_data_type(),
response_callback_type on_response = nullptr,
write_callback_type on_write = nullptr
);
};
Run Code Online (Sandbox Code Playgroud)
我能够在Python中成功进行此调用:
http.get_async('http://www.google.ca')
Run Code Online (Sandbox Code Playgroud)
但是,我想打个电话:
http.get_async('http://www.google.ca', on_response=f)
Run Code Online (Sandbox Code Playgroud)
这里的关键是我希望通过名称显式指定参数,并让所有其他参数成为默认值,就像在常规Python中一样.
不幸的是,当我这样做时,我从Python得到以下错误:
ArgumentError: Python argument types in
HttpManager.get_async(HttpManager, str)
did not match C++ signature:
get_async(struct naga::http_manager {lvalue}, class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > url)
get_async(struct naga::http_manager {lvalue}, class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > url, class std::vector<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > > headers)
get_async(struct naga::http_manager {lvalue}, class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > url, class std::vector<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > > headers, class std::map<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > > data)
get_async(struct naga::http_manager {lvalue}, class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > url, class std::vector<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > > headers, class std::map<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > > data, class std::function<void __cdecl(class boost::shared_ptr<struct naga::http_response>)> on_response)
get_async(struct naga::http_manager {lvalue}, class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > url, class std::vector<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > > headers, class std::map<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,struct std::less<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > >,class std::allocator<struct std::pair<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const ,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > > > data, class std::function<void __cdecl(class boost::shared_ptr<struct naga::http_response>)> on_response, class std::function<void __cdecl(unsigned int)> on_write)
Run Code Online (Sandbox Code Playgroud)
我很困惑为什么它认为参数签名是HttpManager.get_async(HttpManager, str)我明显传递3个参数(self,url和on_response)的时候.
以下是我的BOOST_PYTHON_MODULE块中的相关位:
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(http_manager_get_async_overloads, http_manager::get_async, 1, 5)
class_<http_manager, noncopyable>("HttpManager", no_init)
.def("get_async", &http_manager::get_async, http_manager_get_async_overloads(args("url", "headers", "data", "on_response", "on_write")))
;
Run Code Online (Sandbox Code Playgroud)
感谢您阅读,非常感谢任何帮助!
Tan*_*ury 11
总的问题是C++只使用默认参数来丢失尾随参数,而Boost.Python没有值来提供非尾随缺失参数.
当Boost.Python内部无法调度函数时,异常消息仅捕获位置参数.异常消息尝试捕获调用者的意图,但以C++函数调用格式显示它.由于C++仅支持位置参数,因此没有明显的格式来显示非位置参数.考虑这个简单的例子:
#include <boost/python.hpp>
void f() {}
BOOST_PYTHON_MODULE(example)
{
boost::python::def("f", &f);
}
Run Code Online (Sandbox Code Playgroud)
使用以下消息调用example.f(a=0)throws Boost.Python.ArgumentError:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
Boost.Python.ArgumentError: Python argument types in
example.f()
did not match C++ signature:
f(void)
Run Code Online (Sandbox Code Playgroud)
虽然Boost.Python完全知道调用者提供了一个参数,但异常只捕获了位置参数.在上面的例子中,没有位置参数,所以输出是example.f().在原始问题中,调用http.get_async('http://www.google.ca', on_response=f)结果显示HttpManager.get_async(HttpManager, str)只显示两个位置参数的异常:类型HttpManager和str对象的隐式自参数.
在C++中,默认参数不是函数类型的一部分.此外,在调用具有默认参数的函数时,默认参数仅用于缺少尾随参数的位置.例如,考虑:
void f(int a, int b=1, int c=2);
Run Code Online (Sandbox Code Playgroud)
上面的函数有一个类型void(int, int, int),并且没有办法调用函数f,只提供参数a和参数c.如果希望f使用参数的非默认值调用c,则必须提供所有三个参数.
但是,如果在Python中声明了类似的函数,则可以调用f仅为参数提供参数a,c并且Python支持关键字参数:
def f(a, b=1, c=2):
pass
f(0, c=42) # Invokes f(0, b=1, c=42)
Run Code Online (Sandbox Code Playgroud)
考虑这个示例,其中带有默认参数的C++函数已作为重载的Python函数提供:
#include <boost/python.hpp>
void f(int a, int b=1, int c=2) {}
BOOST_PYTHON_FUNCTION_OVERLOADS(f_overloads, f, 1, 3)
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::def("f", &f, f_overloads(
python::args("a", "b", "c")));
}
Run Code Online (Sandbox Code Playgroud)
Boost.Python被告知C++函数f有一种类型void(int, int, int),并且可以用至少1个参数调用,最多3个调用.Boost.Python也不知道默认参数值是什么.因此,Boost.Python创建重载以支持调用:
f(int a)f(int a, int b)f(int, a, int b, int c)因此,当一个人试图example.f(0, c=42)在Python中调用时,函数调度将失败,因为Boost.Python需要在为其提供值b时提供值c,并且b尚未指定值.
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
Boost.Python.ArgumentError: Python argument types in
example.f(int)
did not match C++ signature:
f(int a)
f(int a, int b)
f(int a, int b, int c)
Run Code Online (Sandbox Code Playgroud)
这种行为是http.get_async('http://www.google.ca', on_response=f)失败的原因,因为Boost.Python无法调用:
http_manager.get_async(url, ???, ???, on_response);
Run Code Online (Sandbox Code Playgroud)
继续该示例,如果Python提供了默认参数,则它可以为非尾随参数提供值.在公开函数时,可以通过分配给boost::python::arg对象来为参数提供默认值.以下示例f使用默认参数公开函数:
#include <boost/python.hpp>
void f(int a, int b=1, int c=2) {}
BOOST_PYTHON_FUNCTION_OVERLOADS(f_overloads, f, 1, 3)
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::def("f", &f, f_overloads(
(python::arg("a"),
python::arg("b")=1,
python::arg("c")=2)));
}
Run Code Online (Sandbox Code Playgroud)
通过Boost.Python可以识别默认参数,它可以成功调用example.f(0, c=42).