C ++ enum <-> string? Einfach

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.




All Articles