boost-python, когда метод C ++ возвращает std :: map - PullRequest
3 голосов
/ 27 марта 2019

Я предоставляю API для Python, написанного на C ++, для которого у меня нет доступа к изменениям, используя Boost Python.

Я успешно представил методы, возвращающие ссылки на std: map, где ключ, значениепары являются типами значений - например:

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

Это работает без проблем.Но при попытке получить аналогичный результат, когда значения карты являются указателями на классы, которые я представил в 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
}

Ошибка, обнаруженная в 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, как и в случае базового метода, который возвращает необработанный указатель.

У меня вопрос, каков наилучший способ сделать это?

Из Googling мне кажется, что я могу указать DerivedPolicies дочерний класс map_indexing_suite, который будет перегружать необходимые части для переносаХ * в соответствующем return_value_policy.Однако до сих пор мне не удалось собрать что-нибудь такое, на что компилятор не ругается!

Я также подозреваю, что могу буквально скопировать и вставить весь map_indexing_suite и переименовать его, и сделатьизменения в нем дают новый indexing_suite с правильным return_value_policy, но это выглядит некрасиво по сравнению с решением, использующим DerviedPolicies - при условии, что я прав, что DeriviedPolicies можно использовать вообще!

Любая помощь, указатели или примеры с благодарностью получены!

РЕДАКТИРОВАТЬ

Я доказал, что опция вырезать и вставить работает с одним тривиальным изменением is_classдо is_pointer.Любопытно, что is_pointer не допускается в оригинале, так как целевая политика может обрабатывать указатели.Мне еще предстоит убедить себя, что это ограничение срока службы объекта, означающее, что указатели не допускаются в оригинале?

Весь класс является общедоступным, поэтому я подозреваю, что можно избежать полной вырезки и вставки, простонаследование от 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)
        ;
    }

РЕДАКТИРОВАТЬ 2

Теперь смотрите ответ

1 Ответ

0 голосов
/ 29 марта 2019

Немного более чистая реализация от вырезания и вставки заключается в наследовании map_indexing_suite - для этой работы требуется несколько настроек.

Это кажется разумно разумным - если кто-то вмешивается в более аккуратное решение или может лучшеобъясните 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)
            ;
    }
};
...