当C ++方法返回std :: map< string,X *>时升压python [英] boost-python when C++ method returns std::map<string,X*>

查看:95
本文介绍了当C ++方法返回std :: map< string,X *>时升压python的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Boost Python向API公开用C ++编写的,我无法更改的API.

I'm exposing an API to Python, written in C++ that I have no access to change, using Boost Python.

我已经成功地公开了返回对std:map的引用的方法,其中键,值对是值类型-例如:

I have successfully exposed methods returning references to a std:map where the key,value pairs are value types - eg:

class_< std::map<std::string, std::string> >("StringMap")
    .def(map_indexing_suite< std::map<std::string, std::string>, true >());

这是无缝的.但是,当试图获得相似的结果(映射值是指向我在API中公开的类的指针)时,此方法不起作用:

This works seamlessly. But when trying to achieve a similar result where the map values are pointers to classes I've exposed within the API doesn't work:

struct X_wrap : X, wrapper<X>
{
     X_wrap(int i): X(i) {}
    // virtual methods here, omitted for brevity - as unlikely to be the issue
}

BOOST_PYTHON_MODULE(my_py_extension)
{

    class_< std::map<std::string, X*> >("XPtrMap")
        .def(map_indexing_suite< std::map<std::string, X*> >());

    class_<X_wrap, boost::noncopyable, bases<XBase> >("X", init<int>())

   // other definitions omitted
}

在g ++ 7.3.0中看到的错误:

Error seen in g++ 7.3.0:

/usr/include/boost/python/detail/caller.hpp:100:98: error: ‘struct boost::python::detail::specify_a_return_value_policy_to_wrap_functions_returning<X*>’ has no member named ‘get_pytype’

我理解编译器为何会抱怨-映射中的 X * 需要包装在调用策略中,以便可以将其返回给Python,就像使用返回a的基本方法一样原始指针.

I understand why the compiler is complaining - the X* in the map needs to be wrapped in a call policy so that it can be returned to Python, just like with a basic method that returns a raw pointer.

我的问题是最好的方法是什么?

My question is what is the best way to do this?

从谷歌搜索中发现,我也许可以指定 map_indexing_suite DerivedPolicies 子类,该子类将重载必要的部分以将X *包装在适当的 return_value_policy中.但是到目前为止,我无法将编译器不熟悉的所有内容放在一起!

From Googling it strikes that I can perhaps specify a DerivedPolicies child class of map_indexing_suite that will overload the necessary parts to wrap the X* in an appropriate return_value_policy. However so far I've be unsuccessful in putting anything together that the compiler doesn't bawk at!

我还怀疑我可以从字面上复制并粘贴整个 map_indexing_suite 并将其重命名,然后在其中进行更改以使用正确的生成新的 indexing_suite > return_value_policy ,但是与使用 DerviedPolicies 的解决方案相比,这似乎很难看-假设我完全可以使用 DeriviedPolicies

I also suspect I can literally copy-and-paste the whole map_indexing_suite and rename it, and make the changes therein to produce a new indexing_suite with the right return_value_policy, but this seems ugly compared to the solution using DerviedPolicies - assuming I'm right that DeriviedPolicies can be used at all!

非常感谢收到的任何帮助,指针或示例!

Any help, pointers, or examples gratefully received!

编辑

我证明了剪切粘贴选项仅需将 is_class 更改为 is_pointer 即可.奇怪的是原始代码中不允许 is_pointer ,因为目标策略可以处理指针.我尚未说服自己,这是对象生命周期的限制,也就是说原始对象中不允许使用指针吗?

I have proved that the cut-and-paste option works with a single trivial change of is_class to is_pointer. It's curious that is_pointer is not allowed in the original as the target policy can handle pointers. I'm yet to convince myself that it's an object lifetime restriction that means pointers are not allowed in the original?

整个类都是公共的,所以我怀疑是否可以通过仅继承 map_indexing_suite 或使用神秘的 DerivedPolicies 参数来避免完全剪切粘贴?

The whole class is public so I suspect it's possible to avoid the full cut-and-paste by simply inheriting from map_indexing_suite or perhaps by using the mysterious DerivedPolicies parameter?

    extension_def(Class& cl)
    {
        //  Wrap the map's element (value_type)
        std::string elem_name = "mapptr_indexing_suite_";
        object class_name(cl.attr("__name__"));
        extract<std::string> class_name_extractor(class_name);
        elem_name += class_name_extractor();
        elem_name += "_entry";

        typedef typename mpl::if_<
            mpl::and_<is_pointer<data_type>, mpl::bool_<!NoProxy> >
          , return_internal_reference<>
          , default_call_policies
        >::type get_data_return_policy;

        class_<value_type>(elem_name.c_str())
            .def("__repr__", &DerivedPolicies::print_elem)
            .def("data", &DerivedPolicies::get_data, get_data_return_policy())
            .def("key", &DerivedPolicies::get_key)
        ;
    }

编辑2

现在查看答案

推荐答案

剪切和粘贴过程中较干净的实现是继承map_indexing_suite-需要进行一些调整才能使这项工作生效.

Slightly cleaner implementation from the cut-and-paste is to inherit map_indexing_suite - a few tweaks are needed to make this work.

这似乎是合理的-如果有人采用更整洁的解决方案,或者可以更好地解释 DerivedPolicies ,那就太好了,否则我将在几天左右的时间内接受以下内容作为答案...

This seems reasonably sensible - if someone chimes in with a neater solution or can better explain DerivedPolicies then great, otherwise I'll accept the below as the answer in a few days or so...

using namespace boost;
using namespace boost::python;

//Forward declaration
template <class Container, bool NoProxy, class DerivedPolicies>
class mapptr_indexing_suite;

template <class Container, bool NoProxy>
class final_mapptr_derived_policies
    : public mapptr_indexing_suite<Container,
        NoProxy, final_mapptr_derived_policies<Container, NoProxy> > {};

template <
    class Container,
    bool NoProxy = false,
    class DerivedPolicies
        = final_mapptr_derived_policies<Container, NoProxy> >
class mapptr_indexing_suite
    : public map_indexing_suite<
    Container,
    NoProxy,
    DerivedPolicies
    >
{
public:
    // Must be explicit if the compiler is
    // going to take from the base class
    using typename map_indexing_suite<
        Container,NoProxy,DerivedPolicies>::data_type;
    using typename map_indexing_suite<
        Container,NoProxy,DerivedPolicies>::value_type;

    // Only one class needs to be overridden from the base
    template <class Class>
    static void
    extension_def(Class& cl)
    {
        //  Wrap the map's element (value_type)
        std::string elem_name = "mapptr_indexing_suite_";
        object class_name(cl.attr("__name__"));
        extract<std::string> class_name_extractor(class_name);
        elem_name += class_name_extractor();
        elem_name += "_entry";

        // use of is_pointer here is the only
        // difference to the base map_indexing_suite
        typedef typename mpl::if_<
            mpl::and_<std::is_pointer<data_type>, mpl::bool_<!NoProxy> >
            , return_internal_reference<>
            , default_call_policies
            >::type get_data_return_policy;

        class_<value_type>(elem_name.c_str())
            .def("__repr__", &DerivedPolicies::print_elem)
            .def("data", &DerivedPolicies::get_data, get_data_return_policy())
            .def("key", &DerivedPolicies::get_key)
            ;
    }
};

这篇关于当C ++方法返回std :: map&lt; string,X *&gt;时升压python的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