Хто архітектурно розробляв C ++ IOStreams, і чи вважатиметься він добре розробленим за сьогоднішніми стандартами? [зачинено]


127

По-перше, може здатися, що я прошу суб'єктивних думок, але це не те, що я після цього. Я хотів би почути кілька обґрунтованих аргументів на цю тему.


Сподіваючись отримати деяке розуміння того, як має бути спроектована сучасна структура потоків / серіалізації, я нещодавно отримав собі примірник книги Standard C ++ IOStreams and Locales від Angelika Langer та Klaus Kreft . Я подумав, що якби IOStreams не був належним чином розроблений, це не перетворило б його в стандартну бібліотеку C ++.

Прочитавши різні частини цієї книги, у мене виникають сумніви, чи можна IOStreams порівняти, наприклад, STL з загальної архітектурної точки зору. Прочитайте, наприклад, це інтерв'ю з Олександром Степановим ("винахідником" STL), щоб дізнатись про деякі дизайнерські рішення, які увійшли до STL.

Що мене особливо дивує :

  • Здається, невідомо, хто відповідав за загальний дизайн IOStreams (я хотів би прочитати довідкову інформацію про це - хтось знає хороші ресурси?);

  • Після того, як ви заглибитесь під безпосередню поверхню IOStreams, наприклад, якщо ви хочете розширити IOStreams своїми власними класами, ви потрапляєте на інтерфейс із досить криптовими і заплутаними іменами функцій членів, наприклад, getloc/ imbue, uflow/ underflow, snextc/ sbumpc/ sgetc/ sgetn, pbase/ pptr/ epptr(і є мабуть, ще гірші приклади). Це ускладнює розуміння загальної конструкції та взаємодії окремих деталей. Навіть книга , яку я згадував вище , не допомагає , що багато (ІМХО).


Отже, моє запитання:

Якби вам довелося судити за сьогоднішніми стандартами програмної інженерії (якщо насправді існує якась загальна згода на це), чи все-таки IOStream C ++ все ще вважався б добре розробленим? (Я не хотів би вдосконалювати свої навички проектування програмного забезпечення на основі того, що зазвичай вважається застарілим.)


7
Цікава думка Herb Sutter stackoverflow.com/questions/2485963/… :) Шкода, що хлопець покинув SO після лише декількох днів участі
Йоганнес Шауб - запалив

5
Чи є ще хтось, хто бачить змішування проблем у потоках STL? Потік, як правило, призначений для читання або запису байтів і нічого іншого. Річ, яка може читати або записувати конкретні типи даних, - це форматер (який може не потребувати використання потоку для читання / запису відформатованих байтів). Змішування обох в один клас робить ще складнішим реалізацію власних потоків.
ммммммммм

4
@rsteven, існує поділ цих проблем. std::streambufє базовим класом для читання та запису байтів та istream/ ostreamє для форматованого вводу та виводу, беручи вказівник на std::streambufмісце призначення / джерело.
Йоханнес Шауб - ліб

1
@litb: Але чи можна переключити streambuf, який використовується потоком (форматором)? Тож, можливо, я хочу використовувати форматування STL, але хочу записати дані через певний потік даних?
ммммммммм

2
@rstevens,ostream foo(&somebuffer); foo << "huh"; foo.rdbuf(cout.rdbuf()); foo << "see me!";
Йоханнес Шауб - ліб

Відповіді:


31

Кілька непродумані ідеї знайшли свій шлях у стандарт: auto_ptr, vector<bool>, valarrayі export, просто назвати декілька. Тому я б не сприймав наявність IOStreams обов'язково як знак якісного дизайну.

