누가 C ++의 IOStream을 설계 / 설계했으며 오늘날의 표준에 의해 잘 설계된 것으로 간주됩니까? [닫은]


127

우선, 나는 주관적인 의견을 요구하는 것처럼 보일 수 있지만, 그것은 내가 따르는 것이 아닙니다. 이 주제에 대한 근거가있는 몇 가지 주장을 듣고 싶습니다.


최신 스트림 / 직렬화 프레임 워크를 어떻게 설계해야하는지에 대한 통찰력을 얻기 위해 최근 Angelika Langer와 Klaus Kreft의 Standard C ++ IOStreams and Locales 라는 책을 받았습니다 . IOStreams가 제대로 설계되지 않았다면 처음에는 C ++ 표준 라이브러리로 만들지 않았을 것이라고 생각했습니다.

이 책의 여러 부분을 읽은 후 IOStreams가 전체 아키텍처 관점에서 STL과 비교할 수 있는지 의심됩니다. 예를 들어 Alexander Stepanov (STL의 "발명자")와의 인터뷰에서 STL 에 적용되는 일부 디자인 결정에 대해 알아보십시오.

특히 나를 놀라게하는 것 :

  • IOStreams의 전체 디자인을 담당 한 사람은 누구인지 알 수없는 것 같습니다. 이에 대한 배경 정보를 읽고 싶습니다. 누구나 좋은 리소스를 알고 있습니까?

  • 당신이 당신의 자신의 클래스와 IOSTREAMS를 확장 할 경우 예를 들어 IOSTREAMS의 즉각적인 표면 아래에 탐구하면, 당신은 상당히 애매하고 혼란 멤버 함수 이름, 예를 들어이와 인터페이스에 도착 getloc/ imbue, uflow/ underflow, snextc/ sbumpc/ sgetc/ sgetn, pbase/ pptr/ epptr(그리고있다 아마도 더 나쁜 예). 이로 인해 전체 설계와 단일 부품의 작동 방식을 이해하기가 훨씬 어려워집니다. 위에서 언급 한 책조차 그다지 도움 되지 않습니다 (IMHO).


따라서 내 질문 :

당신은 (실제로이 경우 오늘날의 소프트웨어 엔지니어링 기준으로 판단해야한다면 입니다 다음에 어떤 일반 협정), C ++의 여전히 잘 디자인 된 것으로 간주 할 수 IOSTREAMS 것인가? (저는 일반적으로 구식이라고 여겨지는 것에서 소프트웨어 디자인 기술을 향상시키고 싶지 않습니다.)


7
흥미로운 Herb Sutter의 의견 stackoverflow.com/questions/2485963/… :) 며칠 만 참여한 후에도 그 사람이 그렇게 떠난 것이 너무 나쁘다
Johannes Schaub-litb

5
STL 스트림에서 우려 사항이 혼합되어있는 다른 사람이 있습니까? 스트림은 일반적으로 바이트를 읽거나 쓰도록 설계되었습니다. 특정 데이터 유형을 읽거나 쓸 수있는 것은 포맷터입니다 (형식화 된 바이트를 읽거나 쓰기 위해 스트림을 사용할 필요는 없지만). 두 클래스를 하나의 클래스로 혼합하면 자체 스트림을 구현하는 것이 훨씬 더 복잡해집니다.
mmmmmmmm

4
@rsteven, 이러한 우려는 분리되어 있습니다. std::streambuf바이트를 읽고 쓰는 데 사용되는 기본 클래스이며 istream/ ostreamstd::streambuf대상 / 소스로 포인터를 가져 와서 형식화 된 입 / 출력을위한 것 입니다.
Johannes Schaub-

1
@litb : 그러나 스트림 (포맷터)에서 사용하는 streambuf를 전환 할 수 있습니까? STL 형식을 사용하고 싶지만 특정 streambuf를 통해 데이터를 쓰고 싶습니까?
mmmmmmmm

