Siapa yang merancang / merancang IOStreams C ++, dan apakah itu masih dianggap dirancang dengan baik oleh standar saat ini? [Tutup]

127

Pertama, sepertinya saya meminta pendapat subjektif, tapi bukan itu yang saya cari. Saya ingin mendengar beberapa argumen kuat tentang topik ini.


Dengan harapan mendapatkan wawasan tentang bagaimana aliran modern / kerangka kerja serialisasi seharusnya dirancang, saya baru-baru ini mendapatkan sendiri salinan buku Standard C ++ IOStreams dan Locales oleh Angelika Langer dan Klaus Kreft . Saya pikir jika iOStreams tidak dirancang dengan baik, itu tidak akan membuatnya menjadi pustaka standar C ++.

Setelah membaca berbagai bagian buku ini, saya mulai ragu apakah iOStreams dapat dibandingkan dengan misalnya STL dari sudut pandang arsitektur keseluruhan. Baca misalnya wawancara ini dengan Alexander Stepanov ("penemu" STL) untuk mempelajari tentang beberapa keputusan desain yang masuk ke dalam STL.

Apa yang mengejutkan saya khususnya :

  • Tampaknya tidak diketahui siapa yang bertanggung jawab atas desain keseluruhan IOStreams (Saya ingin membaca beberapa informasi latar belakang tentang ini - apakah ada yang tahu sumber daya yang baik?);

  • Setelah Anda mempelajari di bawah permukaan langsung iOStreams, mis. Jika Anda ingin memperpanjang iOStreams dengan kelas Anda sendiri, Anda bisa masuk ke antarmuka dengan nama fungsi anggota yang cukup samar dan membingungkan, misalnya getloc/ imbue, uflow/ underflow, snextc/ sbumpc/ sgetc/ / sgetn, pbase/ pptr/ epptr(dan ada mungkin contoh yang lebih buruk). Ini membuatnya jauh lebih sulit untuk memahami desain keseluruhan dan bagaimana bagian tunggal bekerja sama. Bahkan buku yang saya sebutkan di atas tidak membantu bahwa banyak (IMHO).


Demikian pertanyaan saya:

Jika Anda harus menilai dengan standar rekayasa perangkat lunak saat ini (jika memang ada kesepakatan umum mengenai hal ini), apakah IOStreams C ++ masih dianggap dirancang dengan baik? (Saya tidak ingin meningkatkan keterampilan desain perangkat lunak saya dari sesuatu yang umumnya dianggap ketinggalan zaman.)

stakx - tidak lagi berkontribusi
sumber
7
Pendapat Herb Sutter yang menarik stackoverflow.com/questions/2485963/... :) Sayang sekali orang itu meninggalkan SO setelah hanya beberapa hari berpartisipasi
Johannes Schaub - litb
5
Apakah ada orang lain yang melihat campuran kekhawatiran dalam aliran STL? Aliran biasanya dirancang untuk membaca atau menulis byte dan tidak ada yang lain. Suatu hal yang dapat membaca atau menulis tipe data tertentu adalah formatter (yang mungkin tetapi tidak perlu menggunakan aliran untuk membaca / menulis byte yang diformat). Menggabungkan keduanya menjadi satu kelas membuatnya semakin kompleks untuk mengimplementasikan stream sendiri.
mmmmmmmm
4
@Revven, ada pemisahan dari kekhawatiran itu. std::streambufadalah kelas dasar untuk membaca dan menulis byte, dan istream/ ostreamuntuk in- dan output yang diformat, menggunakan pointer std::streambufsebagai tujuan / sumbernya.
Johannes Schaub - litb
1
@ litb: Tapi apakah mungkin untuk mengganti streambuf yang digunakan oleh stream (formatter)? Jadi mungkin saya ingin menggunakan format STL tetapi ingin menulis data melalui streambuf tertentu?
mmmmmmmm
2
@rstevens,ostream foo(&somebuffer); foo << "huh"; foo.rdbuf(cout.rdbuf()); foo << "see me!";
Johannes Schaub - litb

Jawaban:

31

