Why does this specialized char_traitslt;uint8_tgt; and codecvtlt;uint8_tgt; for use with the basic_ifstream template throw std::bad_cast?(为什么这个专门的char_traitslt;uint8_tgt;和编解码器lt;uint8_tgt;与 basic_ifstream 模板一起使用 throw std::bad_cast?)
问题描述
那里 有 已经 问题 在 Stackoverflow 上询问 为什么 basic_fstream<uint8_t> 不起作用.答案说 char_traits 仅专门用于 char 和 wchar_t (加上 char16_t、char32_t 在 C++11 中),您应该坚持使用 basic_fstream<char> 来读取二进制数据并在需要时进行转换.
There are already questions here on Stackoverflow asking why basic_fstream<uint8_t> doesn't work. The answers say that char_traits is only specialized for char and wchar_t (plus char16_t, char32_t in C++11) and you should stick with basic_fstream<char> to read binary data and cast it if required.
该死的,这还不够好!:)
Well darn it, that isn't good enough! :)
没有一个答案(我能找到)说 如何 专门化 char_traits<uint8_t> 并将其与 basic_fstream 模板一起使用,或者如果它甚至可能.所以我想我会尝试自己实现它.
None of the answers (that I can find) say how to specialize char_traits<uint8_t> and use it with a basic_fstream template, or if it's even possible. So I thought I'd try implement it myself.
在 Windows 7 64 位上使用 Visual Studio Express 2013 RC 和在 Kubuntu GNU/Linux 13.04 64 位上使用 g++-4.7 时,以下编译没有错误.然而,它在运行时抛出一个 std::bad_cast 异常.我无法使用 libc++ 访问 clang++ 来测试该组合.
The following compiles without error when using Visual Studio Express 2013 RC on Windows 7 64bit and with g++-4.7 on Kubuntu GNU/Linux 13.04 64bit. However it throws a std::bad_cast exception at runtime. I don't have access to clang++ with libc++ to test that combination.
#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 编译和运行:
Compile and run with g++ and 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:
And with 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 会得到预期的输出:
Changing byte_type to char gives the expected output:
$ 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.
I was able to reproduce a bad_cast on my gcc (4.7.2 on AIX).
你得到它的原因是 gcc 库实现者优化了 basic_filebuf::xsgetn(称为 from basic_istream::read)以调用普通 C fread 如果您的流的语言环境是非转换的(也就是说,您没有尝试将 UTF-8 或 GB18030 文件读入 UTF-32 字符串或其他内容),则从文件中读取,这绝对是正确的事情.要确定它是否未转换,它会检查 codecvt::always_noconv 在您的流中包含的语言环境的编解码器方面......这不存在.
The reason you got it is that gcc library implementors optimized basic_filebuf::xsgetn (which is called from basic_istream::read) to call plain C fread to read from the file if your stream's locale is non-converting (that is, you're not trying to read a UTF-8 or maybe GB18030 file into a UTF-32 string or something),  which is absolutely the right thing to do. To find out if it is non-converting, it checks codecvt::always_noconv on the codecvt facet of the locale imbued in your stream... which doesn't exist.
可以通过执行重现异常
std::cout << std::use_facet<
                     std::codecvt<std::uint8_t, char, std::mbstate_t>
             >(stream.getloc()).always_noconv() << '
';
我无法访问 Visual Studio 来了解它为什么在那里工作(他们只是为 basic_fstream::read() 中的每个字符调用 basic_filebuf::sgetc()/code>?),但要在任何情况下使用 basic_filestream,您需要为内部和外部类型(uint8_t 和 char 的组合提供一个 codecvt facet,在此案例).
I don't have access to Visual Studio to see why it works there (do they just call basic_filebuf::sgetc() for every char in basic_fstream::read()?), but to use basic_filestream in any case, you need to provide a codecvt facet for your combination of internal and external types (uint8_t and char, in this case).
你快到了,最后缺少的部分是行
You're almost there, the last missing piece is the line
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
anywhere before stream.read or, alternatively, imbue the global: std::locale::global(std::locale(std::locale(), new std::codecvt<uint8_t, char, std::mbstate_t>)); anywhere before you construct the basic_ifstream
这篇关于为什么这个专门的char_traits<uint8_t>和编解码器<uint8_t>与 basic_ifstream 模板一起使用 throw std::bad_cast?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:为什么这个专门的char_traits<uint8_t>和编解码器<uint8_t>与 basic_ifstream 模板一起使用 throw std::bad_cast?
 
				
         
 
            
        基础教程推荐
- 静态库、静态链接动态库和动态链接动态库的 .lib 文件里面是什么? 2021-01-01
- 这个宏可以转换成函数吗? 2022-01-01
- 在 C++ 中计算滚动/移动平均值 2021-01-01
- C++结构和函数声明。为什么它不能编译? 2022-11-07
- 我有静态或动态 boost 库吗? 2021-01-01
- 如何在 C++ 中初始化静态常量成员? 2022-01-01
- 如何将 std::pair 的排序 std::list 转换为 std::map 2022-01-01
- 如何通过C程序打开命令提示符Cmd 2022-12-09
- 常量变量在标题中不起作用 2021-01-01
- 如何检查GTK+3.0中的小部件类型? 2022-11-30
 
    	 
    	 
    	 
    	 
    	 
    	 
    	 
    	 
						 
						 
						 
						 
						 
				 
				 
				 
				