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.)
std::streambuf
adalah kelas dasar untuk membaca dan menulis byte, danistream
/ostream
untuk in- dan output yang diformat, menggunakan pointerstd::streambuf
sebagai tujuan / sumbernya.ostream foo(&somebuffer); foo << "huh"; foo.rdbuf(cout.rdbuf()); foo << "see me!";
Jawaban:
Beberapa ide disalahpahami menemukan cara mereka ke standar:
auto_ptr
,vector<bool>
,valarray
danexport
, 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.
sumber
comp.lang.c++.moderated
arsip dan memposting tautan di bagian bawah pertanyaan saya jika saya menemukan sesuatu yang berharga. - Selain itu, saya berani tidak setuju dengan Anda padaauto_ptr
: Setelah membaca C ++ Luar Biasa Herb Sutter sepertinya kelas yang sangat berguna ketika menerapkan pola RAII.unique_ptr
dengan semantik yang lebih jelas dan lebih kuat.unique_ptr
membutuhkan referensi nilai. Jadi pada titikauto_ptr
ini pointer sangat kuat.auto_ptr
telah mengacaukan salinan / tugas semantik yang membuatnya menjadi ceruk untuk bug dereferencing ...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.
sumber
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:
Jika sebaliknya, Anda menulis sesuatu seperti:
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 menggunakanstringstream
sebagai cara standar untuk mengkonversi ke string.Dukungan buruk untuk i18n
Output berbasis Iostream membagi string string menjadi beberapa bagian.
Format string menempatkan seluruh kalimat ke dalam string literal.
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.sumber
$
specifier POSIXprintf
.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
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
xsputn
atau 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:
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_time
atauboost::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 melaluiSerializable
antarmuka.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.
sumber
std::char_traits
tidak bisa dibuat khusus untuk mengambilunsigned 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.std::streambuf
. Jadi, pada dasarnya satu-satunya hal yang Anda perluas adalahstd::basic_ios
kelas. 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.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 ...
sumber
Saya pikir desain IOStreams brilian dalam hal perpanjangan dan kegunaan.
Integrasi lokalisasi dan integrasi format. Lihat apa yang bisa dilakukan:
Dapat mencetak: "seratus" atau bahkan:
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.
sumber
print (spellout(100));
danprint (translate("Good morning"));
Ini akan tampak seperti ide yang baik, karena ini memisahkan format dan i18n dari I / O.french_output << translate("Good morning")
:;english_output << translate("Good morning")
akan memberi Anda: "Selamat pagi Bonjour"out << format("text {1}") % value
dan mungkin diterjemahkan ke"{1} translated"
. Jadi itu berfungsi dengan baik;-)
.(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.
sumber
sstringstream
. Saya pikir kecepatan itu penting, meskipun itu sekunder.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.
sumber
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:
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:
sumber
uint8_t
adalah typedef untuk. Apakah itu sebenarnya char? Maka jangan salahkan iostreams untuk memperlakukannya seperti arang.num_put
facet alih-alih operator penyisipan streaming.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.
sumber