Beberapa ide disalahpahami menemukan cara mereka ke standar: auto_ptr, vector<bool>, valarraydan export, hanya untuk beberapa nama. Jadi saya tidak akan menganggap kehadiran iOStreams sebagai tanda kualitas desain.

IOStreams memiliki sejarah kotak-kotak. Mereka sebenarnya adalah pengerjaan ulang perpustakaan aliran sebelumnya, tetapi ditulis pada saat banyak idiom C ++ saat ini tidak ada, sehingga para desainer tidak mendapatkan manfaat dari tinjauan balik. Satu masalah yang hanya menjadi jelas dari waktu ke waktu adalah bahwa hampir tidak mungkin untuk mengimplementasikan IOStreams seefisien stdio C, karena penggunaan fungsi virtual yang berlebihan dan meneruskan ke objek buffer internal bahkan pada granularity terbaik, dan juga berkat beberapa keanehan yang tidak dapat dipahami. dalam cara lokal didefinisikan dan diimplementasikan. Ingatan saya tentang ini cukup kabur, saya akui; Saya ingat itu menjadi topik perdebatan sengit beberapa tahun yang lalu, di comp.lang.c ++. Dimoderasi.

Marcelo Cantos
sumber
3
Terima kasih atas masukannya. Saya akan menelusuri comp.lang.c++.moderatedarsip dan memposting tautan di bagian bawah pertanyaan saya jika saya menemukan sesuatu yang berharga. - Selain itu, saya berani tidak setuju dengan Anda pada auto_ptr: Setelah membaca C ++ Luar Biasa Herb Sutter sepertinya kelas yang sangat berguna ketika menerapkan pola RAII.
stakx - tidak lagi berkontribusi
5
@stakx: Namun demikian itu semakin usang dan digantikan unique_ptrdengan semantik yang lebih jelas dan lebih kuat.
UncleBens
3
@UncleBens unique_ptrmembutuhkan referensi nilai. Jadi pada titik auto_ptrini pointer sangat kuat.
Artyom
7
Tetapi auto_ptrtelah mengacaukan salinan / tugas semantik yang membuatnya menjadi ceruk untuk bug dereferencing ...
Matthieu M.
5
@TokenMacGuy: ini bukan vektor, dan tidak menyimpan bools. Yang membuatnya agak menyesatkan. ;)
jalf
40

Mengenai siapa yang mendesainnya, perpustakaan asli (tidak mengherankan) dibuat oleh Bjarne Stroustrup, dan kemudian diimplementasikan kembali oleh Dave Presotto. Ini kemudian dirancang ulang dan diimplementasikan kembali oleh Jerry Schwarz untuk Cfront 2.0, menggunakan ide manipulator dari Andrew Koenig. Versi standar perpustakaan didasarkan pada implementasi ini.

Sumber "Desain & Evolusi C ++", bagian 8.3.1.

Quuxplusone
sumber
3
@ Neil - kacang apa pendapat Anda tentang desain? Berdasarkan jawaban Anda yang lain, banyak orang akan senang mendengar pendapat Anda ...
DVK
1
@DVK Baru saja memposting pendapat saya sebagai jawaban terpisah.
2
Baru saja menemukan transkrip wawancara dengan Bjarne Stroustrup di mana ia menyebutkan beberapa bagian dari sejarah IOStreams : www2.research.att.com/~bs/01chinese.html (tautan ini tampaknya rusak untuk sementara waktu sekarang, tetapi Anda dapat mencoba Tembolok halaman Google)
stakx - tidak lagi berkontribusi
2
Tautan yang diperbarui: stroustrup.com/01chinese.html .
FrankHB
28

Jika Anda harus menilai dengan standar rekayasa perangkat lunak saat ini (jika memang ada kesepakatan umum mengenai hal ini), akankah C ++ IOStreams masih dianggap dirancang dengan baik? (Saya tidak ingin meningkatkan keterampilan desain perangkat lunak saya dari sesuatu yang umumnya dianggap ketinggalan zaman.)

Saya akan mengatakan TIDAK , karena beberapa alasan:

