简体   繁体   English

Boost.python继承自包装类

[英]Boost.python inheriting from wrapped classes

I have run into a problem when creating python bindings for an existing library with boost.python. 我在使用boost.python为现有库创建python绑定时遇到了问题。 The scenario is as follows: 方案如下:

#include<boost/python.hpp>

namespace bp = boost::python;

struct Base {
    std::stringstream _myString;
    Base() { };
    Base(const Base& base) { _myString<<base._myString.str(); }

    void getString(std::stringstream& some_string) {
        _myString.str("");
        _myString<<some_string.str();
        std::cout<<"Got string: \""<<_myString.str()<<"\""<<std::endl;
    }
};

struct BaseWrapper : Base,
                     bp::wrapper<Base>
{
    BaseWrapper() :
        Base(),
        bp::wrapper<Base>() { };

    BaseWrapper(const Base& base) :
        Base(base),
        bp::wrapper<Base>() { };

    void getString(bp::object pyObj) {
        std::string strLine = bp::extract<std::string>(pyObj);
        std::stringstream sstrLine;
        sstrLine<<strLine;
        Base::getString(sstrLine);
    }
};

struct Derived : Base
{
    Derived() : Base() { };
    Derived(const Derived& derived) : Base() { _myString<<derived._myString.str(); };
};

struct DerivedWrapper : Derived,
                        bp::wrapper<Derived>
{
    DerivedWrapper() :
        Derived(),
        bp::wrapper<Derived>() { };

    DerivedWrapper(const Derived derived) :
        Derived(derived),
        bp::wrapper<Derived>() { };
};

BOOST_PYTHON_MODULE(testInheritance){
    bp::class_<BaseWrapper>("Base")
        .def("getString", &BaseWrapper::getString);

    bp::class_<DerivedWrapper, bp::bases<Base> >("Derived");
}

(Sorry for the long code block, it was the minimum example I could think of.) (对不起长代码块,这是我能想到的最小例子。)

You can see that I had to override getString() method in the BaseWrapper so that it would work with Python strings and this part works fine: 您可以看到我必须覆盖BaseWrapper中的getString()方法,以便它可以使用Python字符串,这部分工作正常:

>>> import testInheritance
>>> base = testInheritance.Base()
>>> base.getString("bla")
Got string: "bla"
>>>

The problem appears as soon as I try to call getString from a instance of Derived : 一旦我尝试从Derived实例调用getString ,就会出现问题:

>>> derived = testInheritance.Derived()
>>> derived.getString("bla")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
    Base.getString(Derived, str)
did not match C++ signature:
    getString(BaseWrapper {lvalue}, boost::python::api::object)
>>>

I can understand what is going wrong here, but I have no idea how to fix that. 我可以理解这里出了什么问题,但我不知道如何解决这个问题。 I would appreciate any help! 我将不胜感激任何帮助!

Best Regards, eDude 最诚挚的问候,eDude

The problem is, that DerivedWrapper has no relationship to BaseWrapper . 问题是, DerivedWrapperBaseWrapper没有任何关系。 Thus DerivedWrapper needs to provide its own python adapted implementation of void getString(bp::object pyObj) . 因此DerivedWrapper需要提供自己的python改编的void getString(bp::object pyObj)

So one way to make it work is like this: 因此,使其工作的一种方法是这样的:

struct DerivedWrapper : Derived,
                        bp::wrapper<Derived>
{
    DerivedWrapper() :
        Derived(),
        bp::wrapper<Derived>() { };

    DerivedWrapper(const Derived derived) :
        Derived(derived),
        bp::wrapper<Derived>() { };

    void getString(bp::object pyObj) {
        std::string strLine = bp::extract<std::string>(pyObj);
        std::stringstream sstrLine;
        sstrLine<<"(from DerivedWrapper) "<<strLine;
        Derived::getString(sstrLine);
    }
};

[...]

    bp::class_<DerivedWrapper, bp::bases<Base> >("Derived")
        .def("getString", &DerivedWrapper::getString);

And the output of 和输出

base = testInheritance.Base()
base.getString("bla")

derived = testInheritance.Derived()
derived.getString("blub")

is as expected 如预期的那样

Got string: "bla"
Got string: "(from DerivedWrapper) blub"

