Fatalismo en el manejo de errores

Prólogo


Este artículo es una reacción al artículo: ¿Qué pasará con el manejo de errores en C ++ 2a ? Después de cada párrafo, me picaba, las heridas curadas se abrieron y comencé a sangrar. Tal vez llevo lo que está escrito demasiado cerca de mi corazón. Pero solo quiero aullar sobre la miopía y el analfabetismo que los programadores de C ++ muestran en el siglo XXI. Y ni siquiera al principio.


Empecemos


Clasificación


Convencionalmente, todas las situaciones erróneas en el programa se pueden dividir en 2 grandes grupos:
  1. Errores fatales.
  2. No son fatales ni se esperan errores.


Ahora encontraré la culpa. Pero errores fatales: también son, en cierto sentido, esperados. Esperamos que el viaje de memoria a menudo conduzca a una caída, pero puede que no conduzca a ella. Y esto se espera, ¿no? Cuando se introduce una clasificación, siempre será posible verificar su coherencia.


Pero esto es así, un error sutil frecuente.


Veamos los errores fatales.


División por 0 . Me pregunto por qué este error es fatal? Me encantaría lanzar una excepción en este caso y atraparla para su posterior procesamiento. ¿Por qué es ella fatal? ¿Por qué me impone un cierto comportamiento de mi propio programa y no puedo influirlo de ninguna manera? ¿C ++ no se trata de flexibilidad y del hecho de que el lenguaje se dirige hacia el programador? Aunque ...


Puntero nulo desreferenciación . Inmediatamente recuerdo Java, hay una NullPointerException que se puede manejar. ¡También hay una NullPointerException biblioteca de Poco! Entonces, ¿por qué los desarrolladores estándar con la tenacidad del sordomudo repiten el mismo mantra?


En general, ¿por qué comencé este tema? Es muy importante y solo revela la comprensión del desarrollador sobre el manejo de errores. No se trata del manejo de errores per se, es el preludio de una acción importante. Siempre se trata de la confiabilidad de la aplicación, de la tolerancia a fallas y, a veces, en los tipos de programas más raros y en peligro, incluso diría, en peligro, del comportamiento transaccional y consistente.


En este aspecto, todas las disputas sobre dividir entre cero y desreferenciar los punteros parecen una lucha de pájaros por una miga de pan. Ciertamente un proceso importante. Pero solo desde el punto de vista de las aves.


Volvamos a la división en fatalismo y su ausencia ... Comenzaré con una simple pregunta: si recibí datos incorrectos a través de la red, ¿es un error fatal?


Respuesta simple y correcta: depende de. Está claro que en la mayoría de los casos esto no es un error fatal, y todos los datos recibidos a través de la red deben ser validados y devueltos 4xx si los datos son incorrectos. ¿Hay algún caso en el que deba bloquearse? Y se estrelló con un aullido salvaje, por lo que llegarían SMS, por ejemplo. Sí, y no uno.


Hay. Puedo dar un ejemplo de mi área temática: un algoritmo de consenso distribuido. Un nodo recibe una respuesta que contiene un hash de una cadena de cambios de otro nodo. Y este hash es diferente del local. Esto significa que algo salió mal, y continuar con la ejecución posterior es simplemente peligroso: los datos pueden divergir, si no es que ya. Ocurre cuando la disponibilidad de un servicio es menos importante que su consistencia. En este caso, tenemos que caer, y con un rugido, para que todos escuchen. Es decir Recibimos datos a través de la red, los validaron y cayeron. Para nosotros, este error no es más fatal. ¿Se espera este error? Bueno, sí, escribimos el código con validación. Es una tontería decir lo contrario. Solo que no queremos continuar el programa después de eso. Se requiere intervención manual, la automatización no funcionó.


Elección del fatalismo


Lo más obvio en el manejo de errores es decidir qué es fatal y qué no. Pero cada programador se hace esta pregunta durante toda la actividad de desarrollo. Por lo tanto, de alguna manera se responde a esta pregunta. La respuesta correcta proviene de la práctica por alguna razón.


Sin embargo, esta es solo la parte visible del iceberg. En las profundidades hay una pregunta mucho más monstruosa. Para comprender la profundidad completa, debe establecer una tarea simple e intentar responderla.


Desafío Haz un marco para algo.


Todo es simple Hacemos un marco, por ejemplo, de interacción de red. O analizando JSON. O, en el peor de los casos, XML. La pregunta surge de inmediato: pero cuando ocurre un error desde el zócalo, ¿es un error fatal o no? Parafraseando: ¿debo lanzar una excepción o devolver un error? ¿Es esta una situación excepcional o no? ¿O puede devolver std::optional ? O una monja? (^ 1)