IOStreams має картату історію. Вони насправді є переробкою більш ранньої бібліотеки потоків, але були автором у той час, коли багато сучасних ідіом C ++ не існувало, тому дизайнери не мали переваги заднього огляду. Одне питання, яке стало очевидним лише з часом, - майже неможливо реалізувати IOStreams так ефективно, як stdio C, завдяки рясному використанню віртуальних функцій та переадресації до внутрішніх буферних об’єктів навіть при найтоншій деталізації, а також завдяки деякій непереборній дивацтві у способі визначення та реалізації локальних локалів. Моя пам'ять про це досить нечітка, визнаю; Я пам’ятаю, що вона була предметом бурхливих дебатів кілька років тому на моделях comp.lang.c ++.


3
Дякую за ваш внесок Я перегляну comp.lang.c++.moderatedархів і розміщую посилання внизу запитання, якщо знайду щось цінне. - Крім того, я наважуся не погодитися з вами з приводу auto_ptr: Після прочитання винятку C ++ Herb Sutter це здається дуже корисним класом при реалізації схеми RAII.
stakx - більше не надсилається

5
@stakx: Тим не менш, це стає застарілим і витісняється unique_ptrчіткішою та потужнішою семантикою.
UncleBens

3
@UncleBens unique_ptrвимагає посилання на rvalue. Так що на даний момент auto_ptrдуже потужний покажчик.
Артем

7
Але auto_ptrнакручена семантика копіювання / присвоєння, що робить її нішею для відліку помилок ...
Матьє М.

5
@TokenMacGuy: це не вектор, і він не зберігає булі. Що робить його дещо оманливим. ;)
jalf

40

Що стосується того, хто їх розробив, оригінальну бібліотеку створив (не дивно) Б'ярн Струструп, а потім повторно доповнив Дейв Пресотто. Потім він був перероблений і знову доповнений Джеррі Шварцом для Cfront 2.0, використовуючи ідею маніпуляторів від Ендрю Кеніга. На цій реалізації базується стандартна версія бібліотеки.

Джерело "Дизайн та еволюція C ++", розділ 8.3.1.


3
@Neil - гайка, яка ваша думка щодо дизайну? Виходячи з інших ваших відповідей, багато людей хотіли б почути вашу думку ...
DVK

1
@DVK Щойно опублікував мою думку як окрему відповідь.

2
Щойно знайшов стенограму інтерв'ю з Bjarne Stroustrup, де він згадує про деякі біти та фрагменти історії IOStreams : www2.research.att.com/~bs/01chinese.html (це посилання, здається, тимчасово порушено, але ви можете спробувати Кеш сторінки сторінки Google)
stakx - більше не надсилається

2
Оновлене посилання: stroustrup.com/01chinese.html .
FrankHB

28

Якби вам довелося судити за сьогоднішніми стандартами інженерії програмного забезпечення (якщо насправді існує якась загальна згода на це), чи вважали б IOStream C ++ таки добре продуманими? (Я не хотів би вдосконалювати свої навички проектування програмного забезпечення на основі того, що зазвичай вважається застарілим.)

Я б сказав « НІ» з кількох причин:

Погане поводження з помилками

Про умови помилок слід повідомляти з винятками, а не з operator void*.

Антидіаграма "об’єкт зомбі" - це те, що викликає подібні помилки .

Погане розмежування між форматуванням і введенням / виведенням

Це робить об'єкти потоку непотрібними складними, оскільки вони повинні містити додаткову інформацію про стан для форматування, потрібна вона вам чи ні.

Це також збільшує шанси написання помилок на кшталт:

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, всі об'єкти мають функцію 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 ++ є неширокі вараги.
dan04

5
Станом на C ++ 11, тепер він має безпечні типи varargs.
Mooing Duck

2
ІМХО "додаткова інформація про стан" є найгіршим питанням. cout - глобальний; додавання до нього прапорів форматування робить ці прапори глобальними, і якщо ви вважаєте, що більшість застосувань мають передбачуваний обсяг у кілька рядків, це досить жахливо. Це могло б виправити це класом "формат", який прив'язується до потоку, але зберігає власний стан. І все, що робиться з cout, як правило, виглядає жахливо порівняно з тим же, що робиться з printf (коли це можливо) ..
greggo

