boost-python - 使用默认参数公开 C++(非纯)虚方法
boost-python - expose C++ (non-pure) virtual method with default arguments
在 boost-python 中,给定一些 class X,公开虚方法的推荐方法是如下所示包装它。
我正在尝试将其与在该虚拟方法上指定默认参数的功能结合起来。 Boost 文档也支持这一点。
但是没有给出公开也具有默认参数的虚方法的示例。
我假设包装器 class 还必须将参数定义为默认参数并将其传递给底层 getItem()
。
默认参数是 NULL 指针,尽管我(目前)还没有理由怀疑这是相关的。
struct X_wrap : X, wrapper<X>
{
X_wrap(): X() {}
// getItem() is a non-Pure Virtual Function in Class X
// It has a single argument a, which has a default value of 1
A* getItem(Z* a=NULL)
{
if (override getItem = this->get_override("getItem"))
return getItem(a);
return X::getItem(a);
}
A* default_getItem(Z* a=NULL) { return this->X::getItem(a); }
};
然后定义为:
.def("getItem",
&X::getItem,
&X_wrap::default_getItem);
问题是默认参数不会作为方法签名的一部分携带。
Boost 为此提供了一个解决方法:
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(getItem_member_overloads, getItem, 0, 1)
在非虚拟情况下很清楚如何将其应用于 def
:
.def("getItem",
&X::getItem,
getItem_member_overloads());
这会按预期编译和工作。
然而,当我们有一个虚函数时,包装器和默认函数会使事情复杂化,目前还不清楚如何将它们结合起来。我假设以上不是正确的解决方案,因为我已经从定义中删除了 default_getItem()
。
这让我尝试创建第二组重载:
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(default_getItem_overloads, default_getItem, 0, 1);
宏可以编译,但似乎没有办法将 2 组单独的重载应用到 .def()
,这样编译就不会失败。
Google 建议我可以使用 boost::python::arg()
并将其定义为 arg("a")=1
:
类似下面的编译:
.def("getItem",
&X::getItem,
arg("a")=0,
&X_wrap::default_getItem,
arg("a")=0);
但我收到运行时错误:
Boost.Python.ArgumentError: Python argument types in
X.getItem(ChildClassOfX)
did not match C++ signature:
getItem(X_wrap {lvalue}, Z* a=0)
getItem(X {lvalue}, Z* a=0)
这表明 ChildClassOfX
由于某种原因与基 class X
.
中 getItem()
的签名不匹配
在这一点上我有点即兴发挥 - 我的定义可能是简单明了的错误!
到目前为止,我的任何解决方案要么以某种方式破坏了运行时多态性,要么无法编译。
任何建议或例子都会有很大的帮助!
(注意 Pure 虚函数缺少对默认函数的要求意味着只有一个函数被传递到 .def()
所以它看起来很容易修改非虚拟的简单示例 - 对于非纯虚拟,情况并非如此)
编辑
在网上找到一个关于其他人提出相同问题的参考资料 - 该解决方案接近于我使用 args 的尝试,但似乎不起作用并且违背了当前的 Boost 文档?它对 getItem()
和 default_getItem()
使用 def()
中的包装器 class 并传入一组参数 - 下面是参考中给出的示例。唯一的区别是默认值是一个值,而不是我的例子中的指针:
def("override", WrapperClass::func, WrapperClass::default_func, (arg("x"), arg("y")=0, arg("z")=false))
修改我的示例构建正常,但抛出:
Boost.Python.ArgumentError: Python argument types in
X.getItem(ChildClassOfX)
did not match C++ signature:
getItem(X_wrap {lvalue}, Z* a=0)
getItem(X_wrap {lvalue}, Z* a=0)
参考:
http://boost.2283326.n4.nabble.com/Boost-Python-inheritance-optional-parameters-td4592869.html
我拼凑了一个似乎可行的解决方案,并且遵守多态性规则。
秘诀就是根本不使用 boost::python::args
或 BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS
宏(尽管我承认它不会按原样支持 python 中的键值参数).
只需创建一个辅助函数,其左值与包含虚函数的 class 相匹配,即 X
- 并且没有其他参数传递给它。这消除了 Python 与方法签名不匹配的问题,因为参数 a
及其默认值根本不存在:
A* getItem_noargs_wrap(X& x)
{
return x.getItem();
}
这似乎毫无意义,但事实并非如此。 x.getItem()
调用是 C++ 到 C++,因此默认参数与空签名正确匹配。
然而,当我们开始编写 def()
时,我们现在可以给 Python 一个真正不带参数的函数,允许它匹配 Python 中的 getItem()
调用.
唯一剩下的事情就是给编译器一些帮助,让其了解哪个签名与哪个底层调用相匹配:
.def("getItem",
&getItem_noargs_wrap);
.def("getItem",
&X::getItem,
&X_wrap::default_getItem);
所以 getItem()
暴露给 Python 两次 - 一次没有参数使用我们的辅助函数在幕后调用正确的实例方法,一次采用 Z*
并使用非纯虚函数的标准模式。
第二次调用有效匹配:
.def<const char* (X::*)(Z*)>("getItem",
这似乎工作得很好 - 但我没有详尽地测试它不会以某种方式巧妙地破坏多态性。
在 boost-python 中,给定一些 class X,公开虚方法的推荐方法是如下所示包装它。
我正在尝试将其与在该虚拟方法上指定默认参数的功能结合起来。 Boost 文档也支持这一点。
但是没有给出公开也具有默认参数的虚方法的示例。
我假设包装器 class 还必须将参数定义为默认参数并将其传递给底层 getItem()
。
默认参数是 NULL 指针,尽管我(目前)还没有理由怀疑这是相关的。
struct X_wrap : X, wrapper<X>
{
X_wrap(): X() {}
// getItem() is a non-Pure Virtual Function in Class X
// It has a single argument a, which has a default value of 1
A* getItem(Z* a=NULL)
{
if (override getItem = this->get_override("getItem"))
return getItem(a);
return X::getItem(a);
}
A* default_getItem(Z* a=NULL) { return this->X::getItem(a); }
};
然后定义为:
.def("getItem",
&X::getItem,
&X_wrap::default_getItem);
问题是默认参数不会作为方法签名的一部分携带。
Boost 为此提供了一个解决方法:
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(getItem_member_overloads, getItem, 0, 1)
在非虚拟情况下很清楚如何将其应用于 def
:
.def("getItem",
&X::getItem,
getItem_member_overloads());
这会按预期编译和工作。
然而,当我们有一个虚函数时,包装器和默认函数会使事情复杂化,目前还不清楚如何将它们结合起来。我假设以上不是正确的解决方案,因为我已经从定义中删除了 default_getItem()
。
这让我尝试创建第二组重载:
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(default_getItem_overloads, default_getItem, 0, 1);
宏可以编译,但似乎没有办法将 2 组单独的重载应用到 .def()
,这样编译就不会失败。
Google 建议我可以使用 boost::python::arg()
并将其定义为 arg("a")=1
:
类似下面的编译:
.def("getItem",
&X::getItem,
arg("a")=0,
&X_wrap::default_getItem,
arg("a")=0);
但我收到运行时错误:
Boost.Python.ArgumentError: Python argument types in
X.getItem(ChildClassOfX)
did not match C++ signature:
getItem(X_wrap {lvalue}, Z* a=0)
getItem(X {lvalue}, Z* a=0)
这表明 ChildClassOfX
由于某种原因与基 class X
.
getItem()
的签名不匹配
在这一点上我有点即兴发挥 - 我的定义可能是简单明了的错误!
到目前为止,我的任何解决方案要么以某种方式破坏了运行时多态性,要么无法编译。
任何建议或例子都会有很大的帮助!
(注意 Pure 虚函数缺少对默认函数的要求意味着只有一个函数被传递到 .def()
所以它看起来很容易修改非虚拟的简单示例 - 对于非纯虚拟,情况并非如此)
编辑
在网上找到一个关于其他人提出相同问题的参考资料 - 该解决方案接近于我使用 args 的尝试,但似乎不起作用并且违背了当前的 Boost 文档?它对 getItem()
和 default_getItem()
使用 def()
中的包装器 class 并传入一组参数 - 下面是参考中给出的示例。唯一的区别是默认值是一个值,而不是我的例子中的指针:
def("override", WrapperClass::func, WrapperClass::default_func, (arg("x"), arg("y")=0, arg("z")=false))
修改我的示例构建正常,但抛出:
Boost.Python.ArgumentError: Python argument types in
X.getItem(ChildClassOfX)
did not match C++ signature:
getItem(X_wrap {lvalue}, Z* a=0)
getItem(X_wrap {lvalue}, Z* a=0)
参考: http://boost.2283326.n4.nabble.com/Boost-Python-inheritance-optional-parameters-td4592869.html
我拼凑了一个似乎可行的解决方案,并且遵守多态性规则。
秘诀就是根本不使用 boost::python::args
或 BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS
宏(尽管我承认它不会按原样支持 python 中的键值参数).
只需创建一个辅助函数,其左值与包含虚函数的 class 相匹配,即 X
- 并且没有其他参数传递给它。这消除了 Python 与方法签名不匹配的问题,因为参数 a
及其默认值根本不存在:
A* getItem_noargs_wrap(X& x)
{
return x.getItem();
}
这似乎毫无意义,但事实并非如此。 x.getItem()
调用是 C++ 到 C++,因此默认参数与空签名正确匹配。
然而,当我们开始编写 def()
时,我们现在可以给 Python 一个真正不带参数的函数,允许它匹配 Python 中的 getItem()
调用.
唯一剩下的事情就是给编译器一些帮助,让其了解哪个签名与哪个底层调用相匹配:
.def("getItem",
&getItem_noargs_wrap);
.def("getItem",
&X::getItem,
&X_wrap::default_getItem);
所以 getItem()
暴露给 Python 两次 - 一次没有参数使用我们的辅助函数在幕后调用正确的实例方法,一次采用 Z*
并使用非纯虚函数的标准模式。
第二次调用有效匹配:
.def<const char* (X::*)(Z*)>("getItem",
这似乎工作得很好 - 但我没有详尽地测试它不会以某种方式巧妙地破坏多态性。