Penanganan kesalahan yang buruk

Kondisi kesalahan harus dilaporkan dengan pengecualian, bukan dengan operator void*.

Anti-pola "objek zombie" adalah penyebab bug seperti ini .

Pemisahan yang buruk antara pemformatan dan I / O

Ini membuat objek stream tidak perlu rumit, karena mereka harus mengandung informasi status tambahan untuk pemformatan, baik Anda memerlukannya atau tidak.

Ini juga meningkatkan kemungkinan menulis bug seperti:

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

Jika sebaliknya, Anda menulis sesuatu seperti:

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

Tidak akan ada bit status terkait-format, dan tidak ada masalah.

Perhatikan bahwa dalam bahasa "modern" seperti Java, C #, dan Python, semua objek memiliki fungsi toString/ ToString/ __str__yang dipanggil oleh rutinitas I / O. AFAIK, hanya C ++ yang melakukan sebaliknya dengan menggunakan stringstreamsebagai cara standar untuk mengkonversi ke string.

Dukungan buruk untuk i18n

Output berbasis Iostream membagi string string menjadi beberapa bagian.

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

Format string menempatkan seluruh kalimat ke dalam string literal.

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

Pendekatan terakhir lebih mudah untuk beradaptasi dengan perpustakaan internasionalisasi seperti GNU gettext, karena penggunaan seluruh kalimat memberikan lebih banyak konteks bagi para penerjemah. Jika rutinitas pemformatan string Anda mendukung pemesanan ulang (seperti $parameter POSIX printf), maka juga lebih baik menangani perbedaan urutan kata di antara bahasa.

dan04
sumber
4
Sebenarnya, untuk i18n, penggantian harus diidentifikasi oleh posisi (% 1,% 2, ..), karena terjemahan mungkin perlu mengubah urutan parameter. Kalau tidak, saya sepenuhnya setuju - +1.
peterchen
4
@ Petereter: Itulah tujuan $specifier POSIX printf.
jamesdlin
2
Masalahnya bukan format string, itu karena C ++ memiliki varargs non-typesafe.
dan04
5
Pada C ++ 11 sekarang memiliki vararg typesafe.
Mooing Duck
2
IMHO 'informasi negara tambahan' adalah masalah terburuk. Cout adalah global; melampirkan pemformatan bendera untuk itu membuat bendera tersebut mendunia, dan ketika Anda menganggap bahwa sebagian besar penggunaannya memiliki cakupan beberapa garis yang dimaksudkan, itu cukup mengerikan. Mungkin untuk memperbaikinya dengan kelas 'formatter', yang mengikat ke ostream tetapi mempertahankan kondisinya sendiri. Dan, hal-hal yang dilakukan dengan cout umumnya terlihat mengerikan dibandingkan dengan hal yang sama dilakukan dengan printf (bila memungkinkan) ..
greggo
17

Saya memposting ini sebagai jawaban terpisah karena ini adalah pendapat murni.

Melakukan input & output (khususnya input) adalah masalah yang sangat, sangat sulit, jadi tidak mengherankan perpustakaan iostreams penuh dengan hal-hal dan hal-hal yang dengan tinjauan sempurna bisa dilakukan dengan lebih baik. Tapi bagi saya sepertinya semua perpustakaan I / O, dalam bahasa apa pun seperti ini. Saya tidak pernah menggunakan bahasa pemrograman di mana sistem I / O adalah sesuatu yang indah yang membuat saya kagum pada perancangnya. Pustaka iostreams memang memiliki kelebihan, terutama di atas pustaka CI / O (ekstensibilitas, keamanan-jenis, dll.), Tapi saya tidak berpikir ada yang mengangkatnya sebagai contoh OO yang hebat atau desain generik.


sumber
16

Pendapat saya tentang C ++ iostreams telah meningkat secara substansial dari waktu ke waktu, terutama setelah saya mulai memperluasnya dengan menerapkan kelas stream saya sendiri. Saya mulai menghargai ekstensibilitas dan desain keseluruhan, meskipun nama fungsi anggota yang sangat buruk seperti xsputnatau apa pun. Apapun, saya pikir I / O stream adalah peningkatan besar-besaran atas C stdio.h, yang tidak memiliki jenis keamanan dan penuh dengan kelemahan keamanan utama.

