Почему этот специализированный char_traits & lt; uint8_t & gt; и codecvt & lt; uint8_t & gt; для использования с шаблоном basic_ifstream бросить std :: bad_cast?

Там являются уже вопросы здесь на Stackoverflow спрашивая Зачем basic_fstream<uint8_t> не работает Ответы говорят, что char_traits специализируется только на char а также wchar_t (плюс char16_t, char32_t в C ++ 11), и вы должны придерживаться basic_fstream<char> читать двоичные данные и приводить их при необходимости.

Ну, черт возьми, это не достаточно хорошо! 🙂

Ни один из ответов (которые я могу найти) не говорит как специализироваться char_traits<uint8_t> и использовать его с basic_fstream шаблон или, если это вообще возможно. Так что я решил попробовать сам.

Следующие компиляции без ошибок при использовании Visual Studio Express 2013 RC в Windows 7 64bit и с g ++ — 4.7 в Kubuntu GNU / Linux 13.04 64bit. Однако он генерирует исключение std :: bad_cast во время выполнения. У меня нет доступа к clang ++ с libc ++ для проверки этой комбинации.

#include <cinttypes>
#include <cstring>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <locale>

#ifdef _WIN32
#define constexpr
#define NOEXCEPT throw()
#else
#define NOEXCEPT noexcept
#endif

// Change this to char and it works.
using byte_type = std::uint8_t;

namespace std
{
// Specialization of std::char_traits
template <> struct char_traits< std::uint8_t >
{
using char_type = std::uint8_t;
using int_type = int;
using off_type = std::streamoff;
using pos_type = std::streampos;
using state_type = std::mbstate_t;

static void assign(char_type& value1, const char_type& value2)
{
value1 = value2;
}

static char_type* assign(char_type* ptr, std::size_t count, char_type value)
{
return static_cast<char_type*>(std::memset(ptr, value, count));
}

static constexpr bool eq(const char_type& value1, const char_type& value2) NOEXCEPT
{
return value1 == value2;
}

static constexpr bool lt(const char_type value1, const char_type value2) NOEXCEPT
{
return value1 < value2;
}

static std::size_t length(const char_type* ptr)
{
std::size_t i = 0;
while (!eq(ptr[i], char_type()))
{
++i;
}
return i;
}

static int compare(const char_type* ptr1, const char_type* ptr2, std::size_t count)
{
return std::memcmp(ptr1, ptr2, count);
}

static const char_type* find(const char_type* ptr, std::size_t count, const char_type& value)
{
return static_cast<const char_type*>(std::memchr(ptr, value, count));
}

static char_type* move(char_type* dest, const char_type* src, std::size_t count)
{
return static_cast<char_type*>(std::memmove(dest, src, count));
}

static char_type* copy(char_type* dest, const char_type* src, std::size_t count)
{
return static_cast<char_type*>(std::memcpy(dest, src, count));
}

static constexpr char_type to_char_type(const int_type& value) NOEXCEPT
{
return static_cast<char_type>(value);
}

static constexpr int_type to_int_type(const char_type& value) NOEXCEPT
{
return static_cast<int_type>(value);
}

static constexpr bool eq_int_type(const int_type& value1, const int_type& value2) NOEXCEPT
{
return value1 == value2;
}

static constexpr int_type eof() NOEXCEPT
{
return static_cast<int_type>(std::char_traits<char>::eof());
}

static constexpr int_type not_eof(const int_type& value) NOEXCEPT
{
return (value == eof()) ? 0 : value;
}
};

// Specialization of std::codecvt
template<> class codecvt< std::uint8_t, char, std::mbstate_t > : public locale::facet, public codecvt_base
{
public:
using internal_type = std::uint8_t;
using external_type = char;
using state_type = std::mbstate_t;

static std::locale::id id;

codecvt(std::size_t refs = 0)
: locale::facet(refs)
{}

std::codecvt_base::result out(state_type& state, const internal_type* from, const internal_type* from_end, const internal_type*& from_next, external_type* to, external_type* to_end, external_type*& to_next) const
{
return do_out(state, from, from_end, from_next, to, to_end, to_next);
}

std::codecvt_base::result in(state_type& state, const external_type* from, const external_type* from_end, const external_type*& from_next, internal_type* to, internal_type* to_end, internal_type*& to_next) const
{
return do_in(state, from, from_end, from_next, to, to_end, to_next);
}

std::codecvt_base::result unshift(state_type& state, external_type* to, external_type* to_end, external_type*& to_next) const
{
return do_unshift(state, to, to_end, to_next);
}

int length(state_type& state, const external_type* from, const external_type* from_end, std::size_t max) const
{
return do_length(state, from, from_end, max);
}

int max_length() const NOEXCEPT
{
return do_max_length();
}

int encoding() const NOEXCEPT
{
return do_encoding();
}

bool always_noconv() const NOEXCEPT
{
return do_always_noconv();
}

protected:
virtual ~codecvt() {}
virtual std::codecvt_base::result do_out(state_type& state, const internal_type* from, const internal_type* from_end, const internal_type*& from_next, external_type* to, external_type* to_end, external_type*& to_next) const;
virtual std::codecvt_base::result do_in(state_type& state, const external_type* from, const external_type* from_end, const external_type*& from_next, internal_type* to, internal_type* to_end, internal_type*& to_next) const;
virtual std::codecvt_base::result do_unshift(state_type& state, external_type* to, external_type* to_end, external_type*& to_next) const;
virtual int do_length(state_type& state, const external_type* from, const external_type* from_end, std::size_t max) const;
virtual int do_max_length() const NOEXCEPT;
virtual int do_encoding() const NOEXCEPT;
virtual bool do_always_noconv() const NOEXCEPT;
}; // class codecvt

locale::id codecvt< std::uint8_t, char, std::mbstate_t >::id;

codecvt_base::result codecvt< std::uint8_t, char, std::mbstate_t >::do_out(state_type& state, const internal_type* from, const internal_type* from_end, const internal_type*& from_next, external_type* to, external_type* to_end, external_type*& to_next) const
{
(void) state; (void) from_end; (void) to_end; // Unused parameters
from_next = from;
to_next = to;
return codecvt_base::noconv;
}

codecvt_base::result codecvt< std::uint8_t, char, std::mbstate_t >::do_in(state_type& state, const external_type* from, const external_type* from_end, const external_type*& from_next, internal_type* to, internal_type* to_end, internal_type*& to_next) const
{
(void) state; (void) from_end; (void) to_end; // Unused parameters
from_next = from;
to_next = to;
return std::codecvt_base::noconv;
}

codecvt_base::result codecvt< std::uint8_t, char, std::mbstate_t >::do_unshift(state_type& state, external_type* to, external_type* to_end, external_type*& to_next) const
{
(void) state; (void) to_end; // Unused perameters
to_next = to;
return std::codecvt_base::noconv;
}

int codecvt< std::uint8_t, char, std::mbstate_t >::do_length(state_type& state, const external_type* from, const external_type* from_end, std::size_t max) const
{
(void) state; // Unused parameter
return static_cast<int>(std::min< std::size_t >(max, static_cast<std::size_t>(from_end - from)));
}

int codecvt< std::uint8_t, char, std::mbstate_t >::do_max_length() const NOEXCEPT
{
return 1;
}

int codecvt< std::uint8_t, char, std::mbstate_t >::do_encoding() const NOEXCEPT
{
return 1;
}

bool codecvt< std::uint8_t, char, std::mbstate_t >::do_always_noconv() const NOEXCEPT
{
return true;
}
} // namespace stdint main(int argc, char *argv [])
{
if (argc < 2)
{
std::cerr << argv[0] << " {file to read}" << std::endl;
return EXIT_FAILURE;
}

using stream_type = std::basic_ifstream< byte_type, std::char_traits<byte_type> >;

stream_type stream(argv[1], std::ifstream::in | std::ifstream::binary);
if (stream.is_open() == false)
{
std::cerr << "file not found" << std::endl;
return EXIT_FAILURE;
}
stream.exceptions(std::ifstream::badbit);

static const auto read_size = 4;
stream_type::char_type buffer[read_size];

stream.read(buffer, read_size);

std::cout << "Got:" << stream.gcount() << std::endl;

return EXIT_SUCCESS;
}