2
@rstevensostream foo(&somebuffer); foo << "huh"; foo.rdbuf(cout.rdbuf()); foo << "see me!";
Johannes Schaub-litb

답변:


31

: 여러 병 선입견은 표준에 자신의 방법을 발견 auto_ptr, vector<bool>, valarray그리고 export몇 가지 이름을. 따라서 필자는 품질 설계의 표시로 IOStream이 존재하지 않을 것입니다.

IOStream에는 체크 무늬 기록이 있습니다. 실제로는 이전 스트림 라이브러리의 재 작업이지만 오늘날의 많은 C ++ 관용구가 존재하지 않았을 때 작성되었으므로 디자이너는 가늠자의 이점을 얻지 못했습니다. 시간이 지남에 따라 분명하게 드러난 한 가지 문제는 가상 함수를 많이 사용하고 아주 세분화 된 수준으로 내부 버퍼 객체로 전달하기 때문에 C의 stdio만큼 효율적으로 IOStream을 구현하는 것이 거의 불가능하다는 것입니다. 로케일이 정의되고 구현되는 방식. 이것에 대한 나의 기억은 매우 희미하다. 나는 인정할 것이다. 나는 그것이 몇 년 전에 comp.lang.c ++. moderated에 대한 격렬한 토론의 주제라는 것을 기억합니다.


3
입력 해 주셔서 감사합니다. comp.lang.c++.moderated가치있는 것을 찾으면 질문 하단에 있는 아카이브를 탐색하고 링크를 게시하겠습니다. -게다가, 나는 당신과 동의하지 않습니다 auto_ptr: Herb Sutter의 탁월한 C ++을 읽은 후에 는 RAII 패턴을 구현할 때 매우 유용한 클래스처럼 보입니다.
stakx-더 이상

5
@stakx : 그럼에도 불구 unique_ptr하고 더 명확하고 강력한 의미론으로 인해 더 이상 사용되지 않고 대체되고 있습니다.
UncleBens가

3
@UncleBens unique_ptr에는 rvalue 참조가 필요합니다. 이 시점 auto_ptr에서 매우 강력한 포인터입니다.
Artyom

7
그러나 auto_ptr버그를 역 참조하기위한 틈새로 만드는 복사 / 할당 시맨틱을 망쳤습니다 ...
Matthieu M.

5
@TokenMacGuy : 벡터가 아니며 부울을 저장하지 않습니다. 어느 정도 오해의 소지가 있습니다. ;)
jalf

40

누가 디자인했는지에 관해서는, 원래 라이브러리는 Bjarne Stroustrup에 의해 만들어졌으며 Dave Presotto에 의해 다시 구현되었습니다. 그런 다음 Andrew Koenig의 조작자 아이디어를 사용하여 Cfront 2.0 용 Jerry Schwarz가 다시 디자인하고 다시 구현했습니다. 라이브러리의 표준 버전은이 구현을 기반으로합니다.

소스 "C ++의 디자인과 진화", 섹션 8.3.1.


3
@Neil-너트 디자인에 대한 당신의 의견은 무엇입니까? 다른 답변을 바탕으로 많은 사람들이 귀하의 의견을 듣고 싶습니다.
DVK

1
@DVK 방금 의견을 별도의 답변으로 게시했습니다.

2
다만 그는 일부 비트와 IOSTREAMS 역사의 조각을 언급 비얀 스트로브 스트 룹과의 인터뷰의 사본 발견 www2.research.att.com/~bs/01chinese.html (이 링크가 일시적 지금 깨진 것 같다,하지만 당신은 시도 할 수 있습니다 Google의 페이지 캐시)
stakx-더 이상

2
업데이트 링크 : stroustrup.com/01chinese.html .
FrankHB

28

오늘날의 소프트웨어 엔지니어링 표준에 따라 판단해야한다면 (실제로이 표준에 대한 일반적인 합의가있는 경우) C ++의 IOStream은 여전히 ​​잘 설계된 것으로 간주됩니까? (저는 일반적으로 구식이라고 여겨지는 것에서 소프트웨어 디자인 기술을 향상시키고 싶지 않습니다.)

