C ++ enumeración <-> cadena? Fácil

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.




All Articles