plantillas - C/C++: cambiar por no enteros
string to char c++> (16)
Hash tu camino a la victoria
Puede usar una función hash en tiempo de compilación como en esta gloriosa answer desbordamiento de pila. Si creas las funciones
-
int_crc32_s
que devuelve un hash de una cadena en tiempo de ejecución y -
int_crc32
que devuelve un hash de una cadena en tiempo de compilación
estás listo. Para manejar una coincidencia falsa del crc de su cadena de clave y cadena de mayúsculas y minúsculas, debe incluir una verificación explícita de una coincidencia. Esto realmente no afecta el rendimiento porque es solo una comprobación, pero lo hace mucho más feo y la versión de macros se ve mucho mejor.
Encontré estas dos cadenas que tienen el mismo CRC32 .
//two strings that yield same crc32
const char* collision1="DeferredAmbient_6_1_18-1of2_5";
const char* collision2="PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19";
Sin macros
//without macros (you need to check for collisions)
switch( int_crc32_s(str.c_str()) )
{
case int_crc32("foo"): if( str=="foo"){std::cout << "foo you/n"; break;}
case int_crc32("bar"): if( str=="bar"){std::cout << "bar you/n"; break;}
case int_crc32("baz"): if( str=="baz"){std::cout << "baz you/n"; break;}
case int_crc32("PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"):
if( str=="PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"){
std::cout << "jackpot!/n"; break;
}
default: std::cout << "just you/n";
}
Con macros
//convenient macros
#define S_SWITCH( X ) const char* SWITCH_KEY(X.c_str()); switch( int_crc32_s(X.c_str()) )
#define S_CASE( X ) case int_crc32(X): if( strcmp(SWITCH_KEY,X) ){ goto S_DEFAULT_LABEL;}
#define S_DEFAULT S_DEFAULT_LABEL: default:
//with macros
S_SWITCH( str )
{
S_CASE("foo"){ std::cout << "foo you/n"; break; }
S_CASE("bar"){ std::cout << "bar you/n"; break; }
S_CASE("baz"){ std::cout << "baz you/n"; break; }
S_CASE("PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"){ std::cout << "jackpot!/n"; break; }
S_DEFAULT{ std::cout << "just you/n"; }
}
Implementación completa [ gist ]
// This is a demonstration of using a COMPILE-TIME hash to do a
// switch statement with a string to answer this question.
//
// https://stackoverflow.com/questions/4165131/c-c-switch-for-non-integers
//
// It is based on the StackOverflow question:
// https://stackoverflow.com/questions/2111667/compile-time-string-hashing
//
// And the solution
// https://stackoverflow.com/questions/2111667/compile-time-string-hashing/23683218#23683218
//
#include <iostream>
#include <string>
#include <vector>
namespace detail {
// CRC32 Table (zlib polynomial)
static constexpr uint32_t crc_table[256] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
//constexpr combine
template<size_t idx>
constexpr uint32_t combine_crc32(const char * str, uint32_t part) {
return (part >> 8) ^ crc_table[(part ^ str[idx]) & 0x000000FF];
}
//constexpr driver
template<size_t idx>
constexpr uint32_t crc32(const char * str) {
return combine_crc32<idx>(str, crc32<idx - 1>(str));
}
//constexpr recursion stopper
template<>
constexpr uint32_t crc32<size_t(-1)>(const char * str) {
return 0xFFFFFFFF;
}
//runtime combine
uint32_t combine_crc32_s(size_t idx, const char * str, uint32_t part) {
return (part >> 8) ^ crc_table[(part ^ str[idx]) & 0x000000FF];
}
//runtime driver
uint32_t crc32_s(size_t idx, const char * str) {
if( idx==static_cast<size_t>(-1) )return 0xFFFFFFFF;
return combine_crc32_s(idx, str, crc32_s(idx-1,str));
}
} //namespace detail
//constexpr that returns unsigned int
template <size_t len>
constexpr uint32_t uint_crc32(const char (&str)[len]) {
return detail::crc32<len - 2>(str) ^ 0xFFFFFFFF;
}
//constexpr that returns signed int
template <size_t len>
constexpr int int_crc32(const char (&str)[len]) {
return static_cast<int>( uint_crc32(str) );
}
//runtime that returns unsigned int
uint32_t uint_crc32_s( const char* str ) {
return detail::crc32_s(strlen(str)-1,str) ^ 0xFFFFFFFF;
}
//runtime that returns signed int
int int_crc32_s( const char* str) {
return static_cast<int>( uint_crc32_s(str) );
}
//convenient macros
#define S_SWITCH( X ) const char* SWITCH_KEY(X.c_str()); switch( int_crc32_s(X.c_str()) )
#define S_CASE( X ) case int_crc32(X): if( strcmp(SWITCH_KEY,X) ){ goto S_DEFAULT_LABEL;}
#define S_DEFAULT S_DEFAULT_LABEL: default:
int main()
{
std::string str;
std::cin >> str;
//two strings that yield same crc32
const char* collision1="DeferredAmbient_6_1_18-1of2_5";
const char* collision2="PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19";
//without macros (you need to check
switch( int_crc32_s(str.c_str()) )
{
case int_crc32("foo"): if( str=="foo"){std::cout << "foo you/n"; break;}
case int_crc32("bar"): if( str=="bar"){std::cout << "bar you/n"; break;}
case int_crc32("baz"): if( str=="baz"){std::cout << "baz you/n"; break;}
case int_crc32("PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"):
if( str=="PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"){
std::cout << "jackpot!/n"; break;
}
default: std::cout << "just you/n";
}
//with macros
S_SWITCH( str )
{
S_CASE("foo"){ std::cout << "foo you/n"; break; }
S_CASE("bar"){ std::cout << "bar you/n"; break; }
S_CASE("baz"){ std::cout << "baz you/n"; break; }
S_CASE("PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"){ std::cout << "jackpot!/n"; break; }
S_DEFAULT{ std::cout << "just you/n"; }
}
}
A menudo tengo que elegir qué hacer de acuerdo con el valor de un elemento constante no POD, algo como esto:
switch( str ) {
case "foo": ...
case "bar": ...
default: ...
}
Lamentablemente, el switch
solo se puede usar con números enteros: error: switch quantity not an integer
.
La forma más trivial de implementar tal cosa es tener una secuencia de if
s:
if( str == "foo" ) ...
else if( str == "bar" ) ...
else ...
Pero esta solución parece sucia y debería costar O (n), donde n es el número de casos, mientras que ese fragmento de código podría costar O (log n) en el peor de los casos con una búsqueda binaria.
Usando algunas estructuras de datos (como Maps) podría ser posible obtener un número entero que represente la cadena (O (log n)), y luego usar un switch
O (1), o uno podría implementar una clasificación binaria estática anidando if
s de la manera correcta, pero aún así estos hacks requerirían mucha codificación, haciendo que todo sea más complejo y más difícil de mantener.
¿Cuál es la mejor manera de hacer esto? (rápido, limpio y simple, como la declaración de switch
es)
¿Algo así sería demasiado complejo?
#include <iostream>
#include <map>
struct object
{
object(int value): _value(value) {}
bool operator< (object const& rhs) const
{
return _value < rhs._value;
}
int _value;
};
typedef void(*Func)();
void f1() {
std::cout << "f1" << std::endl;
}
void f2() {
std::cout << "f2" << std::endl;
}
void f3() {
std::cout << "f3" << std::endl;
}
int main()
{
object o1(0);
object o2(1);
object o3(2);
std::map<object, Func> funcMap;
funcMap[o1] = f1;
funcMap[o2] = f2;
funcMap[o3] = f3;
funcMap[object(0)](); // prints "f1"
funcMap[object(1)](); // prints "f2"
funcMap[object(2)](); // prints "f3"
}
Aquí hay un código de ejemplo que funciona:
Esto debería funcionar.
(SÓLO en cadenas de 4 bytes o menos)
Esto trata las cadenas como enteros de 4 bytes.
Esto se considera feo, no portátil, "hacky", y para nada un buen estilo. Pero hace lo que querías.
#include "Winsock2.h"
#pragma comment(lib,"ws2_32.lib")
void main()
{
char day[20];
printf("Enter the short name of day");
scanf("%s", day);
switch(htonl(*((unsigned long*)day)))
{
case ''sun/0'':
printf("sunday");
break;
case ''mon/0'':
printf("monday");
break;
case ''Tue/0'':
printf("Tuesday");
break;
case ''wed/0'':
printf("wednesday");
break;
case ''Thu/0'':
printf("Thursday");
break;
case ''Fri/0'':
printf("friday");
break;
case ''sat/0'':
printf("saturday");
break;
}
}
probado en MSVC2010
En C ++, puede obtener el rendimiento O(lg n)
al tener std::map<std::string, functionPointerType>
. (En C podría implementar lo que esencialmente era lo mismo, pero sería más difícil) Extraiga el puntero de función correcto usando std::map<k, v>::find
, y llame a ese puntero. Por supuesto, eso no va a ser tan simple como una declaración de cambio compatible con el lenguaje. Por otro lado, si tiene suficientes elementos que va a haber una gran diferencia entre O(n)
y O(lg n)
, eso es probablemente una indicación de que debe ir a un diseño diferente en primer lugar.
Personalmente, siempre he encontrado la cadena ELSEIF más legible de todos modos.
En C, hay dos soluciones comunes. La primera es mantener sus palabras clave en una matriz ordenada, por ejemplo
typedef struct Keyword {
const char *word;
int sub;
int type;
} Keyword;
Keyword keywords[] ={ /* keep sorted: binary searched */
{ "BEGIN", XBEGIN, XBEGIN },
{ "END", XEND, XEND },
{ "NF", VARNF, VARNF },
{ "atan2", FATAN, BLTIN },
...
};
y hacer una búsqueda binaria en ellos. Lo anterior es directamente del código fuente de awk del gran maestro de C Brian W. Kernighan.
La otra solución, que es O (min ( m , n )) si n es la longitud de su cadena de entrada y m la longitud de la palabra clave más larga, es usar una solución de estado finito como un programa Lex.
Esto es similar en espíritu a las soluciones lambda y unordered_map, pero creo que esto es lo mejor de ambos mundos, con una sintaxis muy natural y legible:
#include "switch.h"
#include <iostream>
#include <string>
int main(int argc, const char* argv[])
{
std::string str(argv[1]);
Switch(str)
.Case("apple", []() { std::cout << "apple" << std::endl; })
.Case("banana", []() { std::cout << "banana" << std::endl; })
.Default( []() { std::cout << "unknown" << std::endl; });
return 0;
}
switch.h:
#include <unordered_map>
#include <functional>
template<typename Key>
class Switcher {
public:
typedef std::function<void()> Func;
Switcher(Key key) : m_impl(), m_default(), m_key(key) {}
Switcher& Case(Key key, Func func) {
m_impl.insert(std::make_pair(key, func));
return *this;
}
Switcher& Default(Func func) {
m_default = func;
return *this;
}
~Switcher() {
auto iFunc = m_impl.find(m_key);
if (iFunc != m_impl.end())
iFunc->second();
else
m_default();
}
private:
std::unordered_map<Key, Func> m_impl;
Func m_default;
Key m_key;
};
template<typename Key>
Switcher<Key> Switch(Key key)
{
return Switcher<Key>(key);
}
Hace algún tiempo, escribí una clase de plantilla para lograr algún tipo de conmutador equivalente, utilizable en cualquier tipo de datos . Sin embargo, existen algunas limitaciones que limitan sus campos de aplicación:
- la tarea para lograr en cada rama debe ser una llamada a función.
- las funciones a llamar tienen un único argumento (o ninguno, o dos, puede modificar la plantilla, pero tiene que ser el mismo para todas las funciones).
- el valor de argumento pasado a las funciones será el mismo en todos los casos (pero se da en el momento en que se ejecuta el cambio).
Como ejemplo, digamos que desea activar un valor de tipo MyType
, si es igual a value1
, value1
function1("abc")
, si es igual a value2
, value2
function2("abc")
(y así sucesivamente) ) Esto terminaría como:
// set up the object
// Type - function sig - function arg. type
SWITCH mySwitch< MyType, void(*)(const std::string&), std::string >;
mySwitch.Add( value1, function1 );
mySwitch.Add( value2, function2 );
mySwitch.AddDefault( function_def );
// process the value
MyType a =...// whatever.
mySwitch.Process( a, "abc" );
Básicamente, envuelve un contenedor std :: map, que contiene el valor / función de par. También puede manejar el "predeterminado", que hace que un cambio sea tan interesante. Se puede adaptar fácilmente a otras situaciones. Aquí está el código:
template < typename KEY, typename FUNC, typename ARG >
class SWITCH
{
public:
SWITCH()
{
Def = 0; // no default function at startup
}
void Process( const KEY& key, ARG arg )
{
typename std::map< KEY, FUNC >::const_iterator it = my_map.find( key );
if( it != my_map.end() ) // If key exists, call
it->second( arg ); // associated function
else // else, call
if( Def ) // default function, if there is one.
Def( arg ); // else, do nothing
}
void Add( const KEY& key, FUNC my_func )
{
typename std::map< KEY, FUNC >::const_iterator it = my_map.find( key );
if( it != my_map.end() )
{
throw "Already defined !/n";
}
my_map[ key ] = my_func;
}
void AddDefault( FUNC f )
{
Def = f;
}
private:
std::map< KEY, FUNC > my_map;
FUNC Def; // default function
};
Otros detalles están aquí .
LLVM tiene llvm::StringSwitch
que llvm::StringSwitch
la siguiente manera:
Color color = StringSwitch<Color>(argv[i])
.Case("red", Red)
.Case("orange", Orange)
.Case("yellow", Yellow)
.Case("green", Green)
.Case("blue", Blue)
.Case("indigo", Indigo)
.Cases("violet", "purple", Violet)
.Default(UnknownColor);
La gran victoria aquí es que no hay problemas debido a las colisiones hash: no importa qué, las cadenas reales siempre se comparan antes de que se acepte un caso.
Me viene a la mente un generador de hash basado en metaprogramación que puedes usar como en este ejemplo . Este es para c ++ 0x, pero estoy seguro de que puedes reproducirlo de manera similar para C ++ estándar.
Puede usar mis macros de conmutación , que admiten todo tipo de tipos de valores. En algunos casos, usar op==
unas pocas veces seguidas es un orden de magnitudes más rápido que crear un mapa cada vez y buscar en él.
sswitch(s) {
scase("foo"): {
std::cout << "s is foo" << std::endl;
break; // could fall-through if we wanted
}
// supports brace-less style too
scase("bar"):
std::cout << "s is bar" << std::endl;
break;
// default must be at the end
sdefault():
std::cout << "neither of those!" << std::endl;
break;
}
Puede utilizar cualquier tipo de implementación de interruptor c / c ++. Tu código será así:
std::string name = "Alice";
std::string gender = "boy";
std::string role;
SWITCH(name)
CASE("Alice") FALL
CASE("Carol") gender = "girl"; FALL
CASE("Bob") FALL
CASE("Dave") role = "participant"; BREAK
CASE("Mallory") FALL
CASE("Trudy") role = "attacker"; BREAK
CASE("Peggy") gender = "girl"; FALL
CASE("Victor") role = "verifier"; BREAK
DEFAULT role = "other";
END
// the role will be: "participant"
// the gender will be: "girl"
Es posible usar tipos más complicados, por ejemplo std::pairs
o cualquier estructura o clase que soporte operaciones de igualdad (o comarisions para modo rápido ).
Caracteristicas
- cualquier tipo de datos que apoyen las comparaciones o verifiquen la igualdad
- posibilidad de construir estados de conmutación anidados en cascada.
- posibilidad de romper o caer a través de declaraciones de casos
- posibilidad de usar expresiones de casos no constantes
- Posibilidad de habilitar el modo estático / dinámico rápido con la búsqueda de árbol (para C ++ 11)
Las diferencias de Sintax con el cambio de idioma son
- palabras clave mayúsculas
- necesita paréntesis para la declaración CASE
- punto y coma '';'' al final de las declaraciones no está permitido
- colon '':'' en la instrucción CASE no está permitido
- necesita una palabra clave BREAK o FALL al final de la sentencia CASE
Para el C++97
utilizado la búsqueda lineal. Para C++11
y más moderno es posible usar quick
modo quick
búsqueda de árbol wuth donde la declaración de retorno en CASE no está permitida. La implementación del lenguaje C
existe donde se usan las comparaciones de cadenas de tipo char*
y terminadas en cero.
Lea más sobre esta implementación de conmutadores.
Puedes lograrlo sin usar ningún mapa o mapa no ordenado como a continuación. Compara el primer carácter solo para identificar qué cadena. Si hay más de una coincidencia, puede recurrir a la cadena if / else dentro de esa declaración de caso. El número de comparaciones se reducirá en gran medida si no hay muchas cadenas que comiencen con la misma letra.
char *str = "foo";
switch(*str)
{
case ''f'':
//do something for foo
cout<<"Foo";
break;
case ''b'':
//do something for bar
break;
case ''c'':
if(strcmp(str, "cat") == 0)
{
//do something for cat
}
else if(strcmp(str, "camel") == 0)
{
//do something for camel
}
}
Esta parece ser la solución óptima sin ningún costo, aunque no es estándar.
Tenga en cuenta que la conmutación por const char * no funcionaría de la manera prevista, incluso si estuviera permitida.
AC String es en realidad un puntero a char. Un código como usted sugirió:
// pseudocode (incorrect C!):
switch(str) {
case "a": ...
case "b": ...
}
Siempre que nuestro lenguaje sea coherente, compararía los valores del puntero y no el contenido real de la cadena. La comparación de cadenas necesita un strcmp()
, por lo que incluso si el compilador tuviera un caso especial como "si cambiamos a un char*
, use strcmp()
lugar de ==
(que de todos modos sería un diseño deficiente), entonces de todos modos sería imposible para el compilador hacer que esto funcione como el O (1) hackeo con enteros y saltos.
Así que no te sientas mal por C / C ++ ya que no es compatible. :)
Recomiendo la solución O (logn) con map (string -> funcptr)
o (string -> some abstract object)
- si cree que necesita escalabilidad aquí. Si no lo hace, no hay nada particularmente malo con la solución O (n) con else if''s. Todavía está claro, es un código que se puede mantener, así que no hay nada de lo que sentirse mal.
Usando magia desagradable de macro y plantilla es posible obtener una búsqueda binaria desenrollada en tiempo de compilación con una sintaxis bonita, pero los MATCHES ("casos") deben ser ordenados : fastmatch.h
NEWMATCH
MATCH("asd")
some c++ code
MATCH("bqr")
... the buffer for the match is in _buf
MATCH("zzz")
... user.YOURSTUFF
/*ELSE
optional
*/
ENDMATCH(xy_match)
Esto generará (más o menos) una función bool xy_match(char *&_buf,T &user)
, por lo que debe estar en el nivel exterior. Llámalo, por ejemplo, con:
xy_match("bqr",youruserdata);
Y las break
son implícitas, no se pueden caer. Tampoco está muy documentado, lo siento. Pero encontrará que hay más posibilidades de uso, eche un vistazo. NOTA: Solo probado con g ++.
Actualizar C ++ 11:
Lambdas y la lista de inicializadores hacen las cosas mucho más bonitas (¡no se necesitan macros!):
#include <utility>
#include <algorithm>
#include <initializer_list>
template <typename KeyType,typename FunPtrType,typename Comp>
void Switch(const KeyType &value,std::initializer_list<std::pair<const KeyType,FunPtrType>> sws,Comp comp) {
typedef std::pair<const KeyType &,FunPtrType> KVT;
auto cmp=[&comp](const KVT &a,const KVT &b){ return comp(a.first,b.first); };
auto val=KVT(value,FunPtrType());
auto r=std::lower_bound(sws.begin(),sws.end(),val,cmp);
if ( (r!=sws.end())&&(!cmp(val,*r)) ) {
r->second();
} // else: not found
}
#include <string.h>
#include <stdio.h>
int main()
{
Switch<const char *,void (*)()>("ger",{ // sorted:
{"asdf",[]{ printf("0/n"); }},
{"bde",[]{ printf("1/n"); }},
{"ger",[]{ printf("2/n"); }}
},[](const char *a,const char *b){ return strcmp(a,b)<0;});
return 0;
}
Esa es la idea. Se puede encontrar una implementación más completa aquí: switch.hpp .
Actualización 2016: tiempo de compilación trie
Mi última versión de este problema utiliza una metaprogramación c ++ 11 avanzada para generar un search-trie en tiempo de compilación. A diferencia de los enfoques anteriores, esto maneja casos-ramas / cadenas sin clasificar muy bien; solo tienen que ser literales de cadena. G ++ también permite constexpr para ellos, pero no clang (a partir de HEAD 3.9.0 / trunk 274233).
En cada nodo trie se utiliza una declaración de conmutación para aprovechar el generador de código avanzado del compilador.
La implementación completa está disponible en github: smilingthax/cttrie .
Use un if...else block
. En realidad, no tienes una razón convincente para no hacerlo, aparte de que no es bonito de ver, y el bloque if...else
es la solución más sencilla.
Todo lo demás requiere un código adicional que, digamos, aumenta la complejidad. Y solo mueve la fealdad a otro lado. Pero en algún nivel, todavía tiene que ocurrir una comparación de cadenas. Ahora lo has tapado con más código.
Puede obtener algunos aumentos de rendimiento mediante el uso de un mapa o un mapa hash, pero también puede obtener ganancias similares, si no mejores, simplemente eligiendo una orden inteligente para evaluar sus bloques if...else
. Y cambiar a un mapa por razones de rendimiento es en realidad una micro-optimización prematura.
aún podrías usar un interruptor ... si conoces las etiquetas de antemano ... (esto es bastante desagradable (es decir, no hay cheques, pero eso debería ser trivial para agregar mientras tengas una cadena con terminación nula válida), debería imaginar que esto funciona más rápido que la mayoría de las opciones?
//labels: "abc", "foo", "bar", "ant" "do"
switch(lbl[0])
{
case ''a'':
{
switch(lbl[1])
{
case ''b'': // abc
case ''n'': // ant
default: // doofus!
}
}
case ''b'':
{
switch(lbl[1])
{
case ''a'': //bar
default: // doofus
}
}
case ''d'':
{
switch(lbl[1])
{
case ''o'': //do
default: // doofus
}
}
case ''f'':
{
switch(lbl[1])
{
case ''o'': //foo
default: // doofus
}
}
}
Por supuesto, si tienes una lista muy grande de "etiquetas", esto se volverá bastante complicado ...