31.3.2. platformstl::scatter_slice_sequence—预告
在STLSoft中,有个组件刚刚加入子项目PlatformSTL且仍在演化,名字叫做scatter_slice_sequence,它以另一种方式表达了这个抽象。这是一个Façade类模板,它维护一个数组,其元素是表示片段的结构,整个数组描述一组I/O缓冲区。该类模板除了提供STL集合风格的访问方法(通过begin()和end()),还允许调用原生read/write函数直接操作缓冲区。这个类与iovec和WSABUF都能搭配,因为通过属性垫片(9.2.1节,包括get_scatter_slice_size、get_scatter_slice_ptr,还有 get_scatter_slice_size_member_ptr)抽象了iovec和WSABUF的特征,如清单31.3所示。
清单31.3. iovec 和 WSABUF 结构的属性垫片
Code View: Scroll / Show All #if defined(PLATFORMSTL_OS_IS_UNIX) inline void* const get_scatter_slice_ptr(struct iovec const& ss) { return ss.iov_base; } inline void*& get_scatter_slice_ptr(struct iovec& ss); inline size_t get_scatter_slice_size(struct iovec const& ss) { return static_cast<size_t>(ss.iov_len); } inline size_t& get_scatter_slice_size(struct iovec& ss); inline size_t iovec::* get_scatter_slice_size_member_ptr(struct iovec const*) { return &iovec::iov_len; } #elif defined(PLATFORMSTL_OS_IS_WIN32) inline void const* get_scatter_slice_ptr(WSABUF const& ss) { return ss.buf; } inline void*& get_scatter_slice_ptr(WSABUF& ss); inline size_t get_scatter_slice_size(WSABUF const& ss) { return static_cast<size_t>(ss.len); } inline size_t& get_scatter_slice_size(WSABUF& ss); inline u_long WSABUF::* get_scatter_slice_size_member_ptr(WSABUF const*) { return &WSABUF::len; } #endif /* operating system */ |
目前scatter_slice_sequence在UNIX上支持readv()/writev(),在Windows上支持WSARecv()/WSASend()和WSARecvFrom()/WSASendTo()。清单31.4展示了一个例子,首先用iovec特化scatter_slice_sequence,然后从一个文件描述符把内容读取到几个缓冲区中,经过STL方式的处理后,最终把转换后的结果写入到另外一个文件描述符。
清单31.4. 搭配 readv()和 writev()使用scatter_slice_sequence的例子
Code View: Scroll / Show All int fs = . . . // Opened for read int fd = . . . // Opened for write for(;;) { const size_t BUFF_SIZE = 100; const size_t MAX_BUFFS = 10; char buffers[MAX_BUFFS][BUFF_SIZE]; const size_t numBuffers = rand() % MAX_BUFFS; // Declare an instance with arity of numBuffers platformstl::scatter_slice_sequence<iovec> sss(numBuffers);
// Set up each slice in the sequence, which may be of // different sizes in reality { for(size_t i = 0; i < numBuffers; ++i) { sss.set_slice(i, &buffers[i][0], sizeof(buffers[i])); }} if(0 != numBuffers) // In real scenario, might get 0 buffers { size_t n = sss.read(::readv, fs); // Read from fs using ::readv() if(0 == n) { break; } // "Process" the contents std::transform( sss.payload().begin(), sss.payload().begin() + n , sss.payload().begin(), ::toupper); sss.write(::writev, fd, n); // Write n to fd using ::writev() } } |
很显然这是一个非常简化的例子,不过我相信你能这样想象:fd和fs是socket描述符,缓冲区来自共享的内存区域(某个时刻可能没有富余空间),而“处理”也不一定像在(重新)传输前把内容改为大写这么简单。
序列的载荷(从payload()得到)提供了迭代器,用以随机访问内存块的内容。但我们必须认识到,这些迭代器就像std::deque一样是不连续的(2.3.6节)!对它们施加的指针算术运算只需常数时间,但区间遍历却不是线性时间操作。scatter_slice_sequence还没有完成,在正式发布到PlatformSTL子项目之前,其接口可能会进一步演化。(代码在CD上。)但它无疑提供了一组数据块表示为STL序列(2.2节)的能力,并提供适配器方法read()和write(),这些方法以文件/socket句柄和分散/聚集I/O函数为参数,然后将其应用到序列所持有的数据块。从逻辑上看,等价于通过“CreateLockBytesOnMemory()加上SYCLBOMF_FIXED_ARRAY和 CreateStreamOnLockBytes()”所创建的COM流对象。不过该适配器也存在一个明显的不足,即只能按照“一次一个元素”的方式遍历其内容,这有可能造成性能损失。(提示:这是以下有趣内容的一个线索…)
【责任编辑:
董书 TEL:(010)68476606】