简体   繁体   English

使用C ++和Python访问数组

[英]Access an Array with both C++ and Python

I am working on a simulation of a mini-galaxy consisting of about a 100,000 stars. 我正在模拟一个由约100,000个恒星组成的迷你星系。 The visual representation I want to do in Python, the large calculations in C++. 我想在Python中进行视觉表示,在C ++中进行大量计算。 Using ctypes, I am able to call a C++ function from Python. 使用ctypes,我可以从Python调用C ++函数。

What I basicly want is an array that is there in the RAM, that can be accessed both by python and C++. 我基本上想要的是RAM中存在的数组,可以由python和C ++访问。 Then upon calling a function update() in python, C++ updates the array. 然后,在python中调用函数update()时,C ++将更新数组。 It is important that C++ really only changes the values in the array. 重要的是,C ++实际上仅更改数组中的值。 Copying it all the time would become quite time consuming. 一直进行复制将非常耗时。

I am quite a beginner, especially in C++, So I don't really know where to find the right information, and what keywords to use. 我是一个初学者,尤其是在C ++中,所以我真的不知道在哪里可以找到正确的信息以及要使用的关键字。 Thoughts on how to do it are of course welcome, but some informational links would also be greatly appreciated. 当然欢迎提出有关如何做的想法,但是一些信息链接也将不胜感激。

Best, 最好,

You can build C++ python wrapper module using the python C/C++ API: 您可以使用python C / C ++ API构建C ++ python包装器模块:

https://docs.python.org/2/extending/extending.html https://docs.python.org/2/extending/extending.html

I would create a C++ module ( dataUpdater ) using python API offering a service, lets call it, update which should receive the Python object you want to load data into. 我将使用提供服务的python API创建一个C ++模块( dataUpdater ),让我们对其进行调用, update ,该操作应该会接收要将数据加载到其中的Python对象。

At your Python side I would call dataUpdater.update whenever I want to load the data from C++ 在您的Python端,每当我想从C ++加载数据时,我都会调用dataUpdater.update

EDIT: 编辑:

Other option is to make your C++ module to behave like a data structure offering data access services such as: 另一种选择是使您的C ++模块的行为类似于提供数据访问服务的数据结构,例如:

  • getValueAt(index)
  • setValueAt(index)
  • getSize()

And using it at python side: 并在python端使用它:

for i in xrange(dataUpdater.getSize()):
    val = dataUpdater.getValueAt(i)
    ...

You should totally check the Python documentation on this issue: 您应该完全检查有关此问题的Python文档:

https://docs.python.org/2/extending/ https://docs.python.org/2/extending/

Having the doc in mind you can define a new Type; 考虑到文档,您可以定义一个新的Type; assuming that stars would be a double precision array: 假设星星将是双精度数组:

typedef struct {
    PyObject_HEAD
    double * Stars;
} Galaxy;

Then define the Math operations method ... (python doc) 然后定义数学运算方法...(python doc)

static PyObject* Galaxy_calc(Galaxy *self, PyObject *args)
{
     double * Star_temp;   

     /* Your Array is referenced by self->Stars*/
     Star_temp = self->Stars;

     /* Do the math in C++ */
     // All necessary calculations go here.
};

It is fairly easy to include these methods in the new type defined (Galaxy), you just have to set the variables: 将这些方法包含在新定义的类型(Galaxy)中相当容易,您只需设置变量即可:

static PyMethodDef Galaxy_methods[] = 
{
    {"calc", (PyCFunction)Galaxy_calc, METH_VARARGS,"Performs stelar calculations."},
    {NULL}  /* Sentinel */
};


static PyMemberDef Galaxy_members[] = 
{    {"Stars", T_OBJECT_EX, offsetof(Galaxy, Galaxy), 0, "Galaxy Stars"},
    {NULL}  /* Sentinel */
};

Now just include the Galaxy_methods var on the adequate position under 现在只需将Galaxy_methods var包含在下面的适当位置

static PyTypeObject Galaxy_GalaxyType = {
    PyObject_HEAD_INIT(NULL)
    0,                         /*ob_size*/
    "Galaxy.Galaxy ",           /*tp_name*/
    sizeof(Galaxy),            /*tp_basicsize*/
    0,                         /*tp_itemsize*/
    (destructor)Galaxy_dealloc, /*tp_dealloc*/
    0,                         /*tp_print*/
    0,                         /*tp_getattr*/
    0,                         /*tp_setattr*/
    0,                         /*tp_compare*/
    0,                         /*tp_repr*/
    0,                         /*tp_as_number*/
    0,                         /*tp_as_sequence*/
    0,                         /*tp_as_mapping*/
    0,                         /*tp_hash */
    0,                         /*tp_call*/
    0,                         /*tp_str*/
    0,                         /*tp_getattro*/
    0,                         /*tp_setattro*/
    0,                         /*tp_as_buffer*/
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,          /*tp_flags*/
    "Galaxy objects",          /* tp_doc */
    0,                         /* tp_traverse */
    0,                         /* tp_clear */
    0,                         /* tp_richcompare */
    0,                         /* tp_weaklistoffset */
    0,                         /* tp_iter */
    0,                         /* tp_iternext */
    Galaxy_methods,            /* tp_methods */
    Galaxy_members,            /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
   (initproc)Galaxy_init,      /* tp_init */
    0,                         /* tp_alloc */
    Galaxy_new,                /* tp_new */
};

