如何返回std :: vector< const T *>到python? [英] How to return a std::vector<const T*> to python?

查看:60
本文介绍了如何返回std :: vector< const T *>到python?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个c ++ 11函数,返回一个:

I have a c++11 function that returns a:

std::vector<const T*> f();

T是我通过class_暴露给python的c ++类.所有T实例都驻留在静态存储中,该存储将一直存在于python进程的整个过程中.

with T being a c++ class that I exposed to python with class_. All the T instances reside in static storage that will live throught the live of the python process.

我正在尝试将f作为python函数公开

I am trying to expose f as a python function

getAllTs()

这将返回T周围的python对象包装.我选择T *作为class_的保留类型.

that would return python objects wrappers around T. I chose T* to be the held type for class_.

我正在使用此错误的半泛型函数将std :: vector转换为python元组:

I am converting std::vector to a python tuple, with this bad semi-generic function:

template <typename Cont>
struct stdcont_to_python_tuple
{
  static PyObject* convert(const Cont& container)
  {
    boost::python::list lst;
    for (const auto& elt: container)
      lst.append(elt);

    return boost::python::incref( boost::python::tuple(lst).ptr() );
  }

  static PyTypeObject const* get_pytype()
  {
    return &PyTuple_Type;
  }
};

我无法从容器构建元组目录.有可能吗?

I couldn't construct the tuple directory from the container. Is that possible?

我需要在UI表中显示这些T,进行排序,过滤.

I need to display these Ts in a UI table, perform sorting, filtering.

T实例的最大数量为30000个奇数.在c ++ 11中:

The max number of T instances is 30000 odd. In c++11:

sizeof(T) = 24 bytes

在python3中:

sys.getsizeof(t) = 72 bytes

在对getAllTs进行定义以最大程度减少重复的情况下(例如,用python添加最少的额外内容),我可以使用什么退货政策?

What is the return policy I can use where def'ing getAllTs to minimize duplication, ie to have the least extras added by python?

谢谢

推荐答案

std::vector<const T*>暴露于Python

公开std::vector<...>的最简单方法是将其公开为

Exposing std::vector<const T*> to Python

The easiest way to expose std::vector<...> is to expose it as a boost::python::class_, and use the vector_indexing_suite to provide a Python sequence like interface.

std::vector<const spam*> get_spams() { ... }

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;

  // Expose `spam`
  python::class_<spam, spam*>("Spam");

  // Expose `std::vector<const spam*>`
  python::class_<std::vector<const spam*>>("Spams")
    .def(python::vector_indexing_suite<std::vector<const spam*>>())
    ;

  python::def("get_spams", &get_spams);
}

但是,使用类型const spam*可能并不如人们希望的那样富有成果.首先,Python没有const的概念.此外,当将spam类公开为由spam*保留时,自动to-python和from-python转换器用于spam*,而不是const spam*.由于索引套件在索引期间返回了代理,因此这可能不会立即显现出来,但是在迭代时会变得显而易见,因为迭代器的值将尝试转换为Python.

However, using the type const spam* may not be as fruitful as one would hope. First, Python does not have the concept of const. Furthermore, when exposing the spam class as being held by spam*, the automatic to-python and from-python converter are for spam*, not const spam*. This may not be immediately apparent due to the indexing suite returning proxies during indexing, but it will become apparent when iterating, as the iterator's value will attempt to be converted to Python.

spams = example.get_spams()
# Access by index returns a proxy.  It does not perform a
# spam-to-python conversion.
spams[0].perform()
# The iterator's value will be returned, requiring a 
# spam-to-python conversion.
for spam in spams:
    pass

要解决此问题,可以为const spam*注册一个显式的python转换.如果需要const spam*,或者如果将spam公开为由其他类型持有会更容易(例如,使用空删除器的boost::shared_ptr),我将强烈考虑重新检查.无论如何,这是一个完整的示例展示此功能:

To resolve this, one can register an explicit to-python conversion for const spam*. I would strongly consider re-examining if const spam* is necessary, or if exposing spam as being held by a different type would be easier (e.g. boost::shared_ptr with a null deleter). Regardless, here is a complete example demonstrating this functionality:

#include <iostream>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>

/// Mocks...
struct spam
{
  spam() { std::cout << "spam() " << this << std::endl; }
  ~spam() { std::cout << "~spam() " << this << std::endl; }
  void perform() { std::cout << "spam::perform() " << this << std::endl; }
};

namespace {
  std::array<spam, 3> spams;
} // namespace

std::vector<const spam*> get_spams()
{
  std::vector<const spam*> result;
  for (auto& spam: spams)
  {
    result.push_back(&spam);
  }
  return result;
}