몇 가지 이유로 NO 라고 대답합니다 .

잘못된 오류 처리

오류 상태는가 아닌 예외로보고해야합니다 operator void*.

"좀비 객체"안티 패턴은 이와 같은 버그를 일으키는 입니다.

포맷팅과 I / O의 불일치

따라서 필요에 관계없이 서식에 대한 추가 상태 정보를 포함해야하므로 스트림 개체가 불필요하게 복잡해집니다.

또한 다음과 같은 버그 작성 가능성을 높입니다.

using namespace std; // I'm lazy.
cout << hex << setw(8) << setfill('0') << x << endl;
// Oops!  Forgot to set the stream back to decimal mode.

대신에, 당신은 다음과 같은 것을 썼습니다 :

cout << pad(to_hex(x), 8, '0') << endl;

서식 관련 상태 비트가없고 문제가 없습니다.

Java, C # 및 Python과 같은 "현대"언어에서 모든 객체에는 I / O 루틴에 의해 호출되는 toString/ ToString/ __str__함수가 있습니다. AFAIK, C ++ 만 stringstream문자열로 변환하는 표준 방법으로 사용하여 다른 방법으로 수행 합니다.

i18n에 대한 지원 부족

Iostream 기반 출력은 문자열 리터럴을 조각으로 나눕니다.

cout << "My name is " << name << " and I am " << occupation << " from " << hometown << endl;

형식 문자열은 전체 문장을 문자열 리터럴에 넣습니다.

printf("My name is %s and I am %s from %s.\n", name, occupation, hometown);

후자의 접근 방식은 GNU gettext와 같은 국제화 라이브러리에 쉽게 적용 할 수 있습니다. 전체 문장을 사용하면 번역가에게 더 많은 컨텍스트를 제공 할 수 있기 때문입니다. 문자열 형식화 루틴이 POSIX $printf 매개 변수 와 같은 순서 변경을 지원하면 언어 간의 단어 순서 차이를 더 잘 처리합니다.


4
실제로 i18n의 경우 대체는 위치 (% 1, % 2, ..)로 식별해야합니다. 번역시 매개 변수 순서를 변경해야 할 수도 있습니다. 그렇지 않으면 전적으로 동의합니다-+1.
peterchen

4
@ peterchen : 그것이 POSIX $지정자입니다 printf.
jamesdlin

2
문제는 문자열 형식이 아니며 C ++에는 형식이 안전하지 않은 varargs가 있다는 것입니다.
dan04

5
C ++ 11부터는 typesafe varargs가 있습니다.
Mooing Duck

2
IMHO '추가 상태 정보'가 최악의 문제입니다. cout은 세계적입니다. 서식 플래그를 첨부하면 해당 플래그가 전역 적으로 만들어지며 대부분의 용도에서 몇 줄의 의도 된 범위가 있다고 생각하면 꽤 끔찍합니다. ostream에 바인딩하지만 자체 상태를 유지하는 'formatter'클래스로 문제를 해결할 수 있었을 것입니다. 그리고, cout과 함께 할 일이 일반적으로 printf와 함께 할 같은 일 (즉, 가능한 경우) ..에 비해 끔찍한 봐
greggo

17

나는 순수한 의견이기 때문에 이것을 별도의 답변으로 게시하고 있습니다.

입력 및 출력 (특히 입력)을 수행하는 것은 매우 어려운 문제이므로 놀랍지 않게 iostream 라이브러리에는 봇 (bodges)과 완벽한 후 시력을 갖춘 것들이 더 좋을 수있는 것들이 가득합니다. 그러나 모든 언어가 이와 같은 언어로 모든 I / O 라이브러리 인 것 같습니다. 나는 I / O 시스템이 디자이너의 경외심을 불러 일으키는 아름다움 인 프로그래밍 언어를 사용한 적이 없다. iostream 라이브러리는 특히 CI / O 라이브러리 (확장 성, 형식 안전성 등)에 비해 이점이 있지만, 아무도 훌륭한 OO 또는 일반 디자인의 예라고 생각하지는 않습니다.


