2010-11-25 4 views
11

En mi código que estoy acostumbrado a escribir Fall Back-casos por defecto que contiene afirma como la siguiente, me evitar el olvido de actualizar el interruptor en caso de que la semántica cambian¿La caja del interruptor "predeterminado" altera la optimización de la tabla de salto?

switch(mode) { 
case ModeA: ... ; 
case ModeB: ... ; 
case .. /* many of them ... */ 
default: { 
    assert(0 && "Unknown mode!"); 
    return ADummyValue(); 
} 
}; 

Ahora me pregunto si el otoño-artificial ¿El caso predeterminado de verificación inversa interferirá con las generaciones de la tabla de salto? Imagine que "ModeA", "ModeB", etc. son consecutivos, por lo que el compilador podría optimizar en una tabla. Dado que el caso "predeterminado" contiene una declaración de "retorno" real (dado que la afirmación desaparecerá en el modo de lanzamiento y el compilador se queja acerca de una declaración de devolución faltante), parece poco probable que el compilador optimice la bifurcación por defecto.

¿Cuál es la mejor manera de manejar esto? Un amigo me recomendó que reemplazara "ADummyValue" con una desreferencia de puntero nulo, de modo que el compilador, en presencia de un comportamiento indefinido, podría omitir advertir acerca de una declaración de devolución faltante. ¿Hay mejores formas de resolver esto?

+0

Dado el 'assert', es probablemente la mejor manera, ya sea' 'throw' o terminate' en lugar de' return'. –

Respuesta

1

Solo veo 1 solución en caso de que la optimización realmente se vea afectada: el infame "#ifndef NDEBUG" redondea el caso por defecto. No es el mejor truco, pero claro en esta situación.

BTW: ¿ya has visto lo que hace tu compilador con y sin el caso por defecto?

+0

No he buscado aún el código generado. –

+0

@Johannes: bueno, al menos Jerry Coffin sí. – stefaanv

1

Si tiene un estado que nunca se debe alcanzar, entonces debe eliminar el programa, porque simplemente alcanzó un estado inesperado, incluso en el modo de lanzamiento (puede ser más diplomático y realmente guardar datos de los usuarios y hacer todo esa otra cosa agradable antes de bajar).

Y por favor no se obsesione con las micro optimizaciones a menos que realmente haya medido (usando un generador de perfiles) que las necesita.

+0

Pero no deseo verificar el estado inalcanzable en el modo de lanzamiento. Mi suposición es que el código no contiene errores. Por supuesto, es muy probable que sea una suposición falsa, pero me parece necesario deshacerme de la comprobación del estado inaccesible (y esto es muy crítico para el rendimiento: rutina de búsqueda de nombres, rutina de conversión implícita, etc. de un tiempo de ejecución del lenguaje de scripts). –

1

La mejor manera de manejar esto es no desactivar la afirmación. De esa manera también puedes vigilar posibles errores. A veces es mejor que la aplicación se cuelgue con un buen mensaje que explique qué sucedió exactamente y luego continúe trabajando.

3

Al menos con los compiladores que he analizado, la respuesta es generalmente no. La mayoría de ellos compilar una sentencia switch como este para codificar o menos equivalente a:

if (mode < modeA || mode > modeLast) { 
    assert(0 && "Unknown mode!"); 
    return ADummyValue(); 
} 
switch(mode) { 
    case modeA: ...; 
    case modeB: ...; 
    case modeC: ...; 
    // ... 
    case modeLast: ...; 
} 
+0

Qué lástima. Tengo que sacrificar la validación de velocidad :( –

+0

@Johannes: ¿funcionaría una matriz de punteros de función de manera similar a las tablas de salto? Busque el puntero de función de la matriz y llámelo. (Tabla de salto consiste en un salto de registro y un salto fijo; una matriz de puntero de función sería una carga de registro y un salto de registro, supongo?) – rwong

2

si está utilizando "default" (ja !) <assert.h>, de la definición ligada a la macro NDEBUG de todos modos, por lo que tal vez sólo

case nevermind: 
#if !defined(NDEBUG) 
    default: 
     assert("can" && !"happen"); 
#endif 
    } 
+0

esto es lo que recomiendo. No sacrifica la velocidad, aún puede probar los tipos no contabilizados, y aún puede obtener advertencias del compilador –

0

Use extensiones del compilador:

// assume.hpp 
#pragma once 

#if defined _MSC_VER 
#define MY_ASSUME(e) (__assume(e), (e) ? void() : void()) 
#elif defined __GNUC__ 
#define MY_ASSUME(e) ((e) ? void() : __builtin_unreachable()) 
#else // defined __GNUC__ 
#error unknown compiler 
#endif // defined __GNUC__ 

-

// assert.hpp 
#include <cassert> 
#include "assume.hpp" 

#undef MY_ASSERT 
#ifdef NDEBUG 
#define MY_ASSERT MY_ASSUME 
#else // NDEBUG 
#define MY_ASSERT assert 
#endif // NDEBUG 
Cuestiones relacionadas