La conclusión paradójica es que el marco en sí mismo no puede responder a esta pregunta. Solo el código que lo usa lo sabe. Es por eso que la excelente biblioteca, en mi opinión, boost.asio usa ambas opciones. Dependiendo de las preferencias personales del autor del código y la lógica aplicada, se puede elegir uno u otro método de manejo de errores. Al principio, estaba un poco avergonzado por este enfoque, pero con el tiempo, me impregné de la flexibilidad de este enfoque.


Sin embargo, esto no es todo. Lo peor está por venir. Aquí estamos escribiendo el código de la aplicación, pero nos parece que se aplica. Para otro código, de nivel superior, nuestro código será la biblioteca. Es decir La división en código de aplicación / biblioteca (marco, etc.) es una convención pura, que depende del nivel de reutilización de los componentes. Siempre puede atornillar algo en la parte superior y el código de la aplicación dejará de serlo. Y esto significa inmediatamente que la elección de lo que es válido y lo que no lo está ya está decidido por el código que usa, y no lo usa.


Si saltamos a un lado, resulta que a veces ni siquiera es posible entender quién usa a quién. Es decir el componente A puede usar el componente B y el componente B componente A (^ 2). Es decir quién determina lo que sucederá no está claro en absoluto.


Enredo desenredando


Cuando miras toda esta desgracia, surge la pregunta de inmediato: ¿cómo vivir con ella? Que hacer ¿Qué pautas puedes elegir para no ahogarse en la variedad?


Para hacer esto, es útil mirar alrededor y comprender cómo se resuelven estos problemas en otros lugares. Sin embargo, uno debe buscar con prudencia: es necesario distinguir entre la "recolección de sellos" de las soluciones completas.


¿Qué es coleccionar sellos? Este es un término colectivo, lo que significa que intercambiamos un objetivo pero algo más. Por ejemplo: teníamos un objetivo: llamar y comunicarnos con nuestros seres queridos. Y una vez, y compramos un juguete caro, porque "de moda" y "hermoso" (^ 3). ¿Eso es familiar? ¿Crees que eso no les sucede a los programadores? No te hagas ilusiones.


El manejo de errores no es el objetivo. Cada vez que hablamos sobre el manejo de errores, inmediatamente nos detenemos. Porque es una forma de lograr un objetivo. Y el objetivo inicial es hacer que nuestro software sea confiable, simple y comprensible. Son estos objetivos los que deben establecerse y cumplirse siempre. Y el manejo de errores es una mierda que no vale la pena discutir. Quiero lanzar una excepción, ¡pero a la salud! Devolvió un error, ¡bien hecho! ¿Quieres una mónada? Felicitaciones, creaste la ilusión de avance, pero solo en tu propia cabeza (^ 4).


Entonces quise escribir cómo hacerlo bien, pero ya escribí. Las heridas sanaron y dejaron de sangrar. En resumen, los consejos son:


  1. Separar en componentes con límites claros.
  2. En las fronteras, describa qué y cómo puede volar. Es deseable que sea uniforme. Pero mucho más importante es.
  3. Facilite el manejo de errores en el código que lo usará.
  4. Si algo puede procesarse internamente sin cargar el código de usuario, no lo sobresalga. Cuantos menos errores deba manejar un usuario, mejor.
  5. Respeta a tu usuario, ¡no seas imbécil! Escriba interfaces intuitivas con el comportamiento esperado para que no necesite leer comentarios y jurar.

El quinto consejo es el más importante, porque él combina los primeros cuatro.


PD: En la infancia, siempre tuve curiosidad por mirar el hormiguero. Miles de hormigas, todos hacen algo, se arrastran por su negocio. El proceso está en marcha. Ahora también estoy mirando con interés. También detrás del hormiguero. Donde miles de personas están haciendo su pequeña cosa. ¡Puedo desearles buena suerte en su duro negocio!


^ 1: La gente es aficionada a las cosas de moda. Cuando todos jugaron lo suficiente, los programadores de C ++ se despertaron y luego todo cambió.


^ 2: Esto puede suceder cuando hay varias abstracciones en el componente B que las conecta. Ver Inversión de control .


^ 3: Y al día siguiente, bam, y la pantalla se bloqueó.


^ 4: No estoy en contra de las mónadas, estoy en contra de aspirar, como, mira, aquí está la mónada, es decir monoide en la categoría monoidal de endofunctores! Se escuchan aplausos y aprobaciones. Y en algún lugar lejos, lejos, apenas audible, alguien tiene orgasmos.

Source: https://habr.com/ru/post/es426971/


All Articles