16

C ++ iostream에 대한 나의 의견은 시간이 지남에 따라 특히 나 자신의 스트림 클래스를 구현하여 실제로 확장하기 시작한 후에 크게 향상되었습니다. 나는 어리석게도 불충분 한 멤버 함수 이름 xsputn이나 그 밖의 이름에도 불구하고 확장 성과 전반적인 디자인에 감사하기 시작했습니다 . 그럼에도 불구하고 I / O 스트림은 C stdio.h에 비해 크게 개선 된 것으로 생각되는데, 이는 형식 안전성이없고 주요 보안 결함으로 가득 차 있습니다.

IO 스트림의 주요 문제는 텍스트 형식과 직렬화라는 두 가지 관련이 있지만 다소 직교 개념을 혼동한다는 것입니다. 한편, IO 스트림은 사람이 읽을 수있는 형식의 개체 텍스트 표현을 생성하고 다른 한편으로 개체를 휴대용 형식으로 직렬화하도록 설계되었습니다. 때때로이 두 목표는 하나이고 같은 것이지만 다른 경우에는 심각하게 성가신 불일치가 발생합니다. 예를 들면 다음과 같습니다.

std::stringstream ss;
std::string output_string = "Hello world";
ss << output_string;

...

std::string input_string;
ss >> input_string;
std::cout << input_string;

여기서 입력으로 얻는 것은 원래 스트림으로 출력 한 것이 아닙니다 . 이것은 <<연산자가 전체 문자열을 출력 하기 때문에, >>공백에 길이 정보가 저장되어 있지 않기 때문에 공백 문자가 발생할 때까지만 스트림에서 읽습니다 . "hello world"를 포함하는 문자열 객체를 출력하더라도 "hello"를 포함하는 문자열 객체 만 입력 할 것입니다. 따라서 스트림이 형식 지정 기능으로 사용되었지만 개체를 ​​올바르게 직렬화 한 다음 직렬화 해제하지 못했습니다.

IO 스트림은 직렬화 기능을 위해 설계되지 않았다고 말할 수 있지만, 그렇다면 입력 스트림이 실제로 무엇 입니까? 게다가, 다른 표준 직렬화 기능이 없기 때문에 실제로 I / O 스트림은 종종 개체를 직렬화하는 데 사용됩니다. 고려 boost::date_time또는 boost::numeric::ublas::matrix사용하면 출력 매트릭스 개체 경우 경우, <<운영자 입력 한가 사용하는 경우, 당신은 똑같은 행렬을 얻을 것이다 >>연산자. 그러나이를 달성하기 위해 Boost 디자이너는 열 개수 및 행 개수 정보를 텍스트 데이터로 출력에 저장해야하므로 실제 사람이 읽을 수있는 디스플레이가 손상됩니다. 다시 말하지만, 텍스트 형식 지정 기능과 직렬화가 어색합니다.

대부분의 다른 언어가이 두 기능을 어떻게 분리하는지 주목하십시오. 예를 들어 Java에서는 형식화가 toString()메소드를 통해 수행 되고 직렬화는 Serializable인터페이스를 통해 수행됩니다 .

제 생각에 가장 좋은 해결책은 표준 문자 기반 스트림 과 함께 바이트 기반 스트림 을 도입하는 것이 었습니다 . 이러한 스트림은 사람이 읽을 수있는 형식화 / 표시에 관계없이 이진 데이터에서 작동합니다. C ++ 객체를 이식 가능한 바이트 시퀀스로 변환하기 위해 직렬화 / 직렬화 기능으로 만 사용할 수 있습니다.