Saya pikir masalah utama dengan aliran IO adalah bahwa mereka mengacaukan dua konsep yang terkait tetapi agak ortogonal: pemformatan teks dan serialisasi. Di satu sisi, stream IO dirancang untuk menghasilkan representasi tekstual yang dapat dibaca oleh manusia dari suatu objek, dan di sisi lain, untuk membuat cerita bersambung objek menjadi format portabel. Terkadang kedua tujuan ini adalah satu dan sama, tetapi di lain waktu hal ini menghasilkan beberapa ketidakcocokan yang sangat mengganggu. Sebagai contoh:

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

...

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

Di sini, apa yang kita dapatkan sebagai input bukanlah apa yang awalnya kita hasilkan ke aliran. Ini karena <<operator mengeluarkan seluruh string, sedangkan >>operator hanya akan membaca dari aliran sampai bertemu karakter spasi, karena tidak ada informasi panjang yang disimpan dalam aliran. Jadi meskipun kita mengeluarkan objek string yang berisi "hello world", kita hanya akan memasukkan objek string yang berisi "halo". Jadi, sementara aliran telah melayani tujuannya sebagai fasilitas pemformatan, ia gagal untuk membuat cerita bersambung dengan benar dan kemudian meng-unserialize objek.

Anda mungkin mengatakan bahwa stream IO tidak dirancang untuk menjadi fasilitas serialisasi, tetapi jika itu masalahnya, untuk apa sebenarnya stream input ? Selain itu, dalam praktiknya I / O stream sering digunakan untuk membuat serialisasi objek, karena tidak ada fasilitas serialisasi standar lainnya. Pertimbangkan boost::date_timeatau boost::numeric::ublas::matrix, di mana jika Anda mengeluarkan objek matriks dengan <<operator, Anda akan mendapatkan matriks yang sama persis ketika Anda memasukkannya menggunakan >>operator. Tetapi untuk mencapai hal ini, desainer Boost harus menyimpan informasi jumlah kolom dan jumlah baris sebagai data tekstual dalam output, yang membahayakan tampilan yang dapat dibaca manusia. Sekali lagi, kombinasi canggung fasilitas pemformatan tekstual dan serialisasi.

Perhatikan bagaimana sebagian besar bahasa lain memisahkan kedua fasilitas ini. Di Jawa, misalnya, pemformatan dilakukan melalui toString()metode, sementara serialisasi dilakukan melalui Serializableantarmuka.

Menurut pendapat saya, solusi terbaik adalah dengan memperkenalkan stream berbasis byte , di samping stream berbasis karakter standar . Streaming ini akan beroperasi pada data biner, tanpa memperhatikan format / tampilan yang dapat dibaca manusia. Mereka dapat digunakan semata-mata sebagai fasilitas serialisasi / deserialisasi, untuk menerjemahkan objek C ++ ke dalam urutan byte portabel.