17

Я публікую це як окрему відповідь, оскільки це чиста думка.

Виконання вводу та виводу (особливо вхідних даних) - дуже, дуже важка проблема, тому не дивно, що бібліотека iostreams повна курганів і речей, які з ідеальним оглядом могли бути зроблені краще. Але мені здається, що всі бібліотеки вводу-виводу будь-якою мовою такі. Я ніколи не використовував мову програмування, де система вводу / виводу була предметом краси, що змусило мене в захваті від свого дизайнера. Бібліотека iostreams має переваги, особливо над бібліотекою CI / O (розширюваність, безпека типу тощо), але я не думаю, що хтось вважає це прикладом чудового OO або загального дизайну.


16

Моя думка щодо іонів C ++ значно покращилася з часом, особливо після того, як я почав фактично їх розширювати, застосовуючи власні класи потоків. Я почав цінувати розширюваність та загальний дизайн, незважаючи на смішно погані назви функцій членів, як xsputnби то не було. Незважаючи на те, я думаю, що потоки вводу / виводу є значним покращенням порівняно з C stdio.h, який не має безпеки типу і пронизаний великими недоліками безпеки.

Я думаю, що головна проблема потоків IO полягає в тому, що вони поєднують два споріднених, але дещо ортогональних поняття: текстове форматування та серіалізація. З одного боку, потоки вводу-виводу призначені для створення читаного у форматі тексту, зображеного людиною, і, з іншого боку, для серіалізації об'єкта у портативному форматі. Іноді ці дві цілі є одними і тими ж, але в інших випадках це призводить до серйозних роздратованих недоліків. Наприклад:

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

...

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

Тут те, що ми отримуємо як вхід, - це не те, що ми спочатку виводили на потік. Це пояснюється тим, що <<оператор виводить весь рядок, тоді як >>оператор буде читати з потоку лише доти, доки він не зустріне символ пробілу, оскільки в потоці немає інформації про довжину . Тож навіть якщо ми виводимо об’єкт рядка, що містить "привіт світ", ми збираємось лише вводити об'єкт рядка, що містить "привіт". Тому, хоча потік слугував своїм призначенням як інструмент форматування, він не зміг належним чином серіалізувати та потім несеріалізувати об’єкт.

Ви можете сказати, що потоки введення-виведення не були розроблені як засоби серіалізації, але якщо це так, для чого насправді вхідні потоки? Крім того, на практиці потоки вводу / виводу часто використовуються для серіалізації об'єктів, оскільки немає інших стандартних засобів серіалізації. Подумайте, boost::date_timeабо boost::numeric::ublas::matrix, якщо ви виведете об'єкт матриці з <<оператором, ви отримаєте таку ж точну матрицю, коли введете її за допомогою >>оператора. Але для того, щоб досягти цього, дизайнерам Boost довелося зберігати інформацію про кількість стовпців та кількість рядків у вигляді текстових даних у висновку, що компрометує фактичний для людини доступний для читання дисплей. Знову незручне поєднання засобів текстового форматування та серіалізації.

Зверніть увагу, як більшість інших мов розділяє ці два засоби. Наприклад, у Java форматування здійснюється за допомогою toString()методу, тоді як серіалізація здійснюється через Serializableінтерфейс.

На мою думку, найкращим рішенням було б запровадити потоки на байті , поряд із стандартними потоками на основі символів . Ці потоки працюватимуть на двійкових даних, не турбуючись про читане для людини форматування / відображення. Вони можуть використовуватися виключно як засоби серіалізації / десеріалізації для перекладу об'єктів C ++ у переносні послідовності байтів.


