2010-09-21 22 views
25

Tengo un código C con el que estoy trabajando, y estoy encontrando errores cuando el código se está ejecutando, pero tengo poca información sobre cómo hacer una prueba/captura adecuada (como en C# o C++).ANSI C equivalente de try/catch?

Por ejemplo, en C++ que acababa de hacer:

try{ 
//some stuff 
} 
catch(...) 
{ 
//handle error 
} 

pero en ANSI C estoy un poco perdido. Probé algunas búsquedas en línea, pero no veo suficiente información sobre cómo hacerlo realidad, pensé que podría preguntar aquí en caso de que alguien me señale la dirección correcta.

Aquí está el código que estoy trabajando con (método bastante sencillo, recursiva) y me gustaría concluir con try/catch (o estructura de control de errores equivalente).

Sin embargo, mi pregunta principal es simplemente cómo hacer un try/catch en ANSI C ... de la aplicación/ejemplo no tiene por qué ser recursivo.

void getInfo(int offset, myfile::MyItem * item) 
{ 
    ll::String myOtherInfo = item->getOtherInfo(); 
    if(myOtherInfo.isNull()) 
     myOtherInfo = ""; 
    ll::String getOne = ""; 
    myfile::Abc * abc = item->getOrig(); 
    if(abc != NULL) 
    { 
     getOne = abc->getOne(); 
    } 
    for(int i = 0 ; i < offset ; i++) 
    { 
      printf("found: %d", i); 
    } 
    if(abc != NULL) 
     abc->release(); 
    int childCount = item->getChildCount(); 
    offset++; 
    for(int i = 0 ; i < childCount ; i++) 
     getInfo(offset, item->getChild(i)); 
    item->release(); 
} 
+2

http://www.nicemice.net/cexcept/ algo que podría ser útil – anijhaw

+23

Este código no es C, ansi o de lo contrario. C no tiene el operador de alcance '::'. –

+4

C no tiene mecanismo de manejo de excepciones. Todo el manejo de errores usualmente se realiza con valores de retorno y variable errnum. Por cierto, sería bueno obtener algunos comentarios expertos detallados de cómo se hace el manejo de errores correctamente en C :) – Kel

Respuesta

25

En general, no lo hace.

Es posible utilizar setjmp y longjmp construir algo bastante similar a tratar/captura, a pesar de que no hay tal cosa como destructores en C o una pila de desenrollado, por lo RAII está fuera de la cuestión. Incluso podría aproximarse a RAII con una llamada "pila de limpieza" (consulte, por ejemplo, Symbian/C++), aunque no es una aproximación muy aproximada, y es mucho trabajo.

La forma habitual para indicar errores o fallos en C es devolver un valor que indica el estado de éxito. Las personas que llaman examinan el valor de retorno y actúan en consecuencia. Consulte, por ejemplo, las funciones C estándar: printf, read, open, para obtener ideas sobre cómo especificar sus funciones.

Al mezclar código C y C++, debe asegurarse de que una excepción de C++ no alcanza nunca el código C. Al escribir las funciones de C++ que serán llamadas desde C, capture todo.

+19

IME La forma habitual de indicar errores o fallas en C es devolver un valor que la persona que llama ignora. ': (' – sbi

+2

@sbi: IME, algunas personas todavía creen que "devolver un valor" es el camino a seguir en C++, Java y C#, también ... ': (' – paercebal

+0

@paercebal: Lo sé. ':(' – sbi

1

Si desea hacer un salto de nivel múltiple, busque setjmp() y longjmp(). Se pueden usar como un lanzamiento de excepción primitivo. La función setjmp() configura un lugar de devolución y devuelve un valor de estado. La función longjmp() va al lugar de devolución y proporciona el valor de estado. Puede crear una función catch llamándose después de setjmp() según el valor de estado.

No, por el motivo que sea, utilícelos en C++. No hacen que la pila se desenrolle o llame a destructores.

17

C no admite el manejo de excepciones.

Existe información sobre un enfoque para este problema here. Esto muestra el enfoque simple setjmp/longjmp pero también proporciona una alternativa más sofisticada, cubierta en profundidad.

+0

Hmm, tercer vástago hoy. ¿Alguna razón en particular para esto? Pensé que esto parecía prometedor. –

+0

¿Hay alguien fuera de ti? –

