2013-10-06 7 views
11

Therearealreadyquestions tutaj na Stackoverflow prosząc dlaczegobasic_fstream<uint8_t> nie działa. Odpowiedzi mówią, że char_traits jest wyspecjalizowany tylko dla char i wchar_t (plus char16_t, char32_t w C++ 11) i powinieneś trzymać się z basic_fstream<char>, aby odczytać dane binarne i przesłać je, jeśli jest to wymagane.Dlaczego ten specjalistyczny char_traits <uint8_t> i codecvt <uint8_t> do użytku z szablonu basic_ifstream throw std :: bad_cast?

No cóż, to nie wystarczy! :)

Żaden z odpowiedziami (które mogę znaleźć) powiedzieć jak specjalizować char_traits<uint8_t> i używać go z basic_fstream szablonu, lub jeśli jest to w ogóle możliwe. Pomyślałem więc, że sam spróbuję to zaimplementować.

Następujące kompilacje bez błędów podczas korzystania z Visual Studio Express 2013 RC na Windows 7 64bit oraz z g ++ - 4.7 na Kubuntu GNU/Linux 13.04 64bit. Jednak generuje wyjątek std :: bad_cast w czasie wykonywania. Nie mam dostępu do clang ++ z libC++, aby przetestować tę kombinację.

#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; 
} 

skompilować i uruchomić zg ++ i 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) 

I z 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. 

Zmiana byte_type do char daje oczekiwany wynik:

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

Dlaczego to jest wyrzucanie std :: bad_cast i jak mogę to naprawić?

+0

byłem stoi te same problemy, co Ty i ten post z odpowiedzią @Cubbi, były bardzo przydatne! Tylko jedna uwaga: wydaje mi się, że całość działa nawet bez specjalizacji 'std :: char_traits ', tj. Specjalizacja 'std :: codecvt' i sztuczki' imbue' są wystarczające. Czy się mylę? –

Odpowiedz

5

Udało mi się odtworzyć bad_cast na moim gcc (4.7.2 w systemie AIX).

Powodem masz to, że GCC implementors biblioteczne zoptymalizowany basic_filebuf::xsgetn (który nazywa from basic_istream::read) nazwać czystym C fread do odczytu z pliku, jeśli locale strumieniu jest bez konwersji (to znaczy, że nie próbujesz odczytać plik UTF-8 lub może GB18030 na łańcuch znaków UTF-32 lub coś takiego), co jest absolutnie słuszne. Aby dowiedzieć się, czy nie jest to konwersja, sprawdza on codecvt::always_noconv na aspekcie kodeków ustawienia narodowego nasyconego w strumieniu ... który nie istnieje.

można odtworzyć wyjątek wykonując

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

nie mam dostępu do Visual Studio zrozumieć, dlaczego to działa tam (oni po prostu zadzwoń basic_filebuf::sgetc() dla każdego char w basic_fstream::read()?), Ale w użyciu basic_filestream w każdym przypadku, musisz podać aspekt kodeków dla kombinacji typów wewnętrznych i zewnętrznych (uint8_t i char, w tym przypadku).

EDIT: Już prawie tam, ostatni brakujący kawałek jest linia

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

nigdzie przed stream.read lub, alternatywnie, nadać globalny: std::locale::global(std::locale(std::locale(), new std::codecvt<uint8_t, char, std::mbstate_t>)); ty nigdzie przed skonstruować basic_ifstream

+0

Dzięki za sugestię. Zaktualizowałem kod tak, aby zawierał wyspecjalizowany kodek , ale teraz generuje std :: bad_cast zarówno w g ++ jak i Visual Studio. –

+0

@DrTwox działa dla mnie z gcc po dodaniu imbue (patrz edytuj) – Cubbi

+0

Dziękuję! Gdybym tylko mógł przegłosować więcej niż raz! –

Powiązane problemy