дякую за відповідь. Я цілком можу помилятися з цього приводу, але що стосується вашої останньої точки (байтових та символьних потоків), чи не відповідь IOStream (часткова?) На це розділення між буферами потоків (перетворення символів, транспорт та буферизація) та потоки (форматування / аналіз)? А чи не могли б ви створити нові класи потоків, ті, які призначені виключно для (машиночитаного) серіалізації та десеріалізації та інших, які однозначно орієнтовані на (для людини зрозумілі) форматування та розбір?
stakx - більше не вносять дописи

@stakx, так, і насправді я це зробив. Це трохи більше дратує, ніж це звучить, оскільки std::char_traitsне може бути портативно спеціалізованим для прийому unsigned char. Однак існують обхідні шляхи, тож я здогадуюсь, що розширюваність знову приходить на допомогу. Але я думаю, те, що потоки на основі байтів не є стандартними, є слабкістю бібліотеки.
Чарльз Сальвія

4
Крім того, реалізація бінарних потоків вимагає від вас впровадження нових класів потоків та нових класів буфера, оскільки проблеми форматування не повністю відокремлені від std::streambuf. Отже, в основному єдине, що ви розширюєте - це std::basic_iosклас. Отже, існує лінія, де "розширення" перетинається на "повністю перекомплектуючу" територію, і створюючи бінарний потік із засобів потоку введення-виводу C ++, схоже, наближається до цієї точки.
Чарльз Сальвія

добре сказано & саме те, що я підозрював. А той факт, що і C, і C ++ намагаються не гарантувати конкретні ширини бітів і подання, справді може стати проблематичним, коли справа доходить до вводу-виводу.
stakx - більше не вносить дописи

" серіалізувати об'єкт у портативному форматі ". ні, вони ніколи не мали на меті цього підтримати
цікаво

11

Я завжди знаходив C ++ IOStreams погано розробленими: їх реалізація ускладнює правильне визначення потоку нового типу. вони також змішують функції io та функції форматування (подумайте про маніпулятори).

особисто, найкраща розробка та реалізація потоку, яку я коли-небудь знаходив, лежить у мові програмування Ada. це модель для роз'єднання, радість створювати потоки нового типу, а функції виводу завжди працюють незалежно від використовуваного потоку. це подяка щонайменше загального знаменника: ви виводите байти в потік, і все. функції потоку піклуються про введення байтів у потік, це не їхня робота, наприклад, форматування цілого числа в шістнадцятковий (звичайно, є набір атрибутів типу, еквівалентний члену класу, визначений для обробки форматування)

Я хотів би, щоб C ++ був таким же простим щодо потоків ...


Книга, яку я згадав, пояснює основну архітектуру IOStreams наступним чином: Існує транспортний шар (класи буферних потоків) та рівень розбору / форматування (класи потоків). Перші відповідають за читання / запис символів з / у бітестрім, а другі відповідають за розбір символів або серіалізацію значень у символи. Це здається досить зрозумілим, але я не впевнений, чи справді ці проблеми справді чітко розділені, особливо. коли локали вступають у гру. - Я також погоджуюся з вами щодо складності впровадження нових класів потоків.
stakx - більше не вносяться повідомлення

"змішати функції io та функції форматування" <- Що в цьому поганого? Ось такий сенс бібліотеки. Щодо створення нових потоків, ви повинні зробити streambuf замість потоку та побудувати звичайний потік навколо streambuf.
Біллі ONeal

Здається, відповіді на це запитання змусили мене зрозуміти щось, що мені ніколи не було пояснено: я мав би отримати стриббуф замість потоку ...
Адрієн Пліссон

@stakx: Якщо шар потокового файлу зробив те, що ви сказали, було б добре. Але перетворення між послідовністю символів і байтом змішується з фактичним введенням / виводом (файл, консоль тощо). Немає можливості виконати введення / виведення файлу без того, щоб зробити перетворення символів, що дуже прикро.
Бен Войгт

10