Скомпилируйте и запустите с g ++ и GNU / Linux:

$ g++ -std=c++11 -Wall -Wextra -pedantic stream.cpp -o stream && ./stream /dev/random
terminate called after throwing an instance of 'std::bad_cast'
what():  std::bad_cast
Aborted (core dumped)

А с Visual Studio Express RC 2013:

First-chance exception at 0x76A6C41F in traits test.exe: Microsoft C++ exception: std::bad_cast at memory location 0x0038F978.
Unhandled exception at 0x76A6C41F in traits test.exe: Microsoft C++ exception: std::bad_cast at memory location 0x0038F978.

изменения byte_type в char дает ожидаемый результат:

$ g++ -std=c++11 -Wall -Wextra -pedantic stream.cpp -o stream && ./stream /dev/random
Got:4

Почему это выбрасывает std :: bad_cast и как я могу это исправить?

12

Решение

Мне удалось воспроизвести bad_cast на моем gcc (4.7.2 в AIX).

Причина в том, что разработчики библиотеки gcc оптимизировали basic_filebuf::xsgetn (который называется from basic_istream::read) называть простым C fread читать из файла, если языковой стандарт вашего потока не преобразуется (то есть вы не пытаетесь прочитать файл UTF-8 или, возможно, GB18030 в строку UTF-32 или что-то в этом роде), что абсолютно правильно , Чтобы узнать, не является ли он неконвертирующим, он проверяет codecvt :: always_noconv на фасете codecvt локали, проникшей в ваш поток … который не существует.

Вы можете воспроизвести исключение, выполнив

std::cout << std::use_facet<
std::codecvt<std::uint8_t, char, std::mbstate_t>
>(stream.getloc()).always_noconv() << '\n';

У меня нет доступа к Visual Studio, чтобы понять, почему он там работает (они просто звонят basic_filebuf::sgetc() за каждый символ в basic_fstream::read()?), но для использования basic_filestream в любом случае вам нужно предоставить фасет codecvt для вашей комбинации внутреннего и внешнего типов (uint8_t а также char, в этом случае).

РЕДАКТИРОВАТЬ: Вы почти там, последний недостающий кусок строки

stream.imbue(std::locale(stream.getloc(),
new std::codecvt<uint8_t, char, std::mbstate_t>));

где-нибудь раньше stream.read или, альтернативно, наполняйте глобальное: std::locale::global(std::locale(std::locale(), new std::codecvt<uint8_t, char, std::mbstate_t>)); где-нибудь, прежде чем построить basic_ifstream

6

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector