Capture el valor de retorno no probado

Un artículo traducido anteriormente "Uso de objetos de código de error para el manejo de errores de C ++" mencionó que la inspiración del autor provino de otro documento "Capturando valores de retorno no probados", así que traduje este artículo nuevamente como El contraste puede considerarse como una serie de artículos.

Prólogo

El valor de retorno de la función se usa generalmente para indicar si la función se ejecuta sin error. Sin embargo, es difícil asegurarse de que la persona que llama utiliza esta información correctamente (se refiere al valor de retorno). Tal vez algunas herramientas comerciales pueden hacer el trabajo, pero no siempre obtienes una licencia de compra, especialmente en proyectos pequeños. Probablemente escuchaste este dicho: "Te creo, no cometerás tal error".

La idea que expuse aquí se inspiró en un error que cometimos en el proyecto hace unas semanas. Solo apareció en un entorno de producción, y tardó varios días en descubrir que provenía del fracaso de la rutina de inicialización de un entorno en particular. De hecho, el código que llamó a esta rutina no probó su código de retorno.

Agregar signo de responsabilidad

En mi experiencia (pero no por mucho tiempo), a menudo veo que los retornos de funciones se agrupan en una enumeración por tipo. Como se muestra en la Figura 1, la persona que llama puede ignorar más o menos dichos valores de retorno. Para controlar lo que sucede con los valores de retorno, no devuelvo estos valores directamente, sino que devuelvo una instancia de la clase ErrorCode. Contiene dos variables miembro: el valor (enValue_) indica el código de error de la función y el indicador de responsabilidad (PboResp_):

class ErrorCode
{
private:
   ErrorCodeValue enValue_;
   bool * PboResp_;
public:
   // some code
}

El propósito del indicador de responsabilidad es indicar si usted es responsable del valor del objeto ErrorCode (se refiere a si necesita verificarlo). Cuando el objeto ErrorCode usa el constructor de copia o la función de asignación para copiar el objeto, el valor (enValue_) se copia y la responsabilidad contenida en el indicador de responsabilidad PboResp_ debe "transferirse". La llamada "transferencia" se refiere a la operación de copia para transferir la responsabilidad de la instancia de origen a la instancia de destino. Después de copiar, la instancia de origen ya no es responsable de su contenido (es decir, la persona que llama ya no necesita verificar). (De hecho, la semántica del constructor de copias y la función de asignación son ligeramente diferentes, pero la idea general es la misma. Los detalles se explican a continuación).

Debido a que los parámetros del constructor de copia y la función de asignación usan el modificador const, elegí implementar el indicador de responsabilidad PboResp_ como un puntero a un valor booleano en lugar de un valor booleano. Esto permite que la función de copia modifique el indicador de responsabilidad PboResp_ de la instancia de origen pasada como parámetro. Hay otro aplicable a la restricción operador =: si el indicador de responsabilidad PboResp_ del parámetro ErrorCode objeto es verdadero, el valor anterior enValue_ se perderá y deberá registrarse (refiriéndose al registro de salida). (La transferencia de responsabilidades descrita aquí es similar a la copia que ocurre en auto_ptr)

Los objetos ErrorCode también usan operadores == y! =. Estos operadores son necesarios cuando se comparan con objetos temporales de ErrorCode devueltos por las funciones. Estos objetos temporales de ErrorCode se han construido para indicar un estado de error específico (por ejemplo, éxito). Como es de esperar, estas funciones de operador comparan el valor enValue_ dentro de dos objetos ErrorCode. Además, el indicador de responsabilidad PboResp_ de estos objetos ErrorCode también debe establecerse en falso, para que no haya registros de registro de "código de error no probado". Si es necesario, también se pueden implementar otros operadores de prueba (<,>).

Finalmente, el destructor debe verificar si la instancia sigue siendo responsable del valor del código de error (el indicador de responsabilidad PboResp_ es verdadero) y registrarlo si existe.

Integración con el código existente.

En mi opinión, el éxito de esta tecnología depende de si se puede integrar fácilmente en los programas existentes. La Figura 1 muestra una situación, que es un buen punto de partida para demostrar cómo integrar.

Primero, en la definición de clase, uso el nombre de la enumeración previamente definida como el nombre de la clase (es decir, el nombre de la enumeración anterior es ErrorCode, luego el nuevo nombre de clase es ErrorCode). Por lo tanto, todas las funciones que anteriormente devolvieron valores de enumeración ahora se convierten en un objeto de código de error, siempre que el programa se vuelva a compilar. También cambié el nombre de enumeración ErrorCode a ErrorCodeValue. Para que esta tecnología sea realmente efectiva, también debe definir un constructor con ErrorCodeValue como parámetro. Este constructor se invocará implícitamente en dos situaciones: primero, la función anterior devolvió ErrorCodeValue, no un objeto de la clase ErrorCode. En segundo lugar, cuando se comparan el objeto ErrorCode y ErrorCodeValue (a través del operador == o! =). En este caso, ErrorCodeValue se usará para construir un objeto temporal ErrorCode, que se usa como parámetro del operador de comparación. Como se mencionó anteriormente, el operador de comparación también "desactiva" los indicadores de responsabilidad de los dos objetos (el indicador de responsabilidad PboResp_ se establece en verdadero).

Implementar

La Figura 2 muestra la nueva implementación del manejo de errores, que utiliza la clase ErrorCode. La clase ErrorCode se agrega al archivo existente ErrorCodes.h. Además, también debe crear un archivo ErrorCodes.cpp para implementar las funciones miembro de la clase ErrorCode.

Como se mencionó anteriormente, los constructores de copias y los operadores de asignación deben transferir la responsabilidad de los códigos de error a sus objetos de destino. Pero esto no es suficiente, la función siempre debe ser el objeto ErrorCode, que es responsable de su contenido. Por lo tanto, el constructor de copia y el constructor que acepta ErrorCodeValue establecen el indicador de responsabilidad PboResp_ en verdadero cuando construyen el objeto (lo hacen incondicionalmente). Esta es la diferencia entre el constructor rf y el operador de asignación: el operador de asignación copia el indicador de responsabilidad desde el objeto de origen a la instancia de destino, y el constructor de copia debe establecer el indicador de responsabilidad en verdadero.

Finalmente, el constructor predeterminado es diferente de todos los demás constructores porque inicializa el indicador de responsabilidad a falso. El objeto ErrorCode construido por defecto no representa un código de error no probado, depende completamente del programador que lo creó para decidir cómo tratarlo.

La versión mejorada de la clase ErrorCode puede agregar aserciones a las funciones del destructor y del operador de asignación para interceptar "fugas de error" (es decir, saber qué códigos de error no se verifican) durante el desarrollo y las pruebas.

La nueva clase de código de error se implementa en el programa de la Figura 1, y el siguiente mensaje se emitirá en la salida de error estándar después de ejecutarse:

Destruction of untested error code: 
value 1
Untested error code (value 0) erased by 
new value 2

Conclusión

Creo que esta técnica de codificación es muy útil para detectar ciertos tipos de errores. Tenga en cuenta que este método no elimina ningún error, solo informe los códigos de retorno no probados después de que ocurra el problema. Creo que es fácil de implementar incluso en proyectos existentes. Al principio, puede obtener un archivo de registro que contiene muchos códigos de retorno no probados. Por lo tanto, en nuestro proyecto, tuvimos que limpiar un poco el código.

Bienvenido a prestar atención a mi cuenta pública [notas de programación del hermano Lin], y bienvenido a apreciar, ¡gracias!

Supongo que te gusta

Origin www.cnblogs.com/qinwanlin/p/12681178.html
Recomendado
Clasificación