Charles Salvia
sumber
terimakasih telah menjawab. Saya mungkin salah tentang hal ini, tetapi mengenai poin terakhir Anda (stream berbasis byte vs karakter), bukankah jawaban IOStream (parsial?) Tentang pemisahan antara buffer aliran (konversi karakter, transportasi, dan buffering) dan stream (pemformatan / penguraian)? Dan tidak bisakah Anda membuat kelas aliran baru, yang dimaksudkan hanya untuk serialisasi & deserialisasi (dapat dibaca mesin) dan lainnya yang secara unik diarahkan ke pemformatan & penguraian (yang dapat dibaca manusia)?
stakx - tidak lagi berkontribusi
@stakx, ya, dan sebenarnya, saya telah melakukan ini. Ini sedikit lebih menyebalkan daripada kedengarannya, karena std::char_traitstidak bisa dibuat khusus untuk mengambil unsigned char. Namun, ada solusi, jadi saya kira diperpanjang datang untuk menyelamatkan sekali lagi. Tapi saya pikir fakta bahwa stream berbasis byte tidak standar adalah kelemahan dari perpustakaan.
Charles Salvia
4
Selain itu, menerapkan aliran biner mengharuskan Anda untuk menerapkan kelas aliran baru dan kelas buffer baru, karena masalah format tidak sepenuhnya terpisah darinya std::streambuf. Jadi, pada dasarnya satu-satunya hal yang Anda perluas adalah std::basic_ioskelas. Jadi ada garis di mana "memperluas" melintasi ke wilayah "sepenuhnya reimplementing", dan membuat aliran biner dari fasilitas aliran C ++ I / O tampaknya mendekati titik itu.
Charles Salvia
kata baik & persis apa yang saya curigai. Dan fakta bahwa C dan C ++ berusaha keras untuk tidak membuat jaminan tentang lebar dan representasi bit tertentu memang bisa menjadi masalah ketika melakukan I / O.
stakx - tidak lagi berkontribusi
" Cerita bersambung objek ke dalam format portabel. " Tidak, mereka tidak pernah dimaksudkan untuk dukungan yang
curiousguy
11

saya selalu menemukan C ++ IOStreams dirancang dengan buruk: implementasi mereka membuatnya sangat sulit untuk mendefinisikan aliran jenis baru dengan benar. mereka juga mencampur fitur io dan fitur format (pikirkan tentang manipulator).

secara pribadi, desain dan implementasi aliran terbaik yang pernah saya temukan terletak pada bahasa pemrograman Ada. itu adalah model dalam decoupling, kegembiraan untuk membuat aliran jenis baru, dan fungsi output selalu bekerja terlepas dari aliran yang digunakan. ini berkat penyebut yang paling tidak umum: Anda menghasilkan byte ke aliran dan hanya itu. fungsi aliran berhati-hati menempatkan byte ke dalam aliran, bukan tugas mereka untuk misalnya memformat integer ke dalam heksadesimal (tentu saja, ada satu set atribut tipe, setara dengan anggota kelas, yang ditentukan untuk menangani pemformatan)

Saya berharap C ++ sesederhana tentang stream ...

Adrien Plisson
sumber
Buku yang saya sebutkan menjelaskan arsitektur dasar IOStreams sebagai berikut: Ada lapisan transport (kelas buffer aliran) dan lapisan parsing / pemformatan (kelas aliran). Yang pertama bertanggung jawab untuk membaca / menulis karakter dari / ke bytestream, sedangkan yang kedua bertanggung jawab untuk parsing karakter atau nilai cerita bersambung menjadi karakter. Ini tampaknya cukup jelas, tetapi saya tidak yakin apakah masalah ini benar-benar dipisahkan dalam kenyataan, khususnya. ketika lokal ikut bermain. - Saya juga setuju dengan Anda tentang kesulitan menerapkan kelas stream baru.
stakx - tidak lagi berkontribusi
"mencampur fitur io dan memformat fitur" <- Apa yang salah tentang itu? Itu semacam titik perpustakaan. Mengenai membuat stream baru, Anda harus membuat streambuf alih-alih stream dan membangun stream polos di sekitar streambuf.
Billy ONeal
sepertinya jawaban untuk pertanyaan ini membuat saya mengerti sesuatu yang tidak pernah saya jelaskan: saya harus menurunkan streambuf alih-alih aliran ...
Adrien Plisson
@stakx: Jika lapisan streambuf melakukan apa yang Anda katakan, itu akan baik-baik saja. Tetapi konversi antara urutan karakter dan byte semuanya dicampur dengan I / O yang sebenarnya (file, konsol, dll). Tidak ada cara untuk melakukan file I / O tanpa juga melakukan konversi karakter, yang sangat disayangkan.
Ben Voigt
10