Я думаю, що дизайн IOStreams є блискучим з точки зору розширюваності та корисності.

  1. Потокові буфери: подивіться на розширення boost.iostream: створюйте gzip, tee, копіюйте потоки в декілька рядків, створюйте спеціальні фільтри тощо. Без цього було б неможливо.
  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"));це здасться гарною ідеєю, оскільки це від'єднання форматування та i18n від I / O.
Schedler

3
Тому що це може бути перекладено відповідно до мови, пронизаного потоком. тобто french_output << translate("Good morning"):; english_output << translate("Good morning") подарував би вам: "Доброго дня доброго ранку"
Артем

3
Локалізація набагато складніше, коли вам потрібно зробити "<<" текст "<< значення" однією мовою, але "<< значення <<" текст "'на іншій - порівняно з printf
Мартін Бекетт

@Martin Beckett Я знаю, погляньте на бібліотеку Boost.Locale, що трапляється, що в такому випадку ви робите, out << format("text {1}") % valueі це може бути переведено на "{1} translated". Так це чудово працює ;-).
Артем

15
Що "можна зробити" не дуже актуально. Ви програміст, все, що можна зробити, достатньо зусиль. Але IOStreams робить жахливо болісним досягти більшості того, що можна зробити . І за свої клопоти ви зазвичай отримуєте невдалий виступ.
jalf

2

(Ця відповідь якраз і грунтується на моїй думці)

Я думаю, що IOStreams набагато складніші за їх еквіваленти функцій. Коли я пишу на C ++, я все-таки використовую заголовки cstdio для вводу-виводу "старого стилю", що вважаю набагато передбачуванішим. Зі сторони, (хоча це не дуже важливо; абсолютна різниця у часі незначна) IOStreams неодноразово доводили, що вони повільніші, ніж CI / O.


Я думаю, ви маєте на увазі "функцію", а не "функціональну". функціональне програмування виробляє код, що ще гірше виглядає, як загальне програмування.
Кріс Бекк

Дякуємо, що вказали на цю помилку; Я відредагував відповідь, щоб відобразити виправлення.
Делан Азабані

5
IOStreams майже напевно повинні були бути повільнішими, ніж класичні stdio; якби мені дали завдання розробити розширювану та просту у використанні рамку потоків вводу / виводу, я, мабуть, вважаю швидкість вторинною, враховуючи, що справжніми вузькими місцями, швидше за все, будуть швидкість вводу / виводу файлів або пропускна здатність мережевого трафіку.
stakx - більше не вносяться повідомлення

1
Я погоджуюся, що обчислювальна швидкість для вводу / виводу або мережі не має великого значення. Однак пам’ятайте, що C ++ для числового / рядкового перетворення використовується sstringstream. Я думаю, що швидкість має значення, хоча це другорядне.
Матьє М.

1
Файли вводу / виводу файлів @stakx і вузькі вузькі мережі - це функція «за байтом» витрат, які є досить невеликими, і їх різко знижують вдосконалення технології. Також, враховуючи DMA, ці накладні витрати не забирають час процесора у інших потоків на тій же машині. Таким чином, якщо ви робите форматизований вихід, вартість його ефективного порівняно з відсутністю може бути значною (принаймні, не затьмареною диском або мережею; швидше за все, це затьмарить інша обробка в додатку).
greggo

2

Я завжди стикаюся з сюрпризами під час використання IOStream.

Бібліотека здається текстово орієнтованою, а не бінарною. Це може бути першим сюрпризом: використання бінарного прапора у потоках файлів недостатньо для отримання бінарної поведінки. Користувач Чарльз Сальвія вище зауважив це правильно: IOStreams змішує аспекти форматування (де ви хочете досить вивести, наприклад, обмежені цифри для плавців) з аспектами серіалізації (де ви не хочете втрати інформації). Напевно, було б добре розділити ці аспекти. Boost.Serialization робить цю половину. У вас є функція серіалізації, яка направляє до вставок і витяжок, якщо ви хочете. Там уже є напруга між обома аспектами.

