【问题标题】:boost-python - expose C++ (non-pure) virtual method with default argumentsboost-python - 使用默认参数公开 C++(非纯)虚拟方法
【发布时间】:2019-08-18 16:54:48
【问题描述】:

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

宏会编译,但似乎没有办法将 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 出于某种原因与基类XgetItem() 的签名不匹配。

在这一点上我有点犹豫 - 我的定义可能是简单明了的错误!

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

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

(注意 Pure 虚拟函数不需要默认函数意味着只有一个函数被传递到.def(),因此修改非虚拟琐碎示例看起来很简单 -非纯虚拟不是这种情况)

编辑

在网上找到了其他人提出相同问题的单一参考 - 该解决方案与我使用 args 的尝试接近,但似乎不起作用并且违反了当前的 Boost 文档?它对getItem()default_getItem() 使用def() 中的包装类,并传入一组参数 - 下面是参考中给出的示例。唯一的其他区别是默认值是一个值而不是我的情况下的指针:

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

【问题讨论】:

    标签: python c++ boost boost-python


    【解决方案1】:

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

    秘诀是根本不使用boost::python::argsBOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS 宏(尽管我接受它不支持python 中的键值参数)。

    只需创建一个辅助函数,它的左值与包含虚函数的类匹配,即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&lt;const char* (X::*)(Z*)&gt;("getItem",

    这似乎运作良好 - 但我没有彻底测试它不会以某种方式巧妙地破坏多态性。

    【讨论】:

      猜你喜欢
      • 2011-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-13
      • 1970-01-01
      • 2010-11-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多