Saya pikir desain IOStreams brilian dalam hal perpanjangan dan kegunaan.

  1. Buffer streaming: lihat ekstensi boost.iostream: buat gzip, tee, salin stream dalam beberapa baris, buat filter khusus, dan sebagainya. Itu tidak akan mungkin tanpa itu.
  2. Integrasi lokalisasi dan integrasi format. Lihat apa yang bisa dilakukan:

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

    Dapat mencetak: "seratus" atau bahkan:

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

    Dapat mencetak "Bonjour" atau "בוקר טוב" sesuai dengan lokasi yang dijiwai std::cout!

    Hal-hal seperti itu dapat dilakukan hanya karena iostreams sangat fleksibel.

Bisakah itu dilakukan lebih baik?

Tentu saja bisa! Bahkan ada banyak hal yang bisa diperbaiki ...

Hari ini cukup menyakitkan untuk diturunkan dengan benar stream_buffer, cukup sepele untuk menambahkan informasi pemformatan tambahan ke streaming, tetapi mungkin.

Tapi melihat ke belakang bertahun-tahun yang lalu saya masih desain perpustakaan cukup baik untuk membawa banyak barang.

Karena Anda tidak selalu dapat melihat gambaran besarnya, tetapi jika Anda meninggalkan poin untuk ekstensi, itu memberi Anda kemampuan yang jauh lebih baik bahkan di poin yang tidak Anda pikirkan.

Artyom
sumber
5
Bisakah Anda memberikan komentar mengapa contoh Anda untuk poin 2 akan lebih baik daripada hanya menggunakan sesuatu seperti print (spellout(100));dan print (translate("Good morning"));Ini akan tampak seperti ide yang baik, karena ini memisahkan format dan i18n dari I / O.
Schedler
3
Karena itu bisa diterjemahkan sesuai dengan bahasa yang dijiwai ke dalam aliran. yaitu french_output << translate("Good morning"):; english_output << translate("Good morning") akan memberi Anda: "Selamat pagi Bonjour"
Artyom
3
Pelokalan jauh lebih sulit ketika Anda perlu melakukan '<< "teks" << nilai' dalam satu bahasa tetapi '<< nilai << "teks"' dalam bahasa lain - dibandingkan dengan printf
Martin Beckett
@ Martin Beckett Saya tahu, lihatlah perpustakaan Boost.Locale, apa yang terjadi dalam kasus seperti itu yang Anda lakukan out << format("text {1}") % valuedan mungkin diterjemahkan ke "{1} translated". Jadi itu berfungsi dengan baik ;-).
Artyom
15
Apa yang "bisa dilakukan" tidak terlalu relevan. Anda seorang programmer, apa pun bisa dilakukan dengan usaha yang cukup. Tapi iOStreams membuatnya sangat menyakitkan untuk mencapai sebagian besar dari apa yang bisa dilakukan . Dan Anda biasanya mendapatkan kinerja buruk untuk masalah Anda.
Jalf
2

(Jawaban ini hanya berdasarkan pendapat saya)

Saya pikir IOStreams jauh lebih kompleks daripada fungsi yang setara. Ketika saya menulis di C ++, saya masih menggunakan header cstdio untuk I / O "gaya lama", yang menurut saya jauh lebih mudah diprediksi. Di samping catatan, (meskipun itu tidak terlalu penting; perbedaan waktu absolut dapat diabaikan) IOS stream telah terbukti pada banyak kesempatan lebih lambat dari CI / O.

Delan Azabani
sumber
Saya pikir maksud Anda "fungsi" daripada "fungsional". pemrograman fungsional menghasilkan kode yang lebih buruk daripada pemrograman generik.
Chris Becke
Terima kasih telah menunjukkan kesalahan itu; Saya telah mengedit jawaban untuk mencerminkan koreksi.
Delan Azabani
5
IOStreams hampir pasti harus lebih lambat daripada stdio klasik; jika saya diberi tugas untuk merancang kerangka kerja stream stream I / O yang dapat diperluas dan mudah digunakan, saya mungkin akan menilai kecepatan sekunder, mengingat bahwa kemacetan nyata kemungkinan akan berupa file kecepatan I / O atau bandwidth lalu lintas jaringan.
stakx - tidak lagi berkontribusi
1
Saya setuju bahwa untuk I / O atau jaringan kecepatan komputasi tidak terlalu penting. Namun ingat bahwa C ++ untuk konversi numerik / string sedang digunakan sstringstream. Saya pikir kecepatan itu penting, meskipun itu sekunder.
Matthieu M.
1
@stakx file I / O dan hambatan jaringan adalah fungsi dari biaya 'per byte' yang cukup kecil, dan didorong secara dramatis oleh peningkatan teknologi. Juga, mengingat DMA, overhead ini tidak menghilangkan waktu CPU dari utas lain pada mesin yang sama. Jadi, jika Anda melakukan output terformat, biaya melakukan itu secara efisien vs tidak, dapat dengan mudah menjadi signifikan (setidaknya, tidak dibayangi oleh disk atau jaringan; lebih besar kemungkinan itu dibayangi oleh pemrosesan lain dalam aplikasi).
greggo
2

