Este es uno de los ejemplos más populares. Podemos decir clásico. Los datos se serializan en, digamos, json. La estructura tiene un campo de enumeración que desea guardar en forma de texto (no como un número). Todos. Detener. No hay una manera fácil de resolver este problema elemental en C ++. (C)
... Pero tengo muchas ganas.
Durante el año pasado, he visto cómo en casi todos los proyectos un desarrollador ha propuesto su propia visión de este problema. Y en todas partes había duplicación de código, en todas partes había algún tipo de muletas y "sutilezas". Lo que realmente está ahí, yo mismo tengo que volver a este tema de vez en cuando. Suficiente. Decidí cerrar el tema de una vez por todas, al menos para mí.
El código está lejos de ser perfecto (espero que anónimo lo corrija), pero hace su trabajo. Tal vez alguien sea útil:
Implementación
// enum_string.h
#pragma once
#define DECLARE_ENUM(T, values...) \
enum class T { values, MAX }; \
char enum_##T##_base[sizeof(#values)] = #values; \
const char* T##_tokens[static_cast<__underlying_type(T)>(T::MAX)]; \
const char* const* T##_tmp_ptr = tokenize_enum_string( \
const_cast<char*>(enum_##T##_base), sizeof(#values), T##_tokens,\
static_cast<__underlying_type(T)>(T::MAX));
#define enum_to_string(T, value) \
(T##_tokens[static_cast<__underlying_type(T)>(value)])
static const char* const* tokenize_enum_string(char* base,
int length,
const char* tokens[],
int size) {
int count = 0;
tokens[count++] = base;
for (int i = 1; i < length; ++i) {
if (base[i] == ',') {
base[i] = '\0';
if (count == size) {
return tokens;
}
do {
if (++i == length) {
return tokens;
}
} while (' ' == base[i]);
tokens[count++] = base + i;
}
}
return tokens;
}
static bool string_equals(const char* a, const char* b) {
int i = 0;
for (; a[i] && b[i]; ++i) {
if (a[i] != b[i]) {
return false;
}
}
return (a[i] == b[i]);
}
static int string_to_enum_int(const char* const tokens[], int max,
const char* value) {
for (int i = 0; i < max; ++i) {
if (string_equals(tokens[i], value)) {
return i;
}
}
return max;
}
#define string_to_enum(T, value) \
static_cast<T>(string_to_enum_int( \
T##_tokens, static_cast<__underlying_type(T)>(T::MAX), value))
Puede reemplazar fácilmente el trabajo con cadenas con sus bibliotecas favoritas, la mayor parte del código aquí es solo análisis de cadenas (realmente quería prescindir de STL).
La idea principal era asegurar el conjunto de bijectividad de enum y su cadena equivalente, y hacer que la implementación del universal en el número de elementos ( adiós, vyrviglazny hardkodny macro _NARG ). Bueno, para que el uso sea lo más lindo posible.
ejemplo de uso
// main.cpp
#include <iostream>
#include "enum_string.h"
DECLARE_ENUM(LogLevel, // enum class LogLevel
Alert, // LogLevel::Alert
Critical, // LogLevel::Critical
Error, // LogLevel::Error
Warning, // LogLevel::Warning
Notice, // LogLevel::Notice
Info, // LogLevel::Info
Debug // LogLevel::Debug
);
int main() {
// serialize
LogLevel a = LogLevel::Critical;
std::cout << enum_to_string(LogLevel, a) << std::endl;
// deserialize
switch (string_to_enum(LogLevel, "Notice")) {
case LogLevel::Alert: {
std::cout << "ALERT" << std::endl;
} break;
case LogLevel::Critical: {
std::cout << "CRITICAL" << std::endl;
} break;
case LogLevel::Error: {
std::cout << "ERROR" << std::endl;
} break;
case LogLevel::Warning: {
std::cout << "WARN" << std::endl;
} break;
case LogLevel::Notice: {
std::cout << "NOTICE" << std::endl;
} break;
case LogLevel::Info: {
std::cout << "INFO" << std::endl;
} break;
case LogLevel::Debug: {
std::cout << "DEBUG" << std::endl;
} break;
case LogLevel::MAX: {
std::cout << "Incorrect value" << std::endl;
} break;
}
return 0;
}
En cuanto a mí, no necesita explicación adicional.
Además, subido a github .
Se invita a los críticos a revisar.