谁设计了C++的IOStreams,现在是否仍然被认为是良好设计?

138

首先,这似乎看起来是在寻求主观意见,但这不是我想要的。我希望听到一些有根据的关于这个话题的论点。


为了了解现代流/序列化框架应该如何设计,我最近购买了Angelika Langer和Klaus Kreft合著的书《C++标准库IOStreams和Locales》。我认为,如果IOStreams没有被很好地设计,它就不会首先进入C++标准库。

在阅读了这本书的各个部分后,我开始怀疑IOStreams是否可以从整体结构的角度与STL相比较。例如,请阅读与STL之父Alexander Stepanov的采访,了解一些导致STL的设计决策。

特别令我惊讶的是:

  • 似乎不知道谁负责IOStreams的整体设计(我很想阅读一些关于此背景信息的资源 - 有人知道好的资源吗?);

  • 一旦你深入IOStreams的表面,例如如果你想用自己的类扩展IOStreams,你就会遇到一个接口,其中成员函数名称相当神秘和令人困惑,例如getloc/imbueuflow/underflowsnextc/sbumpc/sgetc/sgetnpbase/pptr/epptr(可能还有更糟糕的例子)。这使得理解整体设计及其单个部分如何协作变得更加困难。即使是我上面提到的那本书也没能帮助那么多。


因此我的问题是:

如果按照当今的软件工程标准(如果实际上有任何普遍协议),您会认为C++的IOStreams仍然是设计良好的吗?(我不想从被普遍认为已经过时的东西中提高我的软件设计技能。)


8
有趣的Herb Sutter观点 https://dev59.com/fXE95IYBdhLWcg3wCJbk#2486085 :) 很遗憾那个人只参与了几天就离开了SO - Johannes Schaub - litb
5
有其他人看到STL流中混杂的问题吗?流通常设计为读取或写入字节,不涉及其他内容。能够读取或写入特定数据类型的东西是格式化器(它可以使用流来读取/写入格式化后的字节,但并非必须)。将两者混合到一个类中,使得实现自己的流更加复杂。 - mmmmmmmm
5
@rsteven,这里有两个不同的概念。std::streambuf 是读写字节的基类,而 istream / ostream 则是用于格式化输入和输出的类,将一个指向 std::streambuf 的指针作为它们的目标/来源。 - Johannes Schaub - litb
2
@rstevens,ostream foo(&somebuffer); foo << "huh"; foo.rdbuf(cout.rdbuf()); foo << "see me!"; - Johannes Schaub - litb
1
@JohannesSchaub-litb "std::streambuf是读写字节的基类" 这是错误的。std::streambuf支持文本I/O。 - curiousguy
显示剩余8条评论
11个回答

0
我可以帮忙翻译。下面是需要翻译的内容:

我无法不回答问题的第一部分(谁做了那件事?)。但这个问题已经在其他帖子中得到了回答。

至于问题的第二部分(设计良好吗?),我的回答是坚决的“不是!”这里有一个小例子,让我不敢相信多年:

#include <stdint.h>
#include <iostream>
#include <vector>

// A small attempt in generic programming ;)
template <class _T>
void ShowVector( const char *title, const std::vector<_T> &v)
{
    std::vector<_T>::const_iterator iter;
    std::cout << title << " (" << v.size() << " elements): ";
    for( iter = v.begin(); iter != v.end(); ++iter )
    {
        std::cout << (*iter) << " ";
    }
    std::cout << std::endl;
}
int main( int argc, const char * argv[] )
{
    std::vector<uint8_t> byteVector;
    std::vector<uint16_t> wordVector;
    byteVector.push_back( 42 );
    wordVector.push_back( 42 );
    ShowVector( "Garbled bytes as characters output o.O", byteVector );
    ShowVector( "With words, the numbers show as numbers.", wordVector );
    return 0;
}

上述代码由于iostream设计的原因而产生了无意义的结果。出于我难以理解的某些原因,它们将uint8_t字节视为字符,而较大的整数类型则被视为数字。Q.e.d.糟糕的设计。
我也想不到任何方法来解决这个问题。该类型也可以是浮点数或双精度浮点数,因此将其转换为“int”以使愚蠢的iostream理解数字而不是字符并不会有所帮助。
IOStream设计存在缺陷,因为它没有为程序员提供表达如何处理项目的手段。 IOStream实现做出了任意决定(例如将uint8_t视为char而不是字节数)。这是IOStream设计的缺陷,因为它们试图实现不可能的目标。
C ++不允许对类型进行分类 - 该语言没有这样的功能。没有is_number_type()或is_character_type()这样的东西可以用于IOStream以进行合理的自动选择。忽略这一点并试图猜测是库的设计缺陷。
承认,printf()在通用的“ShowVector()”实现中同样无法正常工作。但这并不是iostream行为的借口。但很可能在printf()情况下,ShowVector()将定义如下:
template <class _T>
void ShowVector( const char *formatString, const char *title, const std::vector<_T> &v );

6
责任并不完全在iostream上。检查一下你的uint8_t是哪个类型定义。它实际上是一个char吗?如果是,那就不要因为iostream像对待char一样对待它而责怪iostreams。 - Martin Ba
如果你想确保在通用代码中获得一个数字,你可以使用 num_put facet 而不是流插入运算符。 - Martin Ba
@Martin Ba 你说得对 - c/c++标准保持了“short unsigned int”有多少字节的开放性。 “unsigned char”是该语言的特殊用法。如果你真的想要一个字节,你必须使用unsigned char。C++也不允许对模板参数施加限制 - 比如“只有数字”,所以如果我将ShowVector的实现更改为你提出的num_put解决方案,ShowVector就不能再显示字符串向量了,对吧?;) - BitTickler
1
@Martin Bla:cppreference提到int8_t是一个带有精确8位宽度的有符号整数类型。我同意作者的观点,即如果你得到垃圾输出,那么这很奇怪,尽管在iostream中char类型的typedef和重载在技术上是可以解释的。如果有一个__int8真正的类型而不是typedef,这个问题本应该被解决。 - gast128
哦,其实修复这个问题很容易:// Fixes for std::ostream which has broken support for unsigned/signed/char types // and prints 8-bit integers like they were characters. namespace ostream_fixes {inline std::ostream& operator<< (std::ostream& os, unsigned char i) { return os << static_cast<unsigned int> (i); }inline std::ostream& operator<< (std::ostream& os, signed char i) { return os << static_cast<signed int> (i); }} // namespace ostream_fixes - mcv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接