Hier ist eines der beliebtesten Beispiele. Wir können klassisch sagen. Die Daten werden beispielsweise in json serialisiert. Die Struktur verfügt über ein Aufzählungsfeld, das Sie in Textform speichern möchten (nicht als Zahl). Alle. Halt. Es gibt keine einfache Möglichkeit, diese elementare Aufgabe in C ++ zu lösen. (c)
... aber ich will es wirklich.
Im letzten Jahr habe ich gesehen, wie in fast jedem Projekt ein Entwickler seine eigene Vision dieses Problems vorgeschlagen hat. Und überall gab es doppelten Code, überall gab es Krücken und "Feinheiten". Was wirklich da ist, muss ich selbst von Zeit zu Zeit auf dieses Thema zurückkommen. Genug. Ich beschloss, das Problem ein für alle Mal zu schließen, zumindest für mich.
Der Code ist alles andere als perfekt (ich hoffe, anonym wird ihn korrigieren), aber er macht seinen Job. Vielleicht wird jemand nützlich sein:
Implementierung
// 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))
Sie können die Arbeit mit Zeichenfolgen problemlos durch Ihre Lieblingsbibliotheken ersetzen. Der größte Teil des Codes hier besteht nur aus der Analyse von Zeichenfolgen (ich wollte unbedingt auf STL verzichten).
Die Hauptidee war es, die Bijektivität von enum und seinem String-Äquivalent sicherzustellen und die Implementierung des Universellen in der Anzahl der Elemente zu ermöglichen (auf Wiedersehen, vyrviglazny hardkodny macro _NARG ). Nun, um den Gebrauch so süß wie möglich zu machen.
Anwendungsbeispiel
// 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;
}
Für mich bedarf es keiner zusätzlichen Erklärung.
Auch auf Github hochgeladen .
Kritiker sind herzlich eingeladen, dies zu überprüfen.