I got exactly the same problem which I manage to solve, but solution has some internal faults in some special cases. 我遇到了与我设法解决的问题完全相同的问题,但在某些特殊情况下,解决方案存在一些内部错误。 There is a known problem about using boost::shared_ptr in python when it is necessary to create boost::weak_ptr references from values passed by boost::python. 当需要从boost :: python传递的值创建boost :: weak_ptr引用时,在python中使用boost :: shared_ptr存在一个已知问题。 I won't go into details while it is not related to the post. 我不会详细介绍与帖子无关的细节。 Anyway I needed to wrap boost::shared_ptr into another class (I called it PythonSharedPtr) to hide boost::shared_ptr from boost::python and then I get similar problem. 无论如何,我需要将boost :: shared_ptr包装到另一个类(我称之为PythonSharedPtr)来隐藏boost :: python中的boost :: shared_ptr然后我得到类似的问题。 Consider following setup: class A is used on c++ side as boost::shared_ptr, class B (inherited from A) as boost::shared_ptr and in python both shared_ptr are wrapped into another class (for further info why I needed to make it, is explained at: boost::python and weak_ptr : stuff disappearing http://mail.python.org/pipermail/cplusplus-sig/2009-November/014983.html 考虑以下设置:类A在c ++端用作boost :: shared_ptr,类B(从A继承)用作boost :: shared_ptr,在python中,shared_ptr被包装到另一个类中(为了进一步说明为什么我需要创建它,解释如下: boost :: python和weak_ptr:东西消失 http://mail.python.org/pipermail/cplusplus-sig/2009-November/014983.html

So then I need to write proper export to the boost python: class_, wrapped_shared_ptr, noncopyable> and class_, wrapped_shared_ptr, noncopyable> 那么我需要编写适当的导出到boost python:class_,wrapped_shared_ptr,noncopyable>和class_,wrapped_shared_ptr,noncopyable>

I think till now it is similar with your code. 我想到现在它与你的代码相似。 The tricky part is how to allow in boost::python usage of bases for the exporting of B (while shared_ptr is not related to shared_ptr). 棘手的部分是如何允许在boost :: python中使用base来导出B(而shared_ptr与shared_ptr无关)。 After some research in boost::python sources and came with the solution which is pretty nice, but it works only in cases, that there is no multi-inheritance for class B. 经过对boost :: python源代码的一些研究,并提供了相当不错的解决方案,但它仅适用于案例,B类没有多重继承。

Here is the code: 这是代码:

namespace boost { namespace python { namespace objects
{
    template<typename Source, typename Target>
    struct shared_ptr_cast_generator
    {
        static void* execute(void* source)
        {
            const boost::shared_ptr<Source>* sourcePtr = static_cast<boost::shared_ptr<Source>*>(source);
            const boost::shared_ptr<Target> target     = boost::dynamic_pointer_cast<Target>(*sourcePtr);
            if(reinterpret_cast<size_t>(target.get()) == reinterpret_cast<size_t>(sourcePtr->get()))
                return source;
            else
            {
                // assertion which is triggered when multi-inheritance is used for Source type
                // in this case it is necessary to create new instance of shared_ptr<Target> but
                // it is not possible to make it in-place due to memory leak
                assert(!"Wrong cast");
                return nullptr;
            }
        }
    };

    template<typename Source, typename Target>
    struct cast_generator<boost::shared_ptr<Source>, boost::shared_ptr<Target> >
    {
        typedef shared_ptr_cast_generator<Source, Target> type;
    };
}}}

By providing such code, it is possible to adjust second export to the python: class_, wrapped_shared_ptr, noncopyable, bases > 通过提供这样的代码,可以调整第二次导出到python:class_,wrapped_shared_ptr,noncopyable,bases>

Just be carefull with the conversion stored in execute function - if conversion between Source and Target exists, it returns same address - so it must be also valid if you just reinterpret Source* to Target* (data in both classes must be stored at exactly same places). 只需要小心执行函数中存储的转换 - 如果Source和Target之间存在转换,它将返回相同的地址 - 因此如果只是将Source *重新解释为Target *,它必须也是有效的(两个类中的数据必须完全相同)地方)。

Maybe such solution is not sufficient in your case, but at least it can give you some ideas. 也许这种解决方案在你的情况下是不够的,但至少它可以给你一些想法。

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

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