gpt4 book ai didi

c++ - 为什么这个专门用于 basic_ifstream 模板的 char_traits 和 codecvt 会抛出 std::bad_cast?

转载 作者:IT老高 更新时间:2023-10-28 23:20:37 28 4
gpt4 key购买 nike

There are already questions在 Stackoverflow 上询问 为什么 basic_fstream<uint8_t>不起作用。答案说char_traits仅专门用于 charwchar_t (加上 char16_tchar32_t 在 C++11 中)你应该坚持使用 basic_fstream<char>读取二进制数据并根据需要进行转换。

该死的,这还不够好! :)

没有一个答案(我能找到)说如何特化char_traits<uint8_t>并将其与 basic_fstream 一起使用模板,或者如果它甚至可能的话。所以我想我会尝试自己实现它。

在 Windows 7 64 位上使用 Visual Studio Express 2013 RC 和在 Kubuntu GNU/Linux 13.04 64 位上使用 g++-4.7 时,以下编译没有错误。然而,它在运行时抛出一个 std::bad_cast 异常。我无法使用 libc++ 访问 clang++ 来测试该组合。

#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 std


int 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_typechar给出预期的输出:

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

为什么会抛出 std::bad_cast,我该如何解决?

最佳答案

我能够在我的 gcc(AIX 上的 4.7.2)上重现 bad_cast。

你得到它的原因是 gcc 库实现者优化了 basic_filebuf::xsgetn (称为 from basic_istream::read )调用普通 C fread如果您的流的语言环境未转换(也就是说,您没有尝试将 UTF-8 或 GB18030 文件读入 UTF-32 字符串或其他内容),则从文件中读取,这绝对是正确的做法.要确定它是否未转换,它会检查 codecvt::always_noconv在您的流中包含的语言环境的编解码器方面......这不存在。

可以通过执行重现异常

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_tchar)。

编辑:你快到了,最后缺少的部分是行

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 之前的任何地方

关于c++ - 为什么这个专门用于 basic_ifstream 模板的 char_traits<uint8_t> 和 codecvt<uint8_t> 会抛出 std::bad_cast?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19205531/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com