Cython:避免通过std :: move复制不起作用 [英] Cython: Avoid copy through std::move not working
问题描述
问题
我有一个很大的 std :: vector
从C ++函数返回,让我们称它为 getVector()
。
现在我想将该功能包装在Cython中:
cdef来自 XY.h的外部变量:
cdef cppclass _XY:
vector [double] getVector()除了+
cdef类XY:
cdef _XY _this
...
def getVector():
返回self._this。 getVector()
由于要避免复制此大向量,我想使用std ::移动。像这样:
cdef来自< utility>的extern名称空间 std:
vector [double] move(vector [double])#Cython没有功能模板
这将以以下方式修改Cython源代码:
def getVector():
返回move(self._this.getVector())
问题
以上想法无效。 Cython(至少)产生该载体的1个拷贝。
我认为这是因为无法从向量移走,因为这已经是Cython包装器,围绕C ++中的实际std :: vector类。
还有另一种避免复制的方法吗?我想避免从C ++方法返回指针。
通过定义一个存储向量的C ++包装器类,然后将其移动到Cython中,可能是一种方法但我想知道是否存在一种无需(或很少)修改C ++源代码的方法。
编辑: @DavidW发出警告后,我意识到我误解了您的问题。下面的答案只是让您直接从cython使用模板化的 move
,而无需为每种移动类型进行显式声明(例如,您为 std :: vector< double>
)。
您可以使用此辅助函数来包装 std :: move
调用:
#distutils:语言= c ++
cdef extern from *名称空间 polyfill:
名称空间polyfill {
模板< typename T>
内联类型名std: :remove_reference< T> :: type&& move(T& t){
return std :: move(t);
}
模板< typename T>
内联类型名称std :: remove_reference< T> :: type&& move(T& t){
return std :: move(t);
}
} //名称空间polyfill
cdef T move [T](T)
示例用法:
#d istutils:语言= c ++
cdef extern from *:
#include< iostream>
#define PRINT()std :: cout<< __PRETTY_FUNCTION__<< std :: endl
struct Test {
Test(){PRINT(); }
〜Test(){PRINT(); }
Test(const Test&){PRINT(); }
Test(Test&){PRINT(); }
Test&运算符=(const Test&){PRINT();返回* this; }
Test&运算符=(Test&&){PRINT();返回* this; }
};
void f(const Test&){PRINT(); }
void f(Test&){PRINT(); }
cdef cppclass测试:
通过
cdef void f(Test)
from move cimport移动
cdef测试t1,t2
print(#t1 = t2)
t1 = t2
print(# t1 = move(t2))
t1 = move(t2)
print(#f(t1))
f(t1)
print(#f(move(t1)))
f(move(t1))
print(#f(move(move(t1))))
f(move(move(tve)))
print(#f(move(move(move(t1)))))
f(move(move(move(t1))) ))))
输出(使用 cythonize -3 -i测试编译。 pyx
与Cython 0.29.12和Python 3.7.3):
$ python3 -c import测试
Test :: Test()
Test :: Test()
#t1 = t2
Test& Test :: operator =(const Test&)
#t1 = move(t2)
Test& :: operator =(Test&)
#f(t1)
void f(const Test&)
#f (move(t1))
无效f(Test&)
#f(move(move(t1)))
无效f(Test&)
# f(移动( move(move(t1))))
无效f(Test&)
Test ::〜Test()
Test ::〜Test()
请注意,C ++对象仍默认初始化,因为那是Cython当前所做的,但是此帮助函数允许在初始化后调用移动分配。 p>
编辑:我将此代码段打包为 cymove 。
Problem
I have a very large std::vector
that gets returned from a C++ function, let's call it getVector()
.
Now I want to wrap that function in Cython:
cdef extern from "XY.h":
cdef cppclass _XY:
vector[double] getVector() except +
cdef class XY:
cdef _XY _this
...
def getVector():
return self._this.getVector()
As I want to avoid copying this large vector, I would like to make use of std::move. Like this:
cdef extern from "<utility>" namespace "std":
vector[double] move(vector[double]) # Cython has no function templates
This modifies the Cython source code in the following way:
def getVector():
return move(self._this.getVector())
Question
The above idea is not working. Cython is (at least) producing 1 copy of the vector. I assume this is because there is no way to move from a vector as this is already a Cython wrapper around the actual std::vector class from C++.
Is there another approach to avoid any copies? I would like to avoid returning a pointer from the C++ method.
There is probably a way by defining a C++ wrapper class that stores the vector and then move this class in Cython but I was wondering whether there is a way without (or very little) modifying the C++ source code.
Edit: After @DavidW's warning I realized I misunderstood your question. Below answer just let's you use a templated move
from cython directly without explicit declaration for each moving type (e.g. the one you declared for std::vector<double>
in your question).
You can use this helper function for wrapping std::move
call:
# distutils: language = c++
cdef extern from * namespace "polyfill":
"""
namespace polyfill {
template <typename T>
inline typename std::remove_reference<T>::type&& move(T& t) {
return std::move(t);
}
template <typename T>
inline typename std::remove_reference<T>::type&& move(T&& t) {
return std::move(t);
}
} // namespace polyfill
"""
cdef T move[T](T)
Example usage:
# distutils: language = c++
cdef extern from *:
"""
#include <iostream>
#define PRINT() std::cout << __PRETTY_FUNCTION__ << std::endl
struct Test {
Test() { PRINT(); }
~Test() { PRINT(); }
Test(const Test&) { PRINT(); }
Test(Test&&) { PRINT(); }
Test& operator=(const Test&) { PRINT(); return *this; }
Test& operator=(Test&&) { PRINT(); return *this; }
};
void f(const Test&) { PRINT(); }
void f(Test&&) { PRINT(); }
"""
cdef cppclass Test:
pass
cdef void f(Test)
from move cimport move
cdef Test t1, t2
print("# t1 = t2")
t1 = t2
print("# t1 = move(t2)")
t1 = move(t2)
print("# f(t1)")
f(t1)
print("# f(move(t1))")
f(move(t1))
print("# f(move(move(t1)))")
f(move(move(t1)))
print("# f(move(move(move(t1))))")
f(move(move(move(t1))))
Output (compiled using cythonize -3 -i test.pyx
with Cython 0.29.12 and Python 3.7.3):
$ python3 -c "import test"
Test::Test()
Test::Test()
# t1 = t2
Test& Test::operator=(const Test&)
# t1 = move(t2)
Test& Test::operator=(Test&&)
# f(t1)
void f(const Test&)
# f(move(t1))
void f(Test&&)
# f(move(move(t1)))
void f(Test&&)
# f(move(move(move(t1))))
void f(Test&&)
Test::~Test()
Test::~Test()
Note that C++ objects are still default initialized since that's what Cython does currently, yet this helper function allows calling move assignment after initialization.
Edit: I packaged this snippet as cymove.
这篇关于Cython:避免通过std :: move复制不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!