Cython:避免通过std :: move复制不起作用 [英] Cython: Avoid copy through std::move not working

查看:92
本文介绍了Cython:避免通过std :: move复制不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题



我有一个很大的 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屋!

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