Saya selalu mengalami kejutan ketika menggunakan IOStream.

Perpustakaan tampaknya berorientasi teks dan bukan berorientasi biner. Itu mungkin kejutan pertama: menggunakan bendera biner dalam aliran file tidak cukup untuk mendapatkan perilaku biner. Pengguna Charles Salvia di atas telah mengamatinya dengan benar: IOStreams memadukan aspek pemformatan (di mana Anda menginginkan keluaran yang cantik, misalnya angka terbatas untuk pelampung) dengan aspek serialisasi (di mana Anda tidak ingin kehilangan informasi). Mungkin sebaiknya memisahkan aspek-aspek ini. Boost. Generalisasi melakukan setengah ini. Anda memiliki fungsi bersambung yang mengarahkan rute ke penyisip dan ekstraktor jika Anda mau. Sudah ada ketegangan antara kedua aspek.

Banyak fungsi juga memiliki semantik yang membingungkan (mis. Get, getline, abaikan dan baca. Beberapa mengekstrak pembatas, beberapa tidak; juga beberapa mengatur eof). Lebih lanjut tentang beberapa menyebutkan nama fungsi aneh ketika menerapkan aliran (misalnya xsputn, uflow, underflow). Keadaan menjadi lebih buruk ketika seseorang menggunakan varian wchar_t. Wifstream melakukan terjemahan ke multibyte sedangkan wstringstream tidak. Biner I / O tidak bekerja di luar kotak dengan wchar_t: Anda memiliki menimpa codecvt.

I / O buffer C (yaitu FILE) tidak sekuat rekan C ++-nya, tetapi lebih transparan dan memiliki jauh lebih sedikit kontra perilaku intuisi.

Masih setiap kali saya menemukan iOStream, saya tertarik padanya seperti ngengat untuk menembak. Mungkin itu akan menjadi hal yang baik jika beberapa pria yang benar-benar pintar akan melihat keseluruhan arsitektur.

gast128
sumber
1

Saya tidak bisa membantu menjawab bagian pertama dari pertanyaan (Siapa yang melakukan itu?). Tapi itu dijawab di posting lain.

Adapun bagian kedua dari pertanyaan (Dirancang dengan baik?), Jawaban saya adalah "Tidak!". Berikut sedikit contoh yang membuat saya menggelengkan kepala karena tidak percaya sejak bertahun-tahun:

#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;
}

Kode di atas menghasilkan omong kosong karena desain iostream. Untuk beberapa alasan di luar jangkauan saya, mereka memperlakukan byte uint8_t sebagai karakter, sementara tipe integral yang lebih besar diperlakukan seperti angka. Qed Bad design.

Juga tidak ada cara saya bisa memikirkan untuk memperbaikinya. Jenisnya bisa jadi pelampung atau gandanya ... jadi pemain untuk 'int' untuk membuat iostream konyol memahami bahwa angka bukan karakter adalah topik tidak akan membantu.

Setelah menerima suara untuk jawaban saya, mungkin beberapa kata penjelasan ... desain IOStream cacat karena tidak memberikan programmer alat untuk menyatakan BAGAIMANA item diperlakukan. Implementasi IOStream membuat keputusan sewenang-wenang (seperti memperlakukan uint8_t sebagai char, bukan nomor byte). Ini adalah cacat dari desain IOStream, karena mereka mencoba untuk mencapai yang tidak dapat diraih.