대답 해줘서 고마워. 나는 이것에 대해 잘못 생각할 수 있지만 마지막 포인트 (바이트 기반 대 문자 기반 스트림)와 관련하여 스트림 버퍼 (문자 변환, 전송 및 버퍼링) 사이의 분리에 대한 IOStream의 (일부?) 대답이 아닙니까? 그리고 스트림 (구문 분석 / 포맷)? 그리고 (기계 판독 가능) 직렬화 및 역 직렬화만을위한 스트림 스트림 및 사람이 읽을 수있는 형식화 및 구문 분석을 위해 고유 한 스트림 클래스를 새로 만들 수 없습니까?
stakx-더 이상

@ stakx, 그렇습니다. 실제로이 작업을 수행했습니다. std::char_traits휴대하기에 특화되어 있지 않으므로 소리보다 약간 더 성가시다 unsigned char. 그러나 해결 방법이 있으므로 확장 성이 다시 한 번 구조에 온 것 같습니다. 그러나 바이트 기반 스트림이 표준이 아니라는 사실이 라이브러리의 약점이라고 생각합니다.
Charles Salvia

4
또한 이진 스트림을 구현하려면 형식 문제가 완전히 분리되지 않으므로 새로운 스트림 클래스 새 버퍼 클래스 를 구현해야합니다 std::streambuf. 따라서 기본적으로 확장하는 것은 std::basic_ios수업입니다. 따라서 "확장"이 "완전히 다시 구현하는"영역으로 넘어가는 라인이 있으며 C ++ I / O 스트림 기능에서 이진 스트림을 만드는 것이 그 시점에 근접한 것으로 보입니다.
Charles Salvia

잘 말했고 & 내가 정확히 의심했던 것. 그리고 C와 C ++ 모두 특정 비트 너비와 표현을 보장 하지 않기 위해 많은 시간을 투자 한다는 사실은 I / O를 할 때 실제로 문제가 될 수 있습니다.
stakx-더 이상

" 개체를 이식 가능한 형식으로 직렬화합니다. "아니요, 그것들을 지원하려는 의도는 없었습니다.
curiousguy

11

나는 항상 C ++ IOStreams가 잘못 설계되었다는 것을 알았습니다. 그 구현으로 인해 새로운 유형의 스트림을 올바르게 정의하기가 매우 어렵습니다. 또한 io 기능과 서식 기능을 혼합합니다 (매니퓰레이터에 대한 생각).

개인적으로, 내가 찾은 최고의 스트림 설계 및 구현은 Ada 프로그래밍 언어에 있습니다. 디커플링의 모델이며, 새로운 유형의 스트림을 만드는 기쁨이며, 출력 기능은 사용 된 스트림에 관계없이 항상 작동합니다. 이것은 가장 일반적인 분모에 감사합니다. 스트림에 바이트를 출력하면됩니다. 스트림 함수는 바이트를 스트림에 넣는 것을 처리합니다. 예를 들어 정수를 16 진수로 형식화하는 것은 해당 작업이 아닙니다 (물론 형식 지정을 위해 정의 된 클래스 멤버와 동등한 유형 속성 세트가 있습니다)

C ++이 스트림과 관련하여 간단하기를 바랍니다.


내가 언급 한 책은 기본 IOStreams 아키텍처를 다음과 같이 설명합니다. 전송 계층 (스트림 버퍼 클래스)과 파싱 ​​/ 포맷팅 계층 (스트림 클래스)이 있습니다. 전자는 바이트 스트림에서 문자를 읽거나 쓰는 역할을하는 반면 후자는 문자를 구문 분석하거나 값을 문자로 직렬화하는 역할을합니다. 이것은 충분히 분명해 보이지만 이러한 우려가 실제로 실제로 명확하게 분리되어 있는지 확실하지 않습니다. 로케일이 작동 할 때 -또한 새로운 스트림 클래스를 구현하기가 어렵다는 것에 동의합니다.
stakx-더 이상

"io 기능과 형식 지정 기능 혼합"<-무엇이 잘못 되었나요? 그것이 도서관의 요점입니다. 새로운 스트림 만들기와 관련하여 스트림 대신 streambuf를 만들고 streambuf 주위에 일반 스트림을 구성해야합니다.
Billy ONeal