/// @brief Convert for converting `const spam*` to `Spam`.
struct const_spam_ptr_to_python
{
  static PyObject* convert(const spam* ptr)
  {
    namespace python = boost::python;
    python::object object(python::ptr(ptr));
    return python::incref(object.ptr());
  }
};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;

   // Enable `const spam*` to `Spam` converter.
  python::to_python_converter<const spam*, const_spam_ptr_to_python>();

  // Expose `spam`.
  python::class_<spam, spam*>("Spam", python::no_init)
    .def("perform", &spam::perform)
    ;

  // Expose `std::vector<const spam*>`.
  python::class_<std::vector<const spam*>>("Spams")
    .def(python::vector_indexing_suite<std::vector<const spam*>>())
    ;

  python::def("get_spams", &get_spams);
}

互动用法:

>>> import example
spam() 0x7ffbec612218
spam() 0x7ffbec612219
spam() 0x7ffbec61221a
>>> spams = example.get_spams()
>>> for index, spam in enumerate(spams):
...     spams[index].perform()
...     spam.perform()
...
spam::perform() 0x7ffbec612218
spam::perform() 0x7ffbec612218
spam::perform() 0x7ffbec612219
spam::perform() 0x7ffbec612219
spam::perform() 0x7ffbec61221a
spam::perform() 0x7ffbec61221a
~spam() 0x7ffbec61221a
~spam() 0x7ffbec612219
~spam() 0x7ffbec612218


从C ++容器构造Python元组

A boost::python::tuple 可以从一个序列构造.如果提供了C ++对象,则需要将其转换为实现Python迭代器协议的Python类型.例如,在上面的示例中,由于std::vector<const spam*>被公开并通过vector_indexing_suite提供了类似序列的接口,因此可以将get_spams()编写为:


Construct Python tuple from C++ container

A boost::python::tuple can be constructed from a sequence. If a C++ object is provided, then it needs to be convertible to a Python type that implements the Python iterator protocol. For instance, in the above example, as std::vector<const spam*> is exposed and provides a sequence like interface via the vector_indexing_suite, one could write get_spams() as:

boost::python::tuple get_spams()
{
  std::vector<const spam*> result;
  for (auto& spam: spams)
  {
    result.push_back(&spam);
  }
  return boost::python::tuple(result);
}

或者,可以使用 boost/python/iterator.hpp 文件可从C ++容器或迭代器创建Python迭代器.这些示例演示了将std::pair迭代器(开始和结束)公开给Python.由于std::pair将提供到python的转换,因此可以从std::pair构建boost::python::tuple.

Alternatively, one can use types and functions provided by the boost/python/iterator.hpp file to create Python iterators from C++ Containers or Iterators. The examples demonstrate exposing a std::pair of iterators, begin and end, to Python. As the std::pair will have a to-python conversion available, one could construct a boost::python::tuple from the std::pair.

这是一个竞争示例演示这种方法:

Here is a compete example demonstrating this approach:

#include <boost/python.hpp>

/// @brief Returns a tuple, constructing it form a range.
template <typename Container>
boost::python::tuple container_to_tuple(Container& container)
{
  namespace python = boost::python;
  return python::tuple(std::make_pair(
      container.begin(), container.end()));
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;

  // Expose an int range.
  typedef std::vector<int>::iterator vector_int_iterator;
  typedef std::pair<vector_int_iterator, vector_int_iterator> vector_int_range;
  python::class_<vector_int_range>("IntRange")
    .def("__iter__", python::range(
        &vector_int_range::first, &vector_int_range::second))
      ;

   // Return a tuple of ints.
  python::def("get_ints", +[] {
    std::vector<int> result;
    result.push_back(21);
    result.push_back(42);
    return container_to_tuple(result);
  });
}

互动用法:

>>> import example
>>> ints = example.get_ints()
>>> assert(isinstance(ints, tuple))
>>> assert(ints == (21, 42))


内存占用量

如果C ++对象已经存在,则可以让python::object通过指针对其进行引用,这将减少重复的总内存使用量.但是,没有任何选项可以减少Boost.Python类的实例的基本占用空间,这些类来自于新型类,可变长度数据的大小,C ++对象,vtable指针,指向实例持有人的指针以及实例持有人的填充结盟.如果需要较小的空间,请考虑直接使用Python/C API创建类型,并使用Boost.Python与对象进行交互.


Memory Footprint

If the C++ object already exists, one can have the python::object reference it via a pointer, which will reduce duplicating some overall memory usage. However, there are no options to reduce the base footprint of instances for Boost.Python classes, which comes from new-style class, size for variable-length data, C++ object, vtable pointer, pointer to instance holder, and padding for instance holder alignment. If you need a smaller footprint, then consider using the Python/C API directly for type creation, and using Boost.Python for interacting with the objects.

这篇关于如何返回std :: vector&lt; const T *&gt;到python?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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