简体   繁体   English

当C ++方法返回std :: map时,boost-python <string,X*>

[英]boost-python when C++ method returns std::map<string,X*>

I'm exposing an API to Python, written in C++ that I have no access to change, using Boost Python. 我正在向Python公开一个API,用C ++编写,我无法使用Boost Python进行更改。

I have successfully exposed methods returning references to a std:map where the key,value pairs are value types - eg: 我已经成功地公开了返回对std:map的引用的方法,其中键,值对是值类型 - 例如:

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

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: 但是当试图获得类似的结果时,地图值是指向我在API中公开的类的指针不起作用:

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
}

Error seen in g++ 7.3.0: 在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’

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. 我理解编译器为什么抱怨 - 地图中的X*需要包含在调用策略中,以便它可以返回到Python,就像返回原始指针的基本方法一样。

My question is what is the best way to do this? 我的问题是,最好的方法是什么?

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 . 从谷歌搜索中我可以指定一个DerivedPolicies子类map_indexing_suite ,它将重载必要的部分以将X *包装在适当的return_value_policy However so far I've be unsuccessful in putting anything together that the compiler doesn't bawk at! 但是到目前为止,我还没有成功地将编译器放在一起的东西放在一起!

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! 我还怀疑我可以复制并粘贴整个map_indexing_suite并重命名它,并在其中进行更改以生成具有正确return_value_policy的新indexing_suite ,但与使用DerviedPolicies的解决方案相比,这看起来很难DerviedPolicies - 假设我是对的DeriviedPolicies可以使用!

Any help, pointers, or examples gratefully received! 感激地收到任何帮助,指示或示例!

EDIT 编辑

I have proved that the cut-and-paste option works with a single trivial change of is_class to is_pointer . 我已经证明了cut-and-paste选项适用于is_classis_pointer的一个简单的改变。 It's curious that is_pointer is not allowed in the original as the target policy can handle pointers. 奇怪的是is_pointer中不允许使用is_pointer ,因为目标策略可以处理指针。 I'm yet to convince myself that it's an object lifetime restriction that means pointers are not allowed in the original? 我还没有说服自己这是一个对象的生命周期限制,这意味着原始版本中不允许使用指针?

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? 全班是公开的,所以我怀疑只需继承map_indexing_suite或者使用神秘的DerivedPolicies参数就可以避免完全剪切和粘贴?

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

EDIT 2 编辑2

Now see answer 现在看看答案

Slightly cleaner implementation from the cut-and-paste is to inherit map_indexing_suite - a few tweaks are needed to make this work. 剪切和粘贴的实现稍微清晰,就是继承map_indexing_suite - 需要进行一些调整才能完成这项工作。

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... 这似乎是合情合理的 - 如果有人使用更简洁的解决方案,或者可以更好地解释DerivedPolicies那么我会在几天左右接受以下作为答案......

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

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

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