... 내가 대신 스트림의 streambuf의를 도출해야합니다 :이 질문에 대한 답변은 제가 설명 적이 있다는 것을 이해하게 보인다
아드 Plisson

@ stakx : streambuf 레이어가 말한 것을 수행하면 괜찮을 것입니다. 그러나 문자 순서와 바이트 간의 변환은 모두 실제 I / O (파일, 콘솔 등)와 혼합됩니다. 문자 변환을 수행하지 않고 파일 I / O를 수행 할 수있는 방법은 없습니다. 이는 매우 불행한 일입니다.
벤 Voigt

10

IOStreams 디자인은 확장 성과 유용성면에서 훌륭하다고 생각합니다.

  1. 스트림 버퍼 : boost.iostream 확장 기능 살펴보기 : gzip, 티, 몇 줄로 스트림 복사, 특수 필터 생성 등. 그것 없이는 불가능합니다.
  2. 현지화 통합 및 형식화 통합 수행 할 수있는 작업 확인 :

    std::cout << as::spellout << 100 << std::endl;

    "백"또는 다음을 인쇄 할 수 있습니다.

    std::cout << translate("Good morning")  << std::endl;

    로케일에 따라 "Bonjour"또는 "בוקר טוב"를 인쇄 할 수 있습니다 std::cout!

    이러한 작업은 요오드 스트림이 매우 유연하기 때문에 수행 할 수 있습니다.

더 잘할 수 있을까?

물론 가능합니다! 실제로 개선 할 수있는 많은 것들이 있습니다 ...

현재에서 올바르게 파생하는 것은 상당히 고통 스럽지만 stream_buffer스트림에 추가 서식 정보를 추가하는 것은 쉽지 않습니다.

그러나 몇 년 전을 되돌아 보면 나는 여전히 도서관 디자인이 많은 장점을 가져 오기에 충분했습니다.

항상 큰 그림을 볼 수는 없지만 확장을 위해 포인트를 남겨두면 생각하지 않은 포인트에서도 훨씬 나은 기능을 제공합니다.


5
당신은 점 2에 대한 예는 단순히 같은 것을 사용하는 것보다 더 나은 것이 이유에 대한 설명을 제공 할 수 print (spellout(100));print (translate("Good morning"));이 I / O에서이 디커플링이 포맷으로, 좋은 아이디어처럼 보일와 국제화 것이다.
Schedler

3
언어에 따라 스트림으로 번역 될 수 있기 때문입니다. 즉 : french_output << translate("Good morning"); english_output << translate("Good morning") "Bonjour Good morning"
Artyom

3
한 언어에서는 '<< "text"<< value'를 수행해야하지만 다른 언어에서는 '<< value << "text"'를 수행해야 할 때 현지화가 훨씬 어렵습니다. printf와 비교
Martin Beckett

@Martin Beckett 알고 있습니다. Boost.Locale 라이브러리를 살펴보십시오.이 경우 어떤 일이 발생하고 out << format("text {1}") % value로 번역 될 수 있습니다 "{1} translated". 그래서 잘 작동합니다 ;-).
Artyom

15
"수행 할 수있는"것은 그리 관련이 없습니다. 당신은 프로그래머 입니다. 충분한 노력으로 무엇이든 할 수 있습니다 . 그러나 IOStreams는 수행 할 수있는 대부분의 작업을 달성하는 것이 끔찍한 고통 입니다. 그리고 당신은 일반적으로 당신의 문제에 대해 성능이 크게 향상됩니다.
jalf

2

(이 답변은 제 의견에 근거한 것입니다)

IOStream은 해당 기능보다 훨씬 복잡하다고 생각합니다. C ++로 작성할 때 여전히 "이전 스타일"I / O에 cstdio 헤더를 사용합니다. 참고로 (실제로 중요하지는 않지만 절대 시간 차이는 무시할 수 있지만) IOStream은 CI / O보다 느리게 여러 번 입증되었습니다.


