简体   繁体   English

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

[英]boost-python - expose C++ (non-pure) virtual method with default arguments

In boost-python, given some class X, the recommended way to expose a virtual method is to wrap it as shown below. 在boost-python中,给定一些类X,暴露虚拟方法的推荐方法是将其包装起来,如下所示。

I'm trying to marry this with the functionality to specify default parameters on that virtual method. 我试图将其与在该虚拟方法上指定默认参数的功能结合使用。 This is also supported in the Boost docs. Boost文档也支持这一点。

However no example is given of exposing a virtual method which also has default parameters. 但是,没有给出暴露也具有默认参数的虚拟方法的示例。

I've assumed the wrapper class must also define the argument as a default and pass this through to the underlying getItem() . 我假设包装类还必须将参数定义为默认值,并将其传递给底层的getItem()

The default argument is a NULL pointer, although I have no reason to suspect (yet) that this is relevant. 默认参数是一个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); }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
};

This is then defined as: 然后将其定义为:

.def("getItem",                                                                                                                                                                                                                                      
     &X::getItem,                                                                                                                                                                                                                              
     &X_wrap::default_getItem);

The problem is that the default parameter will not be carried around as part of the method signature. 问题是默认参数不会作为方法签名的一部分携带。

Boost provides a workaround for this: Boost为此提供了一种解决方法:

BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(getItem_member_overloads, getItem, 0, 1)

It's clear in the non-virtual case how to apply this to the def : 在非虚拟案例中很清楚如何将其应用于def

.def("getItem",                                                                                                                                                                                                                                      
     &X::getItem,                                                                                                                                                                                                                              
     getItem_member_overloads());

This compiles and works as expected. 这编译并按预期工作。

However with the wrapper and the default function to complicate matters when we have a virtual function it's not clear how to combine these. 然而,当我们有虚函数时,包装器和默认函数使问题复杂化,不清楚如何组合它们。 I'm assuming the above cannot be the correct solution as I've removed default_getItem() from the definition. 我假设上面不能是正确的解决方案因为我从定义中删除了default_getItem()

This lead me to try to create a second set of overloads: 这导致我尝试创建第二组重载:

BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(default_getItem_overloads, default_getItem, 0, 1);

The macro compiles, but there doesn't seem to be a way to apply 2 separate sets of overloads to the .def() that doesn't then fail compilation. 宏编译,但似乎没有办法将两个独立的重载集应用于.def() ,然后编译失败。

Google has suggested I can use boost::python::arg() and define it like arg("a")=1 : Google建议我可以使用boost::python::arg()并将其定义为arg("a")=1

Something like the below compiles: 类似下面的东西编译:

.def("getItem",                                                                                                                                                                                                                                      
     &X::getItem,
     arg("a")=0,                                                                                                                                                                                                                     
     &X_wrap::default_getItem,
     arg("a")=0);

But I get a runtime error: 但是我收到运行时错误:

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)

This suggests that ChildClassOfX is for some reason not matching to the signature of getItem() in base class X . 这表明ChildClassOfX由于某种原因不匹配基类XgetItem()的签名。

At this point I'm winging it a bit - and my definitions are probably plain and simple wrong! 在这一点上,我有点wing - 我的定义可能是简单而简单的错误!

So far any solution I have either breaks the runtime polymorphism in some way, or doesn't compile. 到目前为止,我所遇到的任何解决方案都以某种方式破坏了运行时多态性,或者没有编译。

Any suggestions or examples would be a huge help! 任何建议或例子都将是一个巨大的帮助!

(Note for Pure Virtual Functions the lack of a requirement for a default function means only a single function is passed into the .def() so it looks straightforward to modify the non-virtual trivial example - this is not the case for non-pure virtuals) (对于虚函数的注意事项,缺少对默认函数的要求意味着只有一个函数被传递到.def()因此它看起来很容易修改非虚拟的例子 - 这不是非纯函数的情况虚函数)

EDIT 编辑

Found a single reference online to someone else asking the same question - the solution is close to my attempt using args, but doesn't seem to work and goes against the current Boost documentation? 在网上找到一个单独的参考文件给其他人提出同样的问题 - 解决方案接近我使用args的尝试,但似乎不起作用并违背当前的Boost文档? It uses the wrapper class in the def() for both getItem() and default_getItem() with a single set of args passed in - below is the example given in the reference. 它使用def()的包装类为getItem()default_getItem()传递一组args - 下面是引用中给出的示例。 The only other difference is the default value is a value not a pointer as in my case: 唯一的另一个区别是默认值是一个值而不是我的情况下的指针:

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

Modifying for my example build OK, but throws with: 修改我的示例构建确定,但抛出:

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)                                                                                                                                                                                                   

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

I have cobbled together a solution which seems to work, and obey the rules of polymorphism. 我拼凑了一个似乎有效的解决方案,并遵守多态的规则。

The secret is to simply not use boost::python::args or the BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS macro at all (although I accept that it won't support key-value args in python as-is). 秘诀就是根本不使用boost::python::argsBOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS宏(虽然我接受它不支持python as-is中的键值args)。

Simply create an auxillary function that has an l-value matching the class containing the virtual function ie X - and has NO other arguments passed into it. 只需创建一个辅助函数,它具有与包含虚函数的类匹配的l值,即X - 并且没有其他参数传递给它。 This gets rid of the issue of Python not matching the method signature because the parameter a along with it's default value simply do not exist: 这解决了Python不匹配方法签名的问题,因为参数a及其默认值根本不存在:

A* getItem_noargs_wrap(X& x)
{
    return x.getItem();
}

This seems pointless but it is anything but. 这似乎毫无意义,但它绝不是。 The x.getItem() call is C++ to C++ so the default parameter is correctly matched against the empty signature. x.getItem()调用是C ++到C ++,因此默认参数与空签名正确匹配。

However when we come to write our def() we can now give Python a function that genuinely takes no arguments, allowing it to match getItem() calls in Python. 但是当我们编写def()我们现在可以为Python提供一个真正不带参数的函数,允许它匹配Python中的getItem()调用。

The only thing left is to give the compiler a bit of help to know which signature to match to which underlying call: 剩下的唯一事情就是给编译器一些帮助,以便知道哪个签名与哪个底层调用匹配:

.def("getItem",                                                                                                                                                                                                                                      
     &getItem_noargs_wrap);
.def("getItem",                                                                                                                                                                                                                                      
     &X::getItem,                                                                                                                                                                                                                   
     &X_wrap::default_getItem);

So getItem() is exposed twice to Python - once with no arguments using our auxillary function to call the right instance method behind the scenes, and once taking a Z* and using the standard pattern for non-pure virtual functions. 所以getItem()暴露两次到Python - 一次没有参数使用我们的辅助函数在幕后调用正确的实例方法,并且一次采用Z*并使用标准模式用于非纯虚函数。

The second call is in-effect matching: 第二个调用是有效匹配:

.def<const char* (X::*)(Z*)>("getItem",

This seems to work well - but I haven't exhaustively tested it doesn't somehow subtely break polymorphism. 这似乎运作良好 - 但我没有详尽地测试它不会以某种方式彻底打破多态性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM