Почему 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
, Хочу задать два вопроса:
- Что это за механизм, который мешает мне использовать тип данных без знака? Это действительно что-то связано с кодировкой символов или служит какой-то другой цели? Почему файловый поток отлично работает со знаковыми символьными типами данных, но не с беззнаковыми?
- Предполагая, что я все еще хочу использовать
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_traits
interface и укажите это как второй аргумент шаблона для стандартных потоков. Думаю, вам понадобится довольно веская (философская) причина, чтобы закатать рукава и сделать это.
Если вы этого не сделаете, вы можете продолжать использовать
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
по умолчанию выполняет форматированный ввод-вывод через параметр шаблона характеристик символов. Он не просто перенаправляет необработанные байты без изменений. Возможно, поэтому вы получаете разные результаты.