C ++ tidak memungkinkan untuk mengklasifikasikan suatu tipe - bahasa tidak memiliki fasilitas. Tidak ada yang namanya is_number_type () atau is_character_type () yang bisa digunakan IOStream untuk membuat pilihan otomatis yang masuk akal. Mengabaikan hal itu dan mencoba lolos dari tebakan adalah cacat desain sebuah perpustakaan.

Diakui, printf () sama-sama gagal bekerja dalam implementasi "ShowVector ()" generik. Tapi itu bukan alasan untuk perilaku iostream. Tetapi sangat mungkin bahwa dalam kasus printf (), ShowVector () akan didefinisikan seperti ini:

template <class _T>
void ShowVector( const char *formatString, const char *title, const std::vector<_T> &v );
BitTickler
sumber
3
Kesalahan tidak (murni) terletak pada iostream. Periksa apa Anda uint8_tadalah typedef untuk. Apakah itu sebenarnya char? Maka jangan salahkan iostreams untuk memperlakukannya seperti arang.
Martin Ba
Dan jika Anda ingin memastikan bahwa Anda mendapatkan nomor dalam kode generik, Anda dapat menggunakan num_putfacet alih-alih operator penyisipan streaming.
Martin Ba
@ Martin Ba Anda benar - c / c ++ standar tetap buka berapa banyak byte yang dimiliki "short unsigned int". "unsigned char" adalah keistimewaan dari bahasa tersebut. Jika Anda benar-benar menginginkan byte, Anda harus menggunakan char yang tidak ditandai. C ++ juga tidak mengizinkan untuk memaksakan pembatasan pada argumen templat - seperti "hanya angka" dan jadi jika saya mengubah implementasi ShowVector ke solusi num_put yang Anda usulkan, ShowVector tidak dapat menampilkan vektor string lagi, bukan? ;)
BitTickler
1
@ Martin Bla: cppreference menyebutkan bahwa int8_t adalah tipe integer yang ditandatangani dengan lebar tepat 8 bit. Saya setuju dengan penulis bahwa aneh jika Anda mendapatkan output sampah, meskipun secara teknis dapat dijelaskan oleh typedef dan overload tipe char di iostream . Itu bisa diselesaikan dengan memiliki __int8 tipe yang benar dan bukan typedef.
gast128
Oh, sebenarnya cukup mudah untuk memperbaikinya: // Perbaikan untuk std :: ostream yang telah merusak dukungan untuk tipe unsigned / signed / char // dan mencetak bilangan bulat 8-bit seperti karakter. 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, char i yang ditandatangani) {return os << static_cast <signed int> (i); }} // namespace ostream_fixes
mcv
1

C ++ iostreams memiliki banyak kekurangan, seperti yang disebutkan dalam respons lain, tetapi saya ingin mencatat sesuatu dalam pembelaannya.

C ++ hampir unik di antara bahasa-bahasa yang digunakan secara serius yang membuat input dan output variabel langsung bagi pemula. Dalam bahasa lain, input pengguna cenderung melibatkan paksaan tipe atau string formatters, sementara C ++ membuat compiler melakukan semua pekerjaan. Hal yang sama sebagian besar berlaku untuk output, meskipun C ++ tidak seunik dalam hal ini. Namun, Anda dapat melakukan I / O yang diformat dengan cukup baik di C ++ tanpa harus memahami konsep kelas dan berorientasi objek, yang bermanfaat secara pedagogis, dan tanpa harus memahami sintaksis format. Sekali lagi, jika Anda mengajar pemula, itu nilai tambah yang besar.

Kesederhanaan untuk pemula ini harus dibayar mahal, yang dapat membuatnya sakit kepala untuk berurusan dengan I / O dalam situasi yang lebih kompleks, tetapi mudah-mudahan pada saat itu programmer telah belajar cukup untuk dapat berurusan dengan mereka, atau setidaknya cukup tua. untuk minum.

pengguna2310967
sumber