나는 당신이 "기능"보다는 "기능"을 의미한다고 생각합니다. 함수형 프로그래밍은 일반적인 프로그래밍보다 더 나쁜 코드를 생성합니다.
Chris Becke

그 실수를 지적 해 주셔서 감사합니다. 수정 사항을 반영하기 위해 답변을 편집했습니다.
Delan Azabani

5
IOStream은 클래식 stdio보다 거의 느려 야합니다. 확장 가능하고 사용하기 쉬운 I / O 스트림 프레임 워크를 설계하는 작업을 받았다면 실제 병목 현상이 파일 I / O 속도 또는 네트워크 트래픽 대역폭 일 수 있으므로 속도 보조를 판단 할 수 있습니다.
stakx-더 이상

1
I / O 또는 네트워크의 경우 계산 속도가 그다지 중요하지 않다는 데 동의합니다. 그러나 숫자 / 문자열 변환을위한 C ++는을 사용하고 sstringstream있습니다. 속도는 중요하지만 부차적이라고 생각합니다.
Matthieu M.

1
@stakx 파일 I / O 및 네트워크 병목 현상은 매우 작은 '바이트 당'비용의 기능이며 기술 향상으로 인해 크게 줄어 듭니다. 또한 DMA가 주어지면 이러한 오버 헤드로 인해 동일한 시스템의 다른 스레드에서 CPU 시간이 걸리지 않습니다. 따라서 포맷 된 출력을 수행하는 경우 효율적으로 수행하는 비용과 그렇지 않은 작업의 비용은 쉽게 중요 할 수 있습니다 (적어도 디스크 또는 네트워크로 가리지 않고 응용 프로그램의 다른 처리로 인해 그림자가 표시 될 수 있음).
greggo

2

IOStream을 사용할 때 항상 놀라게됩니다.

라이브러리는 이진 지향이 아닌 텍스트 지향으로 보입니다. 파일 스트림에서 이진 플래그를 사용하는 것만으로는 이진 동작을 얻기에 충분하지 않습니다. 위의 사용자 Charles Salvia가 올바르게 관찰했습니다. IOStreams는 형식화 측면 (예 : 출력이 적은 곳, 예를 들어 부동 소수점의 경우 제한된 숫자)을 직렬화 측면 (정보 손실을 원하지 않는 곳)과 혼합합니다. 아마도 이러한 측면들을 분리하는 것이 좋을 것입니다. Boost.Serialization은이 절반을 수행합니다. 원하는 경우 삽입 기 및 추출기로 라우팅하는 직렬화 기능이 있습니다. 이미 두 측면 사이에 긴장이 있습니다.

많은 함수는 또한 의미론을 혼동합니다 (예 : get, getline, ignore 및 read. 일부는 분리 문자를 추출하고 일부는 그렇지 않습니다. 또한 스트림을 구현할 때 이상한 함수 이름 (예 : xsputn, uflow, underflow)이 언급되어 있습니다. wchar_t 변형을 사용하면 상황이 더욱 악화됩니다. wifstream은 멀티 바이트로 변환하지만 wstringstream은 변환하지 않습니다. 이진 I / O가 wchar_t와 함께 즉시 작동하지 않습니다. codecvt를 덮어 씁니다.

c 버퍼링 된 I / O (예 : FILE)는 C ++만큼 강력하지는 않지만보다 투명하고 직관적 인 동작이 훨씬 적습니다.

여전히 IOStream을 우연히 발견 할 때마다 나는 나방처럼 불에 매료됩니다. 아마 정말 영리한 사람이 전체 아키텍처를 잘 살펴보면 좋을 것입니다.


1

질문의 첫 부분에 답하는 것을 도울 수 없습니다 (누가 그랬습니까?). 그러나 다른 게시물에서 대답했습니다.

질문의 두 번째 부분 (잘 설계 되었습니까?)과 관련하여 제 대답은 "아니요!"입니다. 몇 년 후 불신으로 머리를 흔들게하는 작은 예가 있습니다.

#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 바이트를 문자로 취급하지만 더 큰 정수 유형은 숫자처럼 취급합니다. Qed 나쁜 디자인.

이 문제를 해결할 생각도 없습니다. 유형은 float 또는 double 일 수도 있습니다 ... 따라서 바보 같은 요오 스트림을 만들기 위해 'int'로 캐스팅하면 문자가 아닌 숫자는 주제가 도움이되지 않습니다.

내 대답에 대한 투표를받은 후 몇 가지 설명이 더 필요할 수 있습니다 ... IOStream 디자인은 프로그래머에게 항목을 처리하는 방법을 알려주는 수단을 제공하지 않기 때문에 결함이 있습니다. IOStream 구현은 임의의 결정을합니다 (예 : uint8_t를 바이트 번호가 아닌 문자로 처리). 이것은 달성 불가능한 목표를 달성하려고 시도함에 따라 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 );

