c++ - dev - Excepciones con Unicode what()
c++17 (8)
Devolver UTF-8 es una elección obvia. Si la aplicación que utiliza las excepciones utiliza una codificación multibyte diferente, puede que le cueste trabajo mostrar la cadena. (No puede saber si es UTF-8, ¿o sí?) Por otro lado, para ISO-8859- * Las codificaciones de 8 bits (occidental, cirílico, etc.) que muestren una secuencia UTF-8 "solo" mostrarán un galimatías y usted (o su usuario) podría estar bien con eso si no puede eliminar la ambigüedad por cierto. un carácter * en el conjunto de caracteres de configuración regional y UTF-8.
Personalmente, creo que solo los mensajes de error de bajo nivel deberían ir a qué () cadenas y, personalmente, creo que estos deberían ser en inglés de todos modos. (Tal vez combinado con algún número de error o lo que sea).
El peor problema que veo con what()
es que no es raro incluir algunos detalles contextuales en el mensaje what (), por ejemplo, un nombre de archivo . Los nombres de archivo no son ASCII bastante a menudo, por lo que no tiene más remedio que usar UTF-8 como la codificación what()
.
Tenga en cuenta también que su clase de excepción (derivada de std :: exception) puede proporcionar obviamente cualquier método de acceso que le guste, por lo que podría tener sentido agregar un explícito what_utf8()
o what_utf16()
o what_iso8859_5()
.
Editar: Con respecto al comentario de John sobre cómo devolver UTF-8:
Si tiene una función const char* what()
, esta función esencialmente devuelve un grupo de bytes. En una plataforma de Windows occidental, estos bytes generalmente se codificarían como Win1252 , pero en una ventana rusa podría ser Win1251 .
El significado de los bytes depende de su codificación y su codificación depende de dónde provienen (y quién los interpreta). La codificación de un literal de cadena se define en tiempo de compilación, pero en el tiempo de ejecución, la aplicación sigue dependiendo de cómo interpretarlas.
Por lo tanto, para que su excepción devuelva cadenas UTF-8 con what()
(o what_utf8()
), debe asegurarse de que:
- El mensaje de entrada a su excepción tiene una codificación bien definida
- Tiene una codificación bien definida para el miembro de cadena que usa para contener el mensaje.
- Usted apropiadamente convierte la codificación cuando se llama
what()
Ejemplo:
struct MyExc : virtual public std::exception {
MyExc(const char* msg)
: exception(msg)
{ }
std::string what_utf8() {
return convert_iso8859_1_to_utf8( what() );
}
};
// In a ISO-8859-1 encoded source file
const char* my_err_msg = "ISO-8859-1 ... äöüß ...";
...
throw MyExc(my_err_msg);
...
catch(MyExc const& e) {
std::string iso8859_1_msg = e.what();
std::string utf_msg = e.what_utf8();
...
La conversión también podría colocarse en la función miembro (reemplazada) de what () de MyExc () o podría definir la excepción para tomar una cadena codificada UTF-8 o podría convertir (desde una codificación de entrada esperada, tal vez wchar_t / UTF -16) en el ctor.
O, "¿cómo los rusos arrojan excepciones?"
La definición de std :: exception es:
namespace std {
class exception {
public:
exception() throw();
exception(const exception&) throw();
exception& operator=(const exception&) throw();
virtual ~exception() throw();
virtual const char* what() const throw();
};
}
Una escuela de pensamiento popular para diseñar jerarquías de excepción es derivar de std :: exception:
En general, es mejor arrojar objetos, no integrados. Si es posible, debe lanzar instancias de clases que derivan (en última instancia) de la clase std :: exception. Al hacer que su clase de excepción herede (en última instancia) de la clase base de excepción estándar, le hace la vida más fácil a sus usuarios (tienen la opción de detectar la mayoría de las cosas a través de std :: exception), y probablemente les esté brindando más información (como el hecho de que su excepción en particular podría ser un refinamiento de std :: runtime_error o lo que sea) .std :: runtime_error o lo que sea).
Pero frente a Unicode, parece imposible diseñar una jerarquía de excepciones que logre los dos siguientes:
- Deriva en última instancia de std :: exception para facilidad de uso en el sitio de captura
- Proporciona compatibilidad Unicode para que los diagnósticos no se corten o se utilicen como galimatías
Proceder con una clase de excepción que se puede construir con cadenas Unicode es bastante simple. Pero el estándar dicta que what () debe devolver un const char *, por lo que en algún punto las cadenas de entrada se deben convertir a ASCII. Ya sea que se haga en tiempo de construcción o cuando se llame a what () (si la cadena de origen utiliza caracteres no representables por ASCII de 7 bits), podría ser imposible formatear el mensaje sin pérdida de fidelidad.
¿Cómo se diseña una jerarquía de excepciones que combina la integración perfecta de una clase std :: exception-derived con diagnósticos Unicode sin pérdidas?
El estándar no especifica qué codificación es la cadena devuelta por qué (), tampoco existe ningún estándar de facto. Simplemente lo codifico como UTF-8 y regreso de qué (), en mis proyectos. Por supuesto, puede haber incompatibilidad con otras bibliotecas.
Ver también: https://.com/questions/1049947/should-utf-16-be-considered-harmful por qué UTF-8 es una buena opción.
Es una mejor manera de agregar unicode en el procesamiento de errores:
try
{
// some code
}
catch (std::exception & ex)
{
report_problem(ex.what())
}
Y:
void report_problem(char const * const)
{
// here we can convert char to wchar_t or do some more else
// log it, save to file or message to user
}
La primera pregunta es: ¿qué piensas hacer con la cadena what ()?
¿Planeas registrar la información en alguna parte?
Si es así, no debería usar el contenido de la cadena what (), debería usar esa cadena como referencia para buscar el mensaje de registro específico local correcto. Entonces, para mí, el contenido de what () no es para fines de registro (o cualquier forma de visualización) es un método para buscar la cadena de registro real (que puede ser cualquier cadena Unicode).
Ahora; Puede ser que estemos completos para que la cadena what () contenga un mensaje legible para los desarrolladores para ayudar en la depuración rápida (pero para este texto altamente legible no es necesario). Como resultado, no hay ninguna razón para sustentar nada más que ASCII. Obedece el principio de KISS.
Un const char * no tiene que apuntar a una cadena ASCII; puede estar en una codificación multibyte tal como UTF-8. Una opción es usar wcstombs()
y amigos para convertir wstrings en cadenas, pero es posible que tengas que convertir el resultado de what()
a wstring antes de imprimir. También implica más copia y asignación de memoria de lo que puede sentirse cómodo con un manejador de excepciones.
Normalmente solo defino mi propia clase de excepción base, que usa wstring en lugar de string en el constructor y devuelve un const wstring & from what()
. No es tan grande de un acuerdo. La falta de una estándar es un gran descuido.
Otra opinión válida es que las cadenas de excepción nunca deben presentarse al usuario, por lo que no es necesario localizarlas, por lo que no debe preocuparse por ninguna de las anteriores.
char * no significa ASCII. Podría usar una codificación Unicode de 8 bits como UTF-8. char también podría ser de 16 bits o más, entonces podría usar UTF-16.
what () generalmente no está destinado a mostrar un mensaje a un usuario. Entre otras cosas, el texto que devuelve no es localizable (incluso si fue Unicode). Solo usaría what () para mostrar algo de valor para usted como desarrollador (como el archivo de origen y el número de línea del lugar donde se presentó la excepción) y para ese tipo de texto, ASCII suele ser más que suficiente.
El mínimo absoluto, cada desarrollador de software Absolutamente, positivamente debe saber sobre Unicode y juegos de caracteres (¡Sin excusas!) Por Joel Spolsky
Editar: Made CW, los comentaristas pueden editar por qué este enlace es relevante si lo desean