You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Cython中正确封装多文件结构的C++ TcpStream类?

用Cython封装多文件C++ TcpStream类的实现方案

问题描述

我需要通过Cython封装层在Python中使用C++编写的TcpStream类(该类还有其他独立用途),但该类拆分在多个文件中:Stream.cpp、Stream.hpp、TcpStream.cpp和TcpStream.hpp(其中TcpStream.hpp已包含Stream.hpp)。请问如何编写对应的.pyx和.pxd文件?

给出的C++头文件如下:

Stream.hpp

#ifndef __STREAM_HPP__
#define __STREAM_HPP__

#include <string>

// Input stream interface
class InputStream {
public:
    // Read exactly byteCount bytes into buffer
    virtual void readBytes(char* buffer, size_t byteCount) = 0;
    // Read a bool from this stream
    bool readBool();
    // Read an int from this stream
    int readInt();
    // Read a long long from this stream
    long long readLongLong();
    // Read a float from this stream
    float readFloat();
    // Read a double from this stream
    double readDouble();
    // Read a string from this stream
    std::string readString();
};

// Output stream interface
class OutputStream {
public:
    // Write byte buffer into this stream
    virtual void writeBytes(const char* buffer, size_t byteCount) = 0;
    // Flush this stream
    virtual void flush() = 0;
    // Write a bool into this stream
    void write(bool value);
    // Write an int into this stream
    void write(int value);
    // Write a long long into this stream
    void write(long long value);
    // Write a float into this stream
    void write(float value);
    // Write a double into this stream
    void write(double value);
    // Write a string into this stream
    void write(const std::string& value);
};

#endif

TcpStream.hpp

#ifndef __TCP_STREAM_HPP__
#define __TCP_STREAM_HPP__

#include "Stream.hpp"

#ifdef _WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#include <Ws2tcpip.h>
#include <winsock2.h>
typedef int RECV_SEND_T;
#else
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <unistd.h>
typedef int SOCKET;
typedef ssize_t RECV_SEND_T;
#endif

class TcpStream : public InputStream, public OutputStream {
public:
    TcpStream(const std::string& host, int port);
    ~TcpStream();
    void readBytes(char* buffer, size_t byteCount);
    void writeBytes(const char* buffer, size_t byteCount);
    void flush();

private:
    SOCKET sock;
    static const size_t BUFFER_CAPACITY = 8 * 1024;
    char readBuffer[BUFFER_CAPACITY];
    size_t readBufferPos;
    size_t readBufferSize;
    char writeBuffer[BUFFER_CAPACITY];
    size_t writeBufferPos;
    size_t writeBufferSize;
};

#endif

解决方案

步骤1:编写Cython声明文件(.pxd)

.pxd文件用于映射C类和函数的结构,让Cython识别底层C代码。

Stream.pxd

映射InputStream和OutputStream接口:

cdef extern from "Stream.hpp":
    cdef cppclass InputStream:
        void readBytes(char* buffer, size_t byteCount) except +
        bool readBool() except +
        int readInt() except +
        long long readLongLong() except +
        float readFloat() except +
        double readDouble() except +
        std::string readString() except +

    cdef cppclass OutputStream:
        void writeBytes(const char* buffer, size_t byteCount) except +
        void flush() except +
        void write(bool value) except +
        void write(int value) except +
        void write(long long value) except +
        void write(float value) except +
        void write(double value) except +
        void write(const std::string& value) except +

注:except +标识该方法可能抛出异常,Cython会自动将其转换为Python异常;std::string依赖Cython对C++标准库的支持,后续编译需启用相关配置。

TcpStream.pxd

映射继承自上述接口的TcpStream类:

from Stream cimport InputStream, OutputStream

cdef extern from "TcpStream.hpp":
    cdef cppclass TcpStream(InputStream, OutputStream):
        TcpStream(const std::string& host, int port) except +
        ~TcpStream() except +
        void readBytes(char* buffer, size_t byteCount) except +
        void writeBytes(const char* buffer, size_t byteCount) except +
        void flush() except +

步骤2:编写Cython实现文件(.pyx)

创建tcp_stream.pyx,实现Python可调用的封装类,对接底层C++对象:

from libcpp.string cimport string
from Stream cimport InputStream, OutputStream
from TcpStream cimport TcpStream

cdef class PyTcpStream:
    cdef TcpStream* cpp_stream  # 持有C++对象指针

    def __cinit__(self, str host, int port):
        cdef string cpp_host = host.encode('utf-8')
        self.cpp_stream = new TcpStream(cpp_host, port)

    def __dealloc__(self):
        if self.cpp_stream is not NULL:
            del self.cpp_stream

    # 封装InputStream的方法
    def read_bool(self):
        return self.cpp_stream.readBool()

    def read_int(self):
        return self.cpp_stream.readInt()

    def read_long_long(self):
        return self.cpp_stream.readLongLong()

    def read_float(self):
        return self.cpp_stream.readFloat()

    def read_double(self):
        return self.cpp_stream.readDouble()

    def read_string(self):
        cdef string cpp_str = self.cpp_stream.readString()
        return cpp_str.decode('utf-8')

    # 封装OutputStream的方法
    def write_bool(self, bint value):
        self.cpp_stream.write(value)

    def write_int(self, int value):
        self.cpp_stream.write(value)

    def write_long_long(self, long long value):
        self.cpp_stream.write(value)

    def write_float(self, float value):
        self.cpp_stream.write(value)

    def write_double(self, double value):
        self.cpp_stream.write(value)

    def write_string(self, str value):
        cdef string cpp_str = value.encode('utf-8')
        self.cpp_stream.write(cpp_str)

    def flush(self):
        self.cpp_stream.flush()

注:

  • __cinit__负责初始化C对象,完成Python字符串到C std::string的编码转换;
  • __dealloc__负责释放C++对象,避免内存泄漏;
  • 方法命名采用Python风格的下划线分隔,符合Python开发习惯;
  • 字符串需做编码转换(Python str → UTF-8字节 → C++ std::string,反之亦然)。

步骤3:编写编译脚本(setup.py)

将所有C++源文件和Cython文件编译为Python扩展模块:

from setuptools import setup, Extension
from Cython.Build import cythonize
import sys

# 定义编译参数
cpp_files = ["Stream.cpp", "TcpStream.cpp"]
ext_modules = [
    Extension(
        "tcp_stream",
        ["tcp_stream.pyx"] + cpp_files,
        language="c++",
        extra_compile_args=["-std=c++11"] if sys.platform != "win32" else ["/std:c++11"],
        extra_link_args=["-lws2_32"] if sys.platform == "win32" else [],
    )
]

setup(
    name="tcp_stream",
    ext_modules=cythonize(ext_modules),
)

注:

  • Windows平台需链接ws2_32.lib以支持Socket功能;
  • 启用C11标准兼容现代C语法;
  • 编译命令:python setup.py build_ext --inplace,生成的.so(Linux/macOS)或.pyd(Windows)文件可直接在Python中导入使用。

使用示例

编译完成后,Python中调用示例:

from tcp_stream import PyTcpStream

# 创建TcpStream连接
stream = PyTcpStream("127.0.0.1", 8080)

# 写入数据
stream.write_int(123)
stream.write_string("hello cython")
stream.flush()

# 读取数据
num = stream.read_int()
msg = stream.read_string()
print(num, msg)

内容的提问来源于stack exchange,提问作者DanteTemplar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 19:27:24