我正在尝试使用Cython从我的Python代码中获取C ++代码列表。
这是我的标题(ex.h):
namespace c
{
class C {
int id;
public:
C(int id) {
this->id = id;
}
int getId();
};
typedef std::vector<C> CList;
}
和我的源文件(ex.cpp):
namespace c
{
int C::getId() {
return this->id;
}
CList getlist(int t) {
CList list;
for (int i = 0; i < t; ++i) {
C c(i);
list.push_back(c);
}
return list;
}
}
所以在我的Python代码中,我想像这样迭代CList:
import exapi
for i in exapi.take_list():
print i
# OR
print i.get_id()
在.pyx文件(exapi.pyx)中我正在尝试创建Cython扩展来处理C ++对象:
cdef extern from "ex.h" namespace "c":
cdef cppclass C:
C(int) except +
int getId()
cdef cppclass CList:
pass
cdef CList getlist(int)
cdef class PY_C:
pass
cdef class PY_CList:
pass
def take_list():
return getlist(10) # I know this is wrong
我应该向PY_C和PY_Clist添加哪些转发方法和函数才能使我的python代码正常工作?
我读了Using C++ in Cython,但是在Cython代码中创建了新对象,但在我的情况下,我需要使用在C ++端创建的对象。
答案 0 :(得分:2)
当试图解决这个问题时,我重写了一些例子,但原则上你给的东西和我给的东西之间没有区别。
我也更改了一些名字,因为那些例子对我来说太短了;)。
cobject.hpp
#include <vector>
namespace cobject {
class CObject {
int id;
public:
CObject(int id): id(id) {}
int getId();
};
typedef std::vector<CObject> CObjectList;
CObjectList getlist(int t);
}
cobject.cpp
#include "cobject.hpp"
namespace cobject {
int CObject::getId() {
return this->id;
}
CObjectList getlist(int t) {
CObjectList list;
for (int i = 0; i < t; ++i) {
list.push_back(CObject(i));
}
return list;
}
}
这些并没有什么不同。
我想使用pyximport
,但要将它与C ++一起使用,您需要一个特殊的文件:
cobject_api.pyxbld
import os
from distutils.extension import Extension
dirname = os.path.dirname(__file__)
def make_ext(modname, pyxfilename):
return Extension(
name=modname,
sources=[pyxfilename, "cobject.cpp"],
language="c++",
include_dirs=[dirname]
)
然后是魔术:
cython_api.pyx
from cython.operator cimport dereference as deref
cdef extern from "cobject.hpp" namespace "cobject":
cdef cppclass CObject:
CObject(int) except +
int getId()
cdef cppclass CObjectList:
CObject &at(size_t)
size_t size()
cdef CObjectList getlist(int)
# Cython wrappers
cdef class PyCObject:
cdef CObject *wrapped
cdef object keepalive
def __cinit__(self, keepalive):
self.keepalive = keepalive
property id:
def __get__(self):
return self.wrapped.getId()
def __repr__(self):
return "PyCObject({})".format(self.id)
cdef class PyCObjectList:
cdef CObjectList wrapped
def __iter__(self):
cdef PyCObject pycobject
cdef size_t idx = 0
while idx < self.wrapped.size():
pycobject = PyCObject(self)
pycobject.wrapped = &self.wrapped.at(idx)
yield pycobject
idx += 1
def take_list():
cdef PyCObjectList pycobjectlist = PyCObjectList()
pycobjectlist.wrapped = getlist(10)
return pycobjectlist
运行方式如下:
main.py
import pyximport
pyximport.install()
import cobject_api
for i in cobject_api.take_list():
print("{} has id {}".format(i, i.id))
这个想法是你的包装类获取数据的所有权。由于getlist
按值返回,您必须复制它,但另一个对象包装器可以使用指针。
使用pre-wrapped vector class并对其进行迭代可能更有意义,而不是使用CObjectList
类型。但是,除非你使用指向间接的指针保留堆上的所有内容,否则包装它们并不好。
这是因为你不能轻易指出你保留在堆栈中的东西,如果Cython想要与Python的垃圾收集器配合,就需要指向它。这解释了代码中keepalive
变量的用途:与在Python中删除向量不影响其元素的内容不同,在C ++中完全破坏它们。使用指针向量,可以缓解这些问题。
Also see this link on another question.已经提出了一些相同的观点,并给出了更传统设置的架构。