![](/img/trans.png)
[英]reinterpret_cast between char* and std::uint8_t* - safe?
[英]Why does this specialized char_traits<uint8_t> and codecvt<uint8_t> for use with the basic_ifstream template throw std::bad_cast?
有 有 已經 問題 #2這里問為什么 basic_fstream<uint8_t>
不起作用。 答案說char_traits
只適用於char
和wchar_t
(加上char16_t
,C ++ 11中的char32_t
),你應該堅持使用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異常。 我沒有訪問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 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_type
更改為char
可得到預期的輸出:
$ g++ -std=c++11 -Wall -Wextra -pedantic stream.cpp -o stream && ./stream /dev/random
Got:4
為什么要拋出std :: bad_cast以及如何解決它?
我能夠在我的gcc上重現bad_cast(AIX上的4.7.2)。
你得到它的原因是gcc庫實現者優化了basic_filebuf::xsgetn
( from basic_istream::read
調用),如果你的流的語言環境是非轉換的,那就調用普通的C fread
來讀取文件(也就是說,你是沒有嘗試將UTF-8或GB18030文件讀入UTF-32字符串或其他東西),這絕對是正確的做法。 要查明它是否是非轉換的,它會檢查您的流中所包含的語言環境的codecvt方面的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_fstream::read()
每個字符調用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
之前的任何地方,或者,或者,imbue global: std::locale::global(std::locale(std::locale(), new std::codecvt<uint8_t, char, std::mbstate_t>));
在構造basic_ifstream
之前的任何地方
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.