boost-python-使用默认参数公开C ++(非纯)虚拟方法

时间:2019-03-28 01:17:01

标签: python c++ boost boost-python

在boost-python中,给定一些X类,建议的公开虚拟方法的方法是将其包装,如下所示。

我正在尝试将此功能与在该虚拟方法上指定默认参数结合使用。 Boost文档也支持此功能。

但是,没有给出公开具有默认参数的虚拟方法的示例。

我假设包装器类还必须将参数定义为默认参数,并将其传递给基础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);

宏可以编译,但是似乎没有一种方法可以对.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由于某种原因与基类getItem()X的签名不匹配。

在这一点上,我要介绍一下-我的定义可能是简单明了的错误!

到目前为止,我所拥有的任何解决方案要么以某种方式破坏了运行时多态性,要么不进行编译。

任何建议或示例都会有很大帮助!

(关于 Pure 虚拟函数的注释,由于缺少默认函数,意味着仅将单个函数传递到.def()中,因此修改非虚拟琐碎函数看起来很简单示例-非纯虚拟机则不是这种情况

编辑

在网上找到了一个向其他人问相同问题的参考,该解决方案与我尝试使用args接近,但是似乎不起作用,并且与当前的Boost文档背道而驰吗?它使用def()getItem()的{​​{1}}中的包装器类,并传入了一组args-下面是参考文献中给出的示例。唯一的不同是默认值是一个值,而不是像我这样的指针:

default_getItem()

为我的示例进行修改,构建成功,但抛出:

def("override", WrapperClass::func, WrapperClass::default_func, (arg("x"), arg("y")=0, arg("z")=false))

参考: http://boost.2283326.n4.nabble.com/Boost-Python-inheritance-optional-parameters-td4592869.html

1 个答案:

答案 0 :(得分:0)

我拼凑了一个似乎可行的解决方案,并遵守多态性规则。

秘密在于根本不使用boost::python::argsBOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS宏(尽管我接受它不支持python原样的键值args)。

仅创建一个辅助函数,该辅助函数的l值与包含虚拟函数的类(即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",

这似乎工作得很好-但我还没有详尽地测试它并不能以某种方式巧妙地破坏多态性。