Tratamento de exceções na linguagem C

Uma implementação de tratamento de exceções usando a linguagem C, discutindo vários modelos e suas limitações, possíveis vazamentos de memórias, testes comparativos, muitos exemplos, introdução a certos recursos da linguagem, ... É certamente um assunto interessante se você quer expandir seus conhecimentos da linguagem C.

[ Hits: 69.365 ]

Por: Vinícius dos Santos Oliveira em 11/11/2010 | Blog: https://vinipsmaker.github.io/


Aninhando exceções



Uma limitação das macros desenvolvidas e demonstradas na seção anterior é que não é possível aninhar o tratamento de exceções ou escrever funções que tratem exceções e a disparem para um escopo externo. O problema pode ser resolvido fazendo com que o usuário da biblioteca possa interferir no nome adotado para as variáveis que são essenciais para os procedimentos responsáveis pelo tratamento das exceções.

Há várias formas de permitir que o usuário consiga interferir no nome das variáveis usadas pelos procedimentos de tratamento de exceções descrito nesse texto, aqui adota-se uma técnica simples, onde todas as macros definidas devem receber um argumento adicional que servirá de sufixo para o nome das variáveis utilizadas:

#define TRY(x) \
  { \
    jmp_buf _eb ## x; \
    struct _gvalue _ev ## x = {NULL, NULL}; \
    struct _gvalue *const _evp ## x = &_ev ## x; \
    if (!setjmp(_eb ## x)) {
#define CATCH(_type, _value, x) \
    } else if (_ev ## x.name && !strcmp(_ev ## x.name, #_type)) { \
      _type _value = *((_type *)(_ev ## x.value));
#define ENDTRY(x) \
    } \
    if (_ev ## x.value) free(_ev ## x.value); \
  }

#define THROW(_type, _value, x) \
  { \
    if (_evp ## x) { \
      _evp ## x->name = #_type; \
      _evp ## x->value = malloc(sizeof(_type)); \
      *((_type *)(_evp ## x->value)) = _value; \
    } \
    longjmp(_eb ## x, 1); \
  }

#define TRY_ARGS(x) _eb ## x, _evp ## x

E para nomear os argumentos utilizados pelas funções que disparam as exceções, a macro THROW_NAMED_ARGS foi introduzida. Como consequências, a definição da macro THROW_ARGS pode ser simplificada. Dessa forma na assinatura da função, recomenda-se o uso de THROW_ARGS, e na sua implementação, o uso de THROW_NAMED_ARGS.

#define THROW_ARGS jmp_buf, struct _gvalue *
#define THROW_NAMED_ARGS(x) jmp_buf _eb ## x, struct _gvalue *_evp ## x

Com isso, podemos escrever códigos que aninham exceções como o do exemplo abaixo:

#include "cexceptions.h"
#include <stdio.h>

void a(THROW_ARGS); // throw const char *
void b(THROW_ARGS); // throw const char *

int main()
{
  TRY()
    a(TRY_ARGS());
  CATCH(const char *, e,)
    printf("Erro: %s\n", e);
  ENDTRY()
  return 0;
}

void a(THROW_NAMED_ARGS(_1))
{
  TRY(_2)
    b(TRY_ARGS(_2));
    THROW(const char *, "disparando exceção da função \"a\" "
                        "de dentro de seu próprio bloco try", _2);
  CATCH(const char *, i, _2)
    THROW(const char *, "disparando exceção da função \"a\" "
                        "de dentro de seu próprio bloco catch", _1);
  ENDTRY(_2)
}

void b(THROW_NAMED_ARGS())
{
  THROW(const char *, "disparando exceção da função \"b\"",);
}

Esse código irá imprimir na tela:

"Erro: disparando exceção da função \"a\" de dentro de seu próprio bloco catch\n"

Se você leu o código com atenção, deve ter percebido que há um vazamento de memória. Na função a, uma chamada a função b é feita, então a função b aloca espaço para um ponteiro (que irá apontar para a string), então a função captura uma exceção e retorna do bloco CATCH (através da chamada a função longjmp) sem liberar o espaço alocado para o ponteiro (ele só seria liberado na linha de código onde ENDTRY se encontra).

Para resolver o problema, devemos introduzir uma nova macro, que libere os recursos alocados para o valor disparado:

#define EXCEPTION_FREE(x) \
  { \
    if (_ev ## x.value) free(_ev ## x.value); \
    _ev ## x.value = NULL; \
  }

void a(THROW_NAMED_ARGS(_1))
{
  TRY(_2)
    b(TRY_ARGS(_2));
    THROW(const char *, "disparando exceção da função \"a\" "
                        "de dentro de seu próprio bloco try", _2);
  CATCH(const char *, i, _2)
    EXCEPTION_FREE(_2)
    THROW(const char *, "disparando exceção da função \"a\" "
                        "de dentro de seu próprio bloco catch", _1);
  ENDTRY(_2)
}

Antes podíamos realizar saltos para fora de blocos TRY, agora podemos fazer o mesmo com blocos CATCH.

Página anterior     Próxima página

Páginas do artigo
   1. Introdução
   2. Tratamento de exceções em C
   3. Quebra de fluxo de execução, goto e setjmp
   4. Exceções associadas a inteiros
   5. Informação sobre os tipos em tempo de execução
   6. Macros
   7. Aninhando exceções
   8. Mais macros
   9. Memory leaks
   10. Uso em dispositivos móveis
   11. Conclusão
Outros artigos deste autor

Próximas Tecnologias do Sistema GNU/Linux

VLC Media Player (parte 2)

Mupen64plus, o melhor emulador de Nintendo 64 disponível para GNU/Linux

Entendendo os codecs, os containers formats e por que o Ogg é tão bom

VLC Media Player

Leitura recomendada

Alocação dinâmica de memória em C

Programação com números inteiros gigantes

Utilizando técnicas recursivas em C e C++

Tutorial SFML

Criando aplicativos para o iPhone no Linux (sem Xcode e MacOS X)

  
Comentários
[1] Comentário enviado por removido em 11/11/2010 - 17:16h

Gostei da aparência do código usando macros para definir os blocos try, catch e throw.

E parabéns pela contribuição de suas idéias!

[2] Comentário enviado por mslomp em 11/11/2010 - 18:17h

permita-me contribuir com conteúdo referente a esse assunto, a quem interessar:
há um tempo atrás postei na seção Scripts um código referente a isso:
http://www.vivaolinux.com.br/script/Excecoes-em-C-atraves-de-trythrowcatch

baseado na questão de um usuário em:
http://www.vivaolinux.com.br/topico/C-C++/C-e-Java

parabéns, um ótimo artigo conceitual e referencial

[3] Comentário enviado por SamL em 12/11/2010 - 12:06h

Cara gostei do artigo, nota 10.
Usar essas macros deixa um código bem mais limpo e organizado.

[4] Comentário enviado por gedarius em 12/11/2010 - 12:06h

ótimo artigo, parabéns!!!!

[5] Comentário enviado por vinipsmaker em 12/11/2010 - 15:47h

@mslomp, vlw, é bom saber que esse assunto interessa a muitos.

E aos outros, agradeço os elogios (críticas também são bem-vindas, caso tenham alguma =D ).

[6] Comentário enviado por mazinsw em 12/11/2010 - 19:41h

valeu pela contribuição, eu não sabia usar macros agora está mais fácil.

[7] Comentário enviado por vinipsmaker em 02/03/2013 - 19:19h

Migrei o código para https://github.com/vinipsmaker/c-except


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts