Definindo códigos de erro

5

Estamos projetando uma estrutura de tratamento de erros para uma biblioteca criptográfica escrita em C.

A abordagem que estamos tomando é que relativamente poucos erros são propagados de volta ao usuário, já que na maioria das vezes ele não pode fazer nada sobre eles (exceção notável: erros de validação de parâmetros. É claro que informamos ao usuário se uma chamada foi bem-sucedida. d também cria entradas de registro para investigação de falhas.) Em suma, só porque temos uma condição de erro em algum lugar dentro da biblioteca, isso não significa que precisamos de um código de erro público (voltado para o usuário) para ela.

Além disso, a partir de experiências passadas, pensamos que uma porção significativa de condições de erro que exigem códigos de erro pertencem a áreas de código muito estreitas, é comum que um erro específico seja retornado somente de uma função.

Portanto, gostaríamos de definir os códigos de erro no nível mais baixo possível. Por exemplo, se apenas uma função puder retornar / definir um determinado código de erro, definiríamos esse código no arquivo .h onde o protótipo da função é declarado.

No entanto, nesse caso, podemos obter conflitos de código de erro - o mesmo valor de código de erro para erros diferentes. Alguém pode, por favor, propor uma abordagem elegante para evitar tais conflitos? Ou, no mínimo, um mecanismo automatizado para verificar esses conflitos (que podem ser corrigidos manualmente)?

    
por malenkiy_scot 09.09.2014 / 17:32
fonte

2 respostas

4

we would define that code in the .h file where the function prototype is declared.

Eu recomendaria contra isso, pois você não tem nada para reduzir o espaço para possíveis conflitos de numeração.

Em vez disso, use um arquivo de cabeçalho comum fornecendo códigos de erro para os aplicativos do cliente e do servidor. Como alternativa, o arquivo do cliente pode ser simplesmente um subconjunto do arquivo de código de erro do servidor.

Dentro do arquivo de cabeçalho comum, cada área principal do seu aplicativo receberá uma sequência de números de erro. E de dentro de cada área principal, você pode atribuir subconjuntos de intervalos de código de erro aos módulos dentro dessa camada.

Por exemplo, digamos que você tenha um gerenciador de sessões, uma lógica de negócios e uma camada de acesso ao banco de dados no aplicativo do servidor. Seu arquivo de cabeçalho pode ser dividido assim:

/*
 * our error.h file 
 */
/* Common Errors 0 - 99 */
#define SUCCESS 0
#define GENERIC_ERROR 1
/* Session Manager errors 100 - 199 */
    /* Login issues 100 - 110 */
#define BAD_PASSWORD 101
    /* session issues 120 - 125 */
    /* other session issues 150 - 162 */
#define SOMETHING_ELSE 150
/* Business logic errors 200 - 299 */

/* Database errors 300 - 399 */
#define INVALID_CONNECTION 300

A vantagem desta abordagem é reduzir o espaço total onde os conflitos na numeração podem ocorrer. O conflito potencial é limitado às áreas em que dois ou mais desenvolvedores estão trabalhando simultaneamente.

Por ter um arquivo de cabeçalho de erro comum, os desenvolvedores podem fazer o check-out do arquivo conforme necessário para criar um novo erro e garantir que ele esteja reivindicando um número não utilizado nesse momento.

Possíveis armadilhas com essa abordagem são quando os desenvolvedores não costumam enviar sua cópia local do cabeçalho de erro. Mas essa é uma questão de processo que geralmente funciona quando os desenvolvedores tiveram que lidar com isso algumas vezes. E como eles estão se referindo ao número do erro através do #define , não é uma grande mudança.

Outra armadilha é quando você julga mal o intervalo necessário para uma camada ou sub-módulo. A maneira mais fácil de evitar isso é pré-alocar intervalos muito grandes para cada camada e sub-módulo. E a partir daí, você deve ser capaz de cortar de um sub-módulo para dar maior alcance a outro. E não há nada que diga que os intervalos devem ser contíguos.

    
por 09.09.2014 / 17:51
fonte
2

Eu recomendaria usar um arquivo de código de erro comum, exatamente como @ GlenH7, mas também recomendo escrever funções ou módulos independentes que não precisem do arquivo de erro global. Por exemplo, se uma função 'connect' pode falhar (ou não), retorne um indicador booleano. O chamador dessa função pode decidir mapear o valor de retorno booleano para uma constante global, mas não a função de conexão em si:

  success = connect(parameters);
  if(!success)
      return INVALID_CONNECTION;

  success = check_password(paramaters);
  if(!success)
      return BAD_PASSWORD;

  //...
  return SUCCESS;

Para softwares maiores, que consistem em vários módulos ou libs diferentes, talvez desenvolvidos por equipes diferentes, geralmente é melhor evitar códigos de erro globais e usar apenas códigos de erro locais por módulo ou por biblioteca. Para distingui-los, por exemplo, em um arquivo de log central, você sempre deve adicionar o nome do módulo ou algum tipo de ID do módulo ao código de erro. Você pode impor o uso desses dois valores projetando sua API do criador de logs da maneira correta - com uma função "log" esperando um ID de módulo e um código de erro como parâmetros, não apenas um código de erro. Se você realmente tem que passar os códigos de erro em torno do seu programa de uma maneira globalmente distinguível, você pode criar uma estrutura "ErrorCode", com dois atributos "ModuleID" e "LocalErrorCode". Dessa forma, você pode evitar a necessidade de ter códigos de erro globais.

    
por 09.09.2014 / 18:41
fonte