+2

+1 porque el voto negativo era incorrecto ... enlace perfecto para la pregunta – Hogan

0

Puesto que C++ fue implementado originalmente como un pre-procesador de C y tenía try/catch que podría rehacer el trabajo de Bjarne Stroustrup y escribir un pre-procesador para hacerlo.

+1

En el momento en que se introdujeron excepciones a C++, la implementación del preprocesador C fue una historia. –

+0

@Nemanja: Eso es cierto para el original cfront. [Comeau C++] (http://www.comeaucomputing.com), sin embargo, sigue produciendo código C. (Porque eso hace que sea muy portátil). Pero dudo que esto sea más útil que la salida del ensamblador de cualquier otro compilador. : es un código generado por la máquina, y como tal no para el consumo humano. – sbi

+1

Creo que las excepciones fueron la gota que colmó el vaso, donde el camello en cuestión era el compilador de C++. Ver Stroupstrup "Diseño y Evolución de C++". –

4

Existe la reversión goto s patrón clásico:

FILE *if = fopen(...); 
FILE *of = NULL; 
if (if == NULL) return; 

of = fopen(...); 
if (of == NULL) goto close_if; 

/* ...code... */ 
if (something_is_wrong) goto close_of; 

/* ... other code... */ 

close_of: 
    fclose(of); 
close_if: 
    fclose(if); 

return state; 

Alternativamente se puede fingir de manera limitada mediante el aislamiento del código de "probar" en otra función

int try_code(type *var_we_must_write, othertype var_we_only_read /*, ... */){ 
    /* ...code... */ 
    if (!some_condition) return 1; 
    /* ...code... */ 
    if (!another_condition) return 2; 
    /* ...code... */ 
    if (last_way_to_fail) return 4; 
    return 0; 
} 

void calling_routine(){ 
    /* ... */ 
    if (try_code(&x,y/*, other state */)) { 
    /* do your finally here */ 
    } 
/* ... */ 
} 

pero ninguno de estos enfoques es totalmente equivalente. Hay que gestionar todos los recursos usted mismo, usted no consigue la reversión automática hasta que se encuentra un controlador, y así sucesivamente ...

+0

Eek!Eso se ve muy complicado para mí. Prefiero mi método que sugerí a continuación. No hay necesidad de ir! – James

+0

'goto' es el camino a seguir (disculpe el juego de palabras) en C. Sin embargo, tener múltiples etiquetas de salida parece exagerado. Es más limpio (aunque ligeramente menos eficiente) tener una sola etiqueta de salida y (si es necesario) verificar qué recursos se asignaron. – jamesdlin

+0

@jamesdlin: Las etiquetas múltiples le compran algo específico: cada una corresponde a un recurso que puede necesitar ser liberado. Puede evitarlos con condicionales en las versiones, pero en algunos casos (no este) que requieren indicadores adicionales. La materia de gusto por lo que puedo decir. – dmckee

4

Un estilo de codificación útil que me gusta usar es el siguiente. No sé si tiene un nombre en particular, pero lo encontré cuando estaba realizando ingeniería inversa de algún código de ensamblaje en el código C equivalente. Pierdes un nivel de sangría, pero no es tan importante para mí. ¡Cuidado con el crítico que señalará el ciclo infinito! :)

int SomeFunction() { 
    int err = SUCCESS; 

    do { 
     err = DoSomethingThatMayFail(); 
     if (err != SUCCESS) { 
      printf("DoSomethingThatMayFail() failed with %d", err); 
      break; 
     } 

     err = DoSomethingElse(); 
     if (err != SUCCESS) { 
      printf("DoSomethingElse() failed with %d", err); 
      break; 
     } 

     // ... call as many functions as needed. 

     // If execution gets there everything succeeded! 
     return SUCCESS; 
    while (false); 

    // Something went wrong! 
    // Close handles or free memory that may have been allocated successfully. 

    return err; 
} 
+1

Agradable. Es posible que necesite instancias anidadas si necesita desconectar cosas condicionalmente. – dmckee

4

Ésta es mi implementación de un sistema de manejo de excepciones en C: exceptions4c.

Es impulsado por macros, construido encima de setjmp y longjmp y es 100% portátil ANSI C

There también se puede encontrar una lista de todas las diferentes implementaciones que conozco.