c++ SWIG:将std::span作为参数传递给python函数

lxkprmvk  于 2022-12-15  发布在  Python
关注(0)|答案(1)|浏览(160)

我需要SWIG的帮助。
我有一个C++纯虚函数,我需要在Python中实现它,它得到一个std:span<uint8_t>作为输出参数。

virtual void fill_buffer(size_t offset, std::span<uint8_t> buffer) = 0;

python端负责用数据填充缓冲区。到目前为止,我所做的是创建一些实用函数,如:

%inline %{
    PyObject* size(const std::span<uint8_t>& span) {
        return PyInt_FromLong(span.size());
    }

    void fill_buffer(const std::span<uint8_t>& span, const std::vector<uint8_t>& buffer) {
        std::copy(buffer.begin(), buffer.end(), span.data());
    }
%}

然后在Python这边我有:

def fill_buffer(self, offset, buffer):
    buffer_size = size(buffer)
    with open(self.resource_file, 'rb') as file:
        file.seek(offset)
        read_bytes = file.read(buffer_size)
    fill_buffer(buffer, read_bytes)

但是我想一定有更好的方法来做到这一点。也许使用类型Map?我想无缝地使用python中的buffer对象,而不需要辅助函数,也许类似于:

def fill_buffer(self, offset, buffer):
    with open(self.resource_file, 'rb') as file:
        file.seek(offset)
        buffer = file.read(buffer.size())
zf9nrax1

zf9nrax11#

鉴于以下情况,完成测试:

#include <span>
#include <iostream>

struct test_base {
    virtual void fill_buffer(size_t offset, std::span<uint8_t> buffer) = 0;
    virtual ~test_base() {}
};

inline void run_test(test_base& tb) {
    uint8_t buffer[1024];
    // call virtual function and just print output to prove it worked
    tb.fill_buffer(0, std::span{buffer});
    std::cout << "Buffer is: " << buffer << "\n";
}

我们的目标是将其很好地 Package 到Python中,以便下面的示例可以工作:

import test

class PythonSpan(test.test_base):
    def __init__(self):
        super().__init__()

    def fill_buffer(self, offset, buf):
        # totally ignored offset param
        with open(__file__, 'rb') as f:
            got = f.readinto(buf)
            buf[got] = 0 # null terminate for demo

filler = PythonSpan()
test.run_test(filler)

(Note readinto是从文件读取直接进入您选择的缓冲区的简洁方法)
我们需要的是一个“directorin”类型Map,幸运的是,Python 3的C API有一个函数,它几乎完全符合我们的要求。PyMemoryView_FromMemory创建了一个buffer对象,这是Python中std::span的一个相当好的等价物。

%module(directors="1") test
%feature("director");

%typemap(directorin) std::span %{
        $input = PyMemoryView_FromMemory(reinterpret_cast<char*>($1.data()), $1.size(), PyBUF_WRITE);
%}

%{
#include "test.h"
%}

%include "test.h"

如果span中的类型不仅仅是uint8_t,我们可以做一些更有想象力的事情,但是对于这种情况,简单的接口就足够了。(另外,因为我们创建的缓冲区允许就地修改,所以不需要“directorargout”类型Map,但是“in”类型Map在这里可能是一个有用的补充)

相关问题