2013-10-06 8 views
11

Therearealreadyquestions qui su StackOverflow chiedendo perchébasic_fstream<uint8_t> non funziona. Le risposte dicono che lo char_traits è specializzato solo per char e wchar_t (più char16_t, char32_t in C++ 11) e si deve rispettare basic_fstream<char> per leggere i dati binari e trasmetterli se necessario.Perché questo char_traits specializzato <uint8_t> e codecvt <uint8_t> da utilizzare con il modello basic_ifstream generano std :: bad_cast?

Beh, dannatamente, non è abbastanza buono! :)

Nessuna delle risposte (che posso trovare) dire come a specializzarsi char_traits<uint8_t> e utilizzarlo con un modello basic_fstream, o se è ancora possibile. Quindi ho pensato di provare a implementarlo da solo.

Il seguente compila senza errori quando si utilizza Visual Studio Express 2013 RC su Windows 7 a 64 bit e con g ++ - 4.7 su Kubuntu GNU/Linux 13.04 a 64 bit. Tuttavia lancia un'eccezione std :: bad_cast in fase di runtime. Non ho accesso a clang ++ con libC++ per testare questa combinazione.

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

Compilare ed eseguire con g ++ e 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) 

E con 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. 

Modifica byte_type al char dà i risultati attesi:

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

Perché questo lancio std :: bad_cast e come posso risolvere il problema?

+0

stavo affrontando gli stessi problemi di te e questo post con la risposta di @Cubbi è stato molto utile! Solo una nota a margine: mi sembra che tutto funzioni anche senza la specializzazione di 'std :: char_traits ', cioè la specializzazione di 'std :: codecvt' e il trucco 'imbusta' sono sufficienti. Ho sbagliato? –

risposta

5

Sono stato in grado di riprodurre un bad_cast sul mio gcc (4.7.2 su AIX).

Il motivo l'avete ottenuto è che gcc implementatori libreria ottimizzati basic_filebuf::xsgetn (che è chiamato from basic_istream::read) per chiamare pianura C fread a leggere dal file se impostazioni internazionali del flusso non è di conversione dell'angiotensina (vale a dire, non si sta cercando di leggere un file UTF-8 o forse GB18030 in una stringa UTF-32 o qualcosa del genere), che è assolutamente la cosa giusta da fare. Per scoprire se è non-convertente, controlla codecvt::always_noconv sul facet codecvt delle impostazioni locali nel tuo stream ... che non esiste.

è possibile riprodurre l'eccezione eseguendo

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

Non ho accesso a Visual Studio per vedere il motivo per cui vi lavora (fanno basta chiamare basic_filebuf::sgetc() per ogni char in basic_fstream::read()?), Ma di utilizzare basic_filestream in ogni caso, è necessario fornire un aspetto codecvt per la combinazione di tipi interni ed esterni (uint8_t e char, in questo caso).

EDIT: Ci siamo quasi, manca l'ultimo pezzo è la linea

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

da nessuna parte prima stream.read o, in alternativa, infondere il globale: std::locale::global(std::locale(std::locale(), new std::codecvt<uint8_t, char, std::mbstate_t>)); ovunque prima di costruire il basic_ifstream

+0

Grazie per il suggerimento. Ho aggiornato il codice per includere un codecvt specializzato ma ora gira std :: bad_cast su g ++ e Visual Studio. –

+0

@DrTwox funziona per me con gcc dopo aver aggiunto l'imbusto (vedi modifica) – Cubbi

+0

Grazie! Se solo potessi inversione più di una volta! –

Problemi correlati