1

使用 boost.python 为现有库创建 python 绑定时遇到问题。场景如下:

#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");
}

(抱歉,代码块很长,这是我能想到的最小示例。)

您可以看到我必须重写getString()BaseWrapper 中的方法,以便它可以与 Python 字符串一起使用,并且这部分工作正常:

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

一旦我尝试getString从以下实例调用,问题就会出现Derived

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

我可以理解这里出了什么问题,但我不知道如何解决这个问题。我将不胜感激任何帮助!

最好的问候, eDude

4

2 回答 2

2

问题是,这DerivedWrapperBaseWrapper. 因此 DerivedWrapper 需要提供自己的 Python 适配实现void getString(bp::object pyObj)

所以让它工作的一种方法是这样的:

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

和输出

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

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

正如预期的那样

Got string: "bla"
Got string: "(from DerivedWrapper) blub"
于 2012-09-04T07:46:44.763 回答
1

我遇到了与我设法解决的完全相同的问题,但是在某些特殊情况下,解决方案存在一些内部故障。当需要从 boost::python 传递的值创建 boost::weak_ptr 引用时,在 python 中使用 boost::shared_ptr 存在一个已知问题。由于与帖子无关,我不会详细说明。无论如何,我需要将 boost::shared_ptr 包装到另一个类中(我称之为 PythonSharedPtr)以从 boost::python 中隐藏 boost::shared_ptr,然后我遇到了类似的问题。考虑以下设置: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

那么我需要编写正确的导出到boost python:class_,wrapped_shared_ptr,noncopyable>和class_,wrapped_shared_ptr,noncopyable>

我认为到目前为止它与您的代码相似。棘手的部分是如何允许在 boost::python 中使用基数来导出 B(而 shared_ptr 与 shared_ptr 无关)。经过对 boost::python 源代码的一些研究,并附带了非常好的解决方案,但它仅适用于 B 类没有多重继承的情况。

这是代码:

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;
    };
}}}

通过提供这样的代码,可以调整第二次导出到 python:class_、wrapped_shared_ptr、noncopyable、bases >

请注意存储在执行函数中的转换 - 如果 Source 和 Target 之间存在转换,则它返回相同的地址 - 因此,如果您只是将 Source* 重新解释为 Target*,它也必须有效(两个类中的数据必须存储在完全相同的位置)地方)。

也许这样的解决方案在你的情况下是不够的,但至少它可以给你一些想法。

于 2013-03-22T16:57:38.507 回答