3
비난은 (순전히) 요오드와 거짓말을하지 않는다. typedefuint8_t 가 무엇인지 확인하십시오 . 실제로 문자입니까? 그런 다음 iostream을 char처럼 취급한다고 비난하지 마십시오.
Martin Ba

그리고 일반 코드로 숫자를 얻으려면 스트림 삽입 연산자 대신 num_put패싯을 사용할 수 있습니다 .
Martin Ba

@Martin Ba 당신 말이 맞아요-c / c ++ 표준은 "short unsigned int"가 몇 바이트 나 열어 두 었는가? "부호없는 문자"는 언어의 특유한 표현입니다. 바이트를 정말로 원한다면 부호없는 문자를 사용해야합니다. C ++은 또한 "숫자 만"과 같은 템플릿 인수에 제한을 둘 수 없으므로 ShowVector 구현을 제안 된 num_put 솔루션으로 변경하면 ShowVector가 더 이상 문자열 벡터를 표시 할 수 없습니까? ;)
BitTickler

1
@Martin Bla : cppreference는 int8_t가 정확히 8 비트의 부호있는 정수 유형이라고 언급합니다 .Iostream에서 chardef의 typedef 및 과부하로 기술적으로 설명 할 수 있지만 가비지 출력을 얻는 것이 이상하다고 저자는 동의합니다 . typedef 대신 __int8에 true 형식을 사용하면 해결할 수 있습니다.
gast128

오, 실제로 고치기는 꽤 쉽다 : // unsigned / signed / char 타입을 지원하지 않는 std :: ostream 수정 // 문자처럼 8 비트 정수를 출력한다. 네임 스페이스 ostream_fixes {인라인 std :: ostream & 연산자 << (std :: ostream & os, 부호없는 문자 i) {return os << static_cast <unsigned int> (i); } 인라인 std :: ostream & 연산자 << (std :: ostream & os, 부호있는 문자 i) {return os << static_cast <signed int> (i); }} // 네임 스페이스 ostream_fixes
mcv

1

C ++ iostream에는 다른 응답에서 언급했듯이 많은 결함이 있지만 방어에 무언가를 언급하고 싶습니다.

C ++은 초보자를 위해 변수 입력 및 출력을 간단하게하는 언어 사용이 매우 독특합니다. 다른 언어에서는 사용자 입력에 유형 강제 또는 문자열 포맷터가 포함되는 반면 C ++에서는 컴파일러가 모든 작업을 수행합니다. C ++이 이와 관련하여 고유하지는 않지만 출력에 대해서도 마찬가지입니다. 여전히 클래스와 객체 지향 개념을 이해할 필요없이 교육적으로 유용하고 형식 구문을 이해할 필요없이 C ++에서 형식화 된 I / O를 꽤 잘 수행 할 수 있습니다. 다시 말하지만, 초보자를 가르치는 경우 큰 장점입니다.

초보자를위한이 단순성은 가격이 비싸기 때문에보다 복잡한 상황에서 I / O를 다루는 데 어려움을 겪을 수 있지만, 그 시점에서 프로그래머가이를 처리 할 수있을만큼 충분히 배웠거나 최소한 나이가 들었 으면합니다. 마시다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.