Use python documentation refered above to implement new, alloc, dealloc and init methods (these are quite simple) and it is done! 使用上面提到的python文档来实现新的,alloc,dealloc和init方法(这些非常简单),就可以了!

Doing this correctly is actually quite complicated. 正确执行此操作实际上非常复杂。 First, you should use the package numpy for the array in Python. 首先,您应该在python中使用numpy包作为数组。 Then, you would define a C interface, as described in https://docs.python.org/2/c-api/ . 然后,您将定义一个C接口,如https://docs.python.org/2/c-api/中所述 (This is a reference manual, so you may want to read, and experiment with https://docs.python.org/2/extending/index.html first.) Most importantly, you will want to use the buffer interface ( https://docs.python.org/2/c-api/buffer.html#bufferobjects ) to access the numpy arrays. (这是参考手册,因此您可能要先阅读并尝试使用https://docs.python.org/2/extending/index.html 。)最重要的是,您将需要使用缓冲区接口( https ://docs.python.org/2/c-api/buffer.html#bufferobjects )访问numpy数组。

ctypes seems to have some support for contiguous arrays as well, but I've no experience with it. ctypes似乎也对连续数组有一些支持,但是我没有经验。 If you do any processing of the arrays on the Python side, however, you'll want to use numpy , which I don't think ctypes will support. 但是,如果您在Python端对数组进行任何处理,则需要使用numpy ,我认为ctypes不支持。

Here is how another proposal on how to accomplish this task, using Boost.Python. 这是关于如何使用Boost.Python完成此任务的另一个建议。

Let's organize the code in 3 files: a setup.py to take care of compiling the extension code, a Python script that just uses the extension code, and the extension code itself: 让我们将代码组织成3个文件: setup.py (负责编译扩展代码),仅使用扩展代码的Python脚本以及扩展代码本身:

.
├── galaxy.cpp
├── main.py
└── setup.py

galaxy.cpp : note that exceptions are not handled, so you can produce a segmentation fault by assigning to a Star that has not been initialized, and other C++ oddities. galaxy.cpp :请注意, galaxy.cpp异常,因此,可以通过分配给尚未初始化的Star和其他C ++ galaxy.cpp来产生分段错误。 If you modify this code, take care to always name the BOOST_PYTHON_MODULE as the file itself. 如果您修改此代码,请务必始终将BOOST_PYTHON_MODULE命名为文件本身。

#include <vector>

#include <boost/python.hpp>

class Star {
public:
    Star(double mass): mass(mass) {}

    bool set_mass(double given_mass) {
        this->mass = given_mass;
        return true;
    }
private:
    double mass;
};

class Galaxy {
public:
    Galaxy(const boost::python::list& masses) {
        for (size_t i = 0; i < len(masses); i++) {
            double mass = boost::python::extract<double>(masses[i]);
            stars.push_back(Star(mass));
        }
    }

    bool update(int star_number, double mass) {
        return this->stars[star_number].set_mass(mass);
    }

private:
    std::vector<Star> stars;
};

BOOST_PYTHON_MODULE(galaxy)
{
    using namespace boost::python;
    class_<Galaxy>("Galaxy", init< boost::python::list >())
        .def("update", &Galaxy::update)
    ;
}

setup.py : note that Boost has been installed on my machine using Macports; setup.py :请注意,Boost已使用Macports安装在我的机器上; you may need to adjust the path where it can be found, in the include_dirs variable. 您可能需要在include_dirs变量中调整可以找到它的路径。

from distutils.core import setup
from distutils.extension import Extension

setup(name="galaxies",
      ext_modules=[
          Extension(
              "galaxy", ["galaxy.cpp"],
              include_dirs=["/opt/local/include"],
              libraries=["boost_python-mt"])])

Finally, use the Galaxy object to do whatever you need in main.py . 最后,使用Galaxy对象在main.py执行所需的任何main.py Note that in this example the object is constructed from a Python list (which means you are actually passing the array at least once between Python and C++), but this is not a must: you can have the C++ code read a file of data, and just pass its path from Python. 请注意,在此示例中,该对象是通过Python列表构建的(这意味着您实际上实际上是在Python和C ++之间至少传递了一次数组),但这不是必须的:您可以让C ++代码读取数据文件,并通过Python传递其路径。

import galaxy

sombrero = galaxy.Galaxy([0.1, 22.3, 33.4])
sombrero.update(0, 24.5)

Here is how to compile and run the example: 这是编译和运行示例的方法:

$ python setup.py build_ext --inplace && python main.py

And there is always this: 总是这样:

http://www.boost.org/doc/libs/1_55_0/libs/python/doc/ http://www.boost.org/doc/libs/1_55_0/libs/python/doc/

"Welcome to version 2 of Boost.Python, a C++ library which enables seamless interoperability between C++ and the Python programming language." “欢迎使用Boost.Python版本2,该版本是一个C ++库, 在C ++和Python编程语言之间实现无缝的互操作性 。”

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

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