Énumération C ++ <-> chaîne? Facile

Voici l'un des exemples les plus populaires. On peut dire classique. Les données sont sérialisées dans, disons, json. La structure a un champ enum que vous souhaitez enregistrer sous forme de texte (pas sous forme de nombre). Tout. Arrêtez. Il n'y a pas de moyen simple de résoudre ce problème élémentaire en C ++. (c)

... Mais je veux vraiment.

Au cours de la dernière année, j'ai vu comment, dans presque tous les projets, un développeur a proposé sa propre vision de ce problème. Et partout il y avait duplication de code, partout il y avait des sortes de béquilles, et des «subtilités». Ce qui est vraiment là, je dois moi-même revenir sur ce sujet de temps en temps. Assez. J'ai décidé de clore le problème une fois pour toutes, du moins pour moi.

Le code est loin d'être parfait (j'espère que anonyme le corrigera), mais il fait son travail. Peut-être que quelqu'un vous sera utile:

la mise en oeuvre
// 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))

Vous pouvez facilement remplacer le travail avec des chaînes par vos bibliothèques préférées, la plupart du code ici est juste une analyse de chaînes (je voulais vraiment me passer de STL).

L'idée principale était d'assurer la bijectivité de l'énumération et de sa chaîne équivalente, et de faire l'implémentation de l'universel dans le nombre d'éléments ( au revoir, macro vyrviglazny hardkodny _NARG ). Bon, pour que l'utilisation soit la plus mignonne possible.

exemple d'utilisation
// 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;
}

Quant à moi, il n'a pas besoin d'explications supplémentaires.

En outre, téléchargé sur github .

Les critiques sont aimablement invités à donner leur avis.




All Articles