Почему std::basic_fstream <unsigned char> не работает?

При попытке скомпилировать этот код:

std::fstream file("file.name", std::ios::out | std::ios::binary);
uint8_t buf[BUFSIZE];
//Fill the buffer, etc...
file.write(buf, BUFSIZE);

компилятор выдаст мне предупреждение об очень плохом преобразовании из unsigned char к char в призыв к write(). В виде std::fstream на самом деле просто typedef для std::basic_fstream<char>можно было подумать, что используя std::basic_fstream<uint8_t> вместо этого позволит им скомпилировать приведенный выше код без предупреждения, так как write() ожидает указатель типа шаблона.

Это, конечно, работает, но выскакивает другая проблема. Хотя этот код компилируется отлично:

std::basic_fstream<uint8_t> file("file.name", std::ios::out | std::ios::binary);
uint8_t buf[BUFSIZE];
//Fill the buffer, etc...
file.write(buf, BUFSIZE);

теперь он не сможет позвонить write(), хотя предыдущая версия работала (не обращайте внимания на предупреждения компилятора). Мне потребовалось время, чтобы точно определить, откуда возникает исключение в коде стандартной библиотеки C++, но я до сих пор не совсем понимаю, в чем тут дело. Это выглядит как std::basic_fstream использует механизм кодирования нескольких символов, и поскольку он определен для char но нет для unsigned char, файловый поток молча терпит неудачу при попытке использовать "неправильный" символьный тип данных... По крайней мере, я так это вижу.

Но вот чего я не понимаю. Кодировка символов не требуется. Я даже не открываю файл в текстовом режиме, я хочу иметь дело с двоичными данными. Поэтому я использую массивы типа uint8_t, а не char, кажется более естественным использовать этот тип данных, а не старый char. Но прежде чем я решу сдаться uint8_t тип данных и просто согласитесь работать с char буферов или начните использовать массивы настраиваемых byte тип данных, определенный как char, Хочу задать два вопроса:

  1. Что это за механизм, который мешает мне использовать тип данных без знака? Это действительно что-то связано с кодировкой символов или служит какой-то другой цели? Почему файловый поток отлично работает со знаковыми символьными типами данных, но не с беззнаковыми?
  2. Предполагая, что я все еще хочу использовать std::basic_fstream<uint8_t>, независимо от того, насколько (не) разумно это - есть ли способ добиться этого?

2 ответа

Решение

std::basic_fstream<unsigned char> не работает, потому что он использует std::char_traits<unsigned char> но стандартная библиотека не предоставляет такой специализации, см. std::char_traits для получения полной информации.

Если вы хотите читать / писать двоичные данные, вам необходимо использовать std::basic_fstream<char>, откройте его с помощью std::ios_base::binary флаг и использование std::basic_ostream<CharT,Traits>::write функция для записи двоичных данных.

Это немного наследие, так как все charтипы могут использоваться для представления двоичных данных. Стандартная библиотека использует char вероятно, потому что это самый короткий текст, который нужно набрать и прочитать.


Что это за механизм, который мешает мне использовать тип данных без знака?

Нет std::char_traits<unsigned char> специализация.

Это действительно что-то связано с кодировкой символов или служит какой-то другой цели?

std::char_traitsимеет несколько целей, точно определенных в его интерфейсе, но не включает декодирование / кодирование. Последнее делается codecvt см. пример использования.

Почему файловый поток отлично работает со знаковыми символьными типами данных, но не с беззнаковыми?

Потому как std::basic_ostream<CharT,Traits>::write принимает CharT, первый параметр шаблона, который вы указываете для потока. Он записывает тот же тип символов, который читает, и использует этот codecvt конвертировать из CharT в байты.

Предполагая, что я все еще хочу использовать std::basic_fstream<uint8_t>, независимо от того, насколько (не) разумно это - есть ли способ добиться этого?

Стандартные шаблоны классов и функций не могут быть специализированы для встроенных типов, если я не ошибаюсь. Вам нужно будет создать другой класс с std::char_traitsinterface и укажите это как второй аргумент шаблона для стандартных потоков. Думаю, вам понадобится довольно веская (философская) причина, чтобы закатать рукава и сделать это.

Если вы этого не сделаете, вы можете продолжать использовать std::fstream<char> и делай stream.write(reinterpret_cast<char const*>(buf), sizeof buf);.

Фактически char и uint8_tмогут быть разных видов. Это также означает, что они могут иметь разные std::char_traits. Тип характеристик персонажа - это второй параметр шаблона в std::basic_fstream, который по умолчанию std::char_traits создается с символьным типом. std::basic_fstreamпо умолчанию выполняет форматированный ввод-вывод через параметр шаблона характеристик символов. Он не просто перенаправляет необработанные байты без изменений. Возможно, поэтому вы получаете разные результаты.

Другие вопросы по тегам