libreria library for descargar cerr c++ iostream

for - library c++



¿Cómo leer de forma segura un int sin firmar de una transmisión? (4)

Esta pregunta ya tiene una respuesta aquí:

En el siguiente programa

#include <iostream> #include <sstream> int main() { std::istringstream iss("-89"); std::cout << static_cast<bool>(iss) << iss.good() << iss.fail() << iss.bad() << iss.eof() << ''/n''; unsigned int u; iss >> u; std::cout << static_cast<bool>(iss) << iss.good() << iss.fail() << iss.bad() << iss.eof() << ''/n''; return 0; }

el flujo lib lee un valor firmado en un unsigned int sin unsigned int sin siquiera un problema, produciendo silenciosamente un resultado incorrecto :

11000 10001

Necesitamos poder detectar esos errores de falta de coincidencia de tipo de tiempo de ejecución. Si no hubiéramos descubierto esto en una simulación, esto podría haber hecho estallar un hardware muy costoso.

¿Cómo podemos leer de forma segura un valor sin signo de una transmisión?


En primer lugar, creo que el análisis de un valor negativo para un valor unsigned es incorrecto. El valor se decodifica por std::num_get<char> según el formato de strtoull() (22.4.2.12 párrafo 3, etapa 3, segundo punto). El formato de strtoull() se define en C 7.22.1.4 para ser el mismo que para las constantes enteras en C 6.4.4.1, que requiere que el valor literal pueda representarse por un tipo unsigned . Claramente, un valor negativo no puede ser representado por un tipo unsigned . Es cierto que miré C11, que en realidad no es el estándar C al que se hace referencia en C ++ 11. Además, citar párrafos estándar en el compilador no solucionará el problema. Por lo tanto, a continuación hay un enfoque que cambia cuidadosamente la decodificación de los valores.

Puede configurar un std::locale global con una faceta std::num_get<...> facet que comienza con un signo de menos para unsigned long y unsigned long long . La do_put() podría simplemente verificar el primer carácter y luego delegar a la versión de clase base si no es un ''-'' .

A continuación se muestra el código para una faceta personalizada. Aunque es bastante código, el uso real es bastante sencillo. La mayor parte del código es simplemente repetitivo que reemplaza las diferentes funciones virtual utilizadas para analizar un número unsigned (es decir, los miembros do_get() ). Estos solo se implementan en términos de la plantilla de función miembro get_impl() que verifica si no hay más caracteres o si el siguiente carácter es un ''-'' . En cualquiera de estos dos casos, la conversión falla al agregar std::ios_base::failbit al parámetro err . De lo contrario, la función simplemente delega a la conversión de la clase base.

La faceta correspondiente se usa finalmente para construir un nuevo objeto std::locale ( custom ; tenga en cuenta que el objeto positive_num_get asignado se libera automáticamente cuando se libera el último objeto std::locale donde se usa). Este std::locale se instala para convertirse en el locale global. La configuración regional global es utilizada por todas las secuencias recién creadas. Las secuencias existentes, en el ejemplo std::cin deben estar imbue() d con la configuración regional si las afecta. Una vez que se configura la configuración regional global, la nueva secuencia creada simplemente recogerá las reglas de decodificación modificadas, es decir, no debería ser necesario cambiar el código.

#include <iostream> #include <sstream> #include <locale> class positive_num_get : public std::num_get<char> { typedef std::num_get<char>::iter_type iter_type; typedef std::num_get<char>::char_type char_type; // actual implementation: if there is no character or it is a ''-'' fail template <typename T> iter_type get_impl(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, T& val) const { if (in == end || *in == ''-'') { err |= std::ios_base::failbit; return in; } else { return this->std::num_get<char>::do_get(in, end, str, err, val); } } // overrides of the various virtual functions iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, unsigned short& val) const override { return this->get_impl(in, end, str, err, val); } iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, unsigned int& val) const override { return this->get_impl(in, end, str, err, val); } iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, unsigned long& val) const override { return this->get_impl(in, end, str, err, val); } iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, unsigned long long& val) const override { return this->get_impl(in, end, str, err, val); } }; void read(std::string const& input) { std::istringstream in(input); unsigned long value; if (in >> value) { std::cout << "read " << value << " from ''" << input << ''/n''; } else { std::cout << "failed to read value from ''" << input << ''/n''; } } int main() { read("/t 17"); read("/t -18"); std::locale custom(std::locale(), new positive_num_get); std::locale::global(custom); std::cin.imbue(custom); read("/t 19"); read("/t -20"); }


Podrías escribir un manipulador:

template <typename T> struct ExtractUnsigned { T& value; ExtractUnsigned(T& value) : value(value) {} void read(std::istream& stream) const { char c; stream >> c; if(c == ''-'') throw std::runtime_error("Invalid unsigned number"); stream.putback(c); stream >> value; } }; template <typename T> inline ExtractUnsigned<T> extract_unsigned(T& value) { return ExtractUnsigned<T>(value); } template <typename T> inline std::istream& operator >> (std::istream& stream, const ExtractUnsigned<T>& extract) { extract.read(stream); return stream; } int main() { std::istringstream data(" +89 -89"); unsigned u; data >> extract_unsigned(u); std::cout << u << ''/n''; data >> extract_unsigned(u); return 0; }


Podrías hacerlo de la siguiente manera:

#include <iostream> #include <sstream> int main() { std::istringstream iss("-89"); std::cout << static_cast<bool>(iss) << iss.good() << iss.fail() << iss.bad() << iss.eof() << ''/n''; int u; if ((iss >> u) && (u > 0)) { unsigned int u1 = static_cast<unsigned int>(u); std::cout << "No errors: " << u1 << std::endl; } else { std::cout << "Error" << std::endl; } std::cout << static_cast<bool>(iss) << iss.good() << iss.fail() << iss.bad() << iss.eof() << ''/n''; return 0; }


Puede leer en una variable de un tipo firmado que puede manejar todo el rango primero y probar si es negativo o supera el máximo de su tipo de destino. Si sus valores sin firmar pueden no coincidir con el tipo de signo más grande disponible, deberá realizar un análisis utilizando algo que no sea iostreams.