如何在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字符串到Cstd::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
相关产品推荐
相关产品推荐