Багато функцій мають також заплутану семантику (наприклад, отримати, отримати лінію, проігнорувати та прочитати. Деякі витягують роздільник, деякі не роблять; також деякі встановлюють eof). Далі дехто згадує назви дивних функцій при реалізації потоку (наприклад, xsputn, uflow, underflow). Все стає ще гірше, коли використовуються варіанти wchar_t. Wifstream робить переклад у багатобайтовий, тоді як wstringstream цього не робить. Бінарний введення / вивід не працює з поля wchar_t: у вас є перезаписати codecvt.

Буферизований введення-виведення (тобто 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 байти як символи, тоді як більші цілісні типи трактуються як числа. Кед поганий дизайн.

Я також не можу придумати це виправити. Тип може бути плаваючим чи подвійним натомість ... тож команда "int", щоб дурний iostream зрозумів, що числа, не символи, це тема не допоможе.

Після отримання голосового відмови на мою відповідь, можливо, ще кілька пояснень ... Дизайн 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
Вина не (чисто) лежить на іостримі. Перевірте , що ваш uint8_tє ЬурейеЕ для. Це насправді чара? Тоді не звинувачуйте іостри, що ставляться до цього, як до шару.
Мартін Ба

І якщо ви хочете переконатися, що ви отримаєте номер у загальному коді, можете використовувати num_putфасетку замість оператора вставки потоку.
Мартін Ба

@Martin Ba Ви праві - стандарти c / c ++ дозволяють відкрити, скільки байтів має "короткий неподписаний int". "непідписаний знак" - це ідіосинкразія мови. Якщо ви дійсно хочете байт, вам доведеться використовувати неподписаний знак. C ++ також не дозволяє накладати обмеження на аргументи шаблону - наприклад, "лише числа", і якщо я змінив реалізацію ShowVector на запропоноване рішенням num_put, ShowVector більше не міг відображати вектор рядків, правда? ;)
BitTickler

1
@Martin Bla: cppreference згадує, що int8_t - це підписаний цілочисельний тип шириною рівно 8 біт. Я погоджуюся з автором, що дивно, що ви отримуєте сміття тоді, хоча це технічно пояснюється typedef та перевантаження типів char у iostream . Це можна було б вирішити, якщо замість typedef був __int8 справжнього типу.
gast128

О, насправді це досить просто виправити: // Виправлення для std :: ostream, яка порушила підтримку непідписаних / підписаних / типів char // та друкує 8-бітні цілі числа, як вони символи. простір імен ostream_fixes {inline std :: ostream & operator << (std :: ostream & os, непідписаний char i) {return os << static_cast <unsigned int> (i); } inline std :: ostream & operator << (std :: ostream & os, підписаний char i) {return os << static_cast <підписаний int> (i); }} // Простір імен ostream_fixes
mcv

1

Як і в інших відповідях, у іонів потоків C ++ є багато недоліків, але я хотів би зазначити щось у його захисті.

C ++ практично унікальний серед мов для серйозного використання, що робить змінний ввід і вихід простим для початківців. В інших мовах введення користувача, як правило, включає типи примусу або форматів рядків, тоді як C ++ змушує компілятор виконувати всю роботу. Це багато в чому стосується випуску, хоча C ++ не настільки унікальний у цьому плані. Тим не менш, ви можете добре відформатувати введення / виведення в C ++, не розуміючи класів та об'єктно-орієнтованих понять, що є педагогічно корисним, і не потребуючи розуміння синтаксису формату. Знову ж таки, якщо ви навчаєте початківців, це великий плюс.

Ця простота для початківців приходить до ціни, що може зробити головний біль для роботи з введенням-виведенням у більш складних ситуаціях, але, сподіваємось, до цього моменту програміст навчився достатньо, щоб можна було з ними впоратися, або хоча б виповнився досить дорослим пити.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.