Defining Scoped Enumerations with Human-readable Outputs

出力がちょっとだけホモ・サピエンスに優しい scoped enumeration を定義するだけのカンタンなマクロを作ったような気がする. GCC 4.6.1 -std=c++0x で動いたような気もする.

#include <iostream>

// 第1引数に enumeration の identifier (enum-name) を
// 第2引数以下に enumerator-definition を指定すること.
// 指定できる enumerator-definition は可変長で最大255個(のはず).
CRYOLITE_DECL_ENUM(Color, red, yellow, green, blue, white, black)

int main()
{
  std::cout << Color::red << std::endl;    // => "red"
  std::cout << Color::yellow << std::endl; // => "yellow"
  std::cout << Color::green << std::endl;  // => "green"
  std::cout << Color::blue << std::endl;   // => "blue"
  std::cout << Color::white << std::endl;  // => "white"
  std::cout << Color::black << std::endl;  // => "black"
}

出力がちょっとだけホモ・サピエンスに優しい以外は scoped enumeration とほとんど同じ.「第2引数以下に enumerator-definition を指定すること」って書いてるんだから, enumerator-definition のうち constant-expression の部分も書けないといけませんがなぜか書けちゃうようにできてる.ふしぎふしぎ.

#include <iostream>

// usamimi は 178
CRYOLITE_DECL_ENUM(KemonoMimi, inumimi, nekomimi, usamimi = 178, kitsunemimi)

int main()
{
  std::cout << KemonoMimi::inumimi << std::endl;     // => "inumimi"
  std::cout << KemonoMimi::nekomimi << std::endl;    // => "nekomimi"
  std::cout << KemonoMimi::usamimi << std::endl;     // => "usamimi"
  std::cout << KemonoMimi::kitsunemimi << std::endl; // => "kitsunemimi"

  std::cout << static_cast<int>(KemonoMimi::usamimi) << std::endl; // => "178"
}

まー,所詮マクロだし, constant-expression の部分に parenthesized じゃないむき出しの comma があると死にますさようなら.ちなみに上の例と下の例とで enumerator-definition の数を変えて可変長な感じを強調しているつもりだけど微妙に強調できてないっぽいのが悲しいですね? 見た目にこだわって可変長マクロと闘うようなことさえしなければ,実装コードにあるような数字の羅列を書かなくて済むわ, __VA_ARGS__ の展開タイミングに悩まなくて済むわ,いろいろ楽できるものを…….

以下,上記の CRYOLITE_DECL_ENUM というマクロの機能を実現するための実装っぽい何か.これ, GCC 以外でマクロの展開順序という超じゃじゃ馬娘を御しきるのが大変じゃないでしょうか.知らんけど? あ, 'a'-'z', 'A'-'Z' の連続を仮定してるの誰かなんとかしといてください私もう寝ますんで.

#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/array/elem.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#include <cstddef>
#include <type_traits>
#include <iostream>


namespace cryolite{ namespace detail{

template<typename E>
class case_dummy{
public:
  constexpr case_dummy()
    : e_()
  {}

  explicit constexpr case_dummy(E const &e)
    : e_(e)
  {}

  constexpr case_dummy operator||(E const &e)
  {
    return case_dummy(e);
  }

  template<typename T>
  constexpr case_dummy operator=(T const &)
  {
    return *this;
  }

  constexpr operator E()
  {
    return e_;
  }

private:
  E e_;
}; // class case_dummy


template<std::size_t N>
constexpr std::size_t begin_identifier_impl(char const (&str)[N], std::size_t i)
{
  return ('a' <= str[i] && str[i] <= 'z') || ('A' <= str[i] && str[i] <= 'Z') || str[i] == '_' ? i : begin_identifier_impl(str, i + 1);
}

template<std::size_t N>
constexpr std::size_t begin_identifier(char const (&str)[N])
{
  return begin_identifier_impl(str, 0u);
}

template<std::size_t N>
constexpr std::size_t end_identifier(char const (&str)[N], std::size_t i)
{
  return ('a' <= str[i] && str[i] <= 'z') || ('A' <= str[i] && str[i] <= 'Z') || ('0' <= str[i] && str[i] <= '9') || str[i] == '_' ? end_identifier(str, i + 1) : i;
}

template<std::size_t I>
class identifier
  : private identifier<I - 1>
{
public:
  template<std::size_t N>
  constexpr identifier(char const (&str)[N], std::size_t first, std::size_t last)
    : identifier<I - 1>(str, first, last),
      c_(I < last - first ? str[I + first] : '\0')
  {}

  template<typename... Chars>
  void print_impl(std::ostream &os, Chars... cs) const
  {
    identifier<I - 1>::print_impl(os, c_, cs...);
  }

  template<typename... Chars>
  void print(std::ostream &os) const
  {
    identifier<I - 1>::print_impl(os, c_);
  }

private:
  char c_;
}; // class identifier

template<>
class identifier<0u>
{
public:
  template<std::size_t N>
  explicit constexpr identifier(char const (&str)[N], std::size_t first, std::size_t last)
    : c_(first != last ? str[first] : '\0')
  {}

  template<typename... Chars>
  void print_impl(std::ostream &os, Chars... cs) const
  {
    os << typename std::common_type<char[sizeof...(cs) + 1]>::type{ c_, cs... };
  }

  template<typename... Chars>
  void print(std::ostream &os) const
  {
    os << c_;
  }

private:
  char c_;
}; // identifier<0u>

template<std::size_t N>
constexpr identifier<N> make_identifier_impl(char const (&str)[N], std::size_t i)
{
  return identifier<N>(str, i, end_identifier(str, i));
}

template<std::size_t N>
constexpr identifier<N> make_identifier(char const (&str)[N])
{
  return make_identifier_impl(str, begin_identifier(str));
}

}} // namespace cryolite::detail

#define CRYOLITE_DECL_ENUM_CASE_IMPL(ENUM_NAME, ENUMERATOR_DEFINITION)                     \
case ::cryolite::detail::case_dummy< ENUM_NAME >() || ENUM_NAME :: ENUMERATOR_DEFINITION : \
{                                                                                          \
  constexpr auto id                                                                        \
    = ::cryolite::detail::make_identifier( BOOST_PP_STRINGIZE(ENUMERATOR_DEFINITION) );    \
  id.print(os);                                                                            \
  break;                                                                                   \
}                                                                                          \
/******************************************************************************************/

#define CRYOLITE_DECL_ENUM_IMPL_IMPL(I, ENUM_NAME, ENUMERATOR_DEFINITION_ARRAY) \
CRYOLITE_DECL_ENUM_CASE_IMPL(                                                   \
  ENUM_NAME, BOOST_PP_ARRAY_ELEM(I, ENUMERATOR_DEFINITION_ARRAY))               \
/*******************************************************************************/

#define CRYOLITE_DECL_ENUM_IMPL(Z, I, DATA)                            \
CRYOLITE_DECL_ENUM_IMPL_IMPL(                                          \
  I, BOOST_PP_TUPLE_ELEM(2, 0, DATA), BOOST_PP_TUPLE_ELEM(2, 1, DATA)) \
/**********************************************************************/

#define CRYOLITE_PP_VA_ARITY_IMPL_IMPL(...) CRYOLITE_PP_VA_ARITY_IMPL(__VA_ARGS__)

#define CRYOLITE_PP_VA_ARITY(...)                    \
CRYOLITE_PP_VA_ARITY_IMPL_IMPL(                      \
  __VA_ARGS__, CRYOLITE_PP_VA_ARITY_REVERSED_IMPL()) \
/****************************************************/

#define CRYOLITE_PP_VA_ARITY_REVERSED_IMPL(...)   \
                    255, 254, 253, 252, 251, 250, \
249, 248, 247, 246, 245, 244, 243, 242, 241, 240, \
239, 238, 237, 236, 235, 234, 233, 232, 231, 230, \
229, 228, 227, 226, 225, 224, 223, 222, 221, 220, \
219, 218, 217, 216, 215, 214, 213, 212, 211, 210, \
209, 208, 207, 206, 205, 204, 203, 202, 201, 200, \
199, 198, 197, 196, 195, 194, 193, 192, 191, 190, \
189, 188, 187, 186, 185, 184, 183, 182, 181, 180, \
179, 178, 177, 176, 175, 174, 173, 172, 171, 170, \
169, 168, 167, 166, 165, 164, 163, 162, 161, 160, \
159, 158, 157, 156, 155, 154, 153, 152, 151, 150, \
149, 148, 147, 146, 145, 144, 143, 142, 141, 140, \
139, 138, 137, 136, 135, 134, 133, 132, 131, 130, \
129, 128, 127, 126, 125, 124, 123, 122, 121, 120, \
119, 118, 117, 116, 115, 114, 113, 112, 111, 110, \
109, 108, 107, 106, 105, 104, 103, 102, 101, 100, \
 99,  98,  97,  96,  95,  94,  93,  92,  91,  90, \
 89,  88,  87,  86,  85,  84,  83,  82,  81,  80, \
 79,  78,  77,  76,  75,  74,  73,  72,  71,  70, \
 69,  68,  67,  66,  65,  64,  63,  62,  61,  60, \
 59,  58,  57,  56,  55,  54,  53,  52,  51,  50, \
 49,  48,  47,  46,  45,  44,  43,  42,  41,  40, \
 39,  38,  37,  36,  35,  34,  33,  32,  31,  30, \
 29,  28,  27,  26,  25,  24,  23,  22,  21,  20, \
 19,  18,  17,  16,  15,  14,  13,  12,  11,  10, \
  9,   8,   7,   6,   5,   4,   3,   2,   1,   0  \
/*************************************************/

#define CRYOLITE_PP_VA_ARITY_IMPL(                            \
  P000, P001, P002, P003, P004, P005, P006, P007, P008, P009, \
  P010, P011, P012, P013, P014, P015, P016, P017, P018, P019, \
  P020, P021, P022, P023, P024, P025, P026, P027, P028, P029, \
  P030, P031, P032, P033, P034, P035, P036, P037, P038, P039, \
  P040, P041, P042, P043, P044, P045, P046, P047, P048, P049, \
  P050, P051, P052, P053, P054, P055, P056, P057, P058, P059, \
  P060, P061, P062, P063, P064, P065, P066, P067, P068, P069, \
  P070, P071, P072, P073, P074, P075, P076, P077, P078, P079, \
  P080, P081, P082, P083, P084, P085, P086, P087, P088, P089, \
  P090, P091, P092, P093, P094, P095, P096, P097, P098, P099, \
  P100, P101, P102, P103, P104, P105, P106, P107, P108, P109, \
  P110, P111, P112, P113, P114, P115, P116, P117, P118, P119, \
  P120, P121, P122, P123, P124, P125, P126, P127, P128, P129, \
  P130, P131, P132, P133, P134, P135, P136, P137, P138, P139, \
  P140, P141, P142, P143, P144, P145, P146, P147, P148, P149, \
  P150, P151, P152, P153, P154, P155, P156, P157, P158, P159, \
  P160, P161, P162, P163, P164, P165, P166, P167, P168, P169, \
  P170, P171, P172, P173, P174, P175, P176, P177, P178, P179, \
  P180, P181, P182, P183, P184, P185, P186, P187, P188, P189, \
  P190, P191, P192, P193, P194, P195, P196, P197, P198, P199, \
  P200, P201, P202, P203, P204, P205, P206, P207, P208, P209, \
  P210, P211, P212, P213, P214, P215, P216, P217, P218, P219, \
  P220, P221, P222, P223, P224, P225, P226, P227, P228, P229, \
  P230, P231, P232, P233, P234, P235, P236, P237, P238, P239, \
  P240, P241, P242, P243, P244, P245, P246, P247, P248, P249, \
  P250, P251, P252, P253, P254, P255, ...) P255               \
/*************************************************************/

#define CRYOLITE_DECL_ENUM(ENUM_NAME, ...)                             \
enum struct ENUM_NAME                                                  \
{                                                                      \
  __VA_ARGS__                                                          \
};                                                                     \
                                                                       \
::std::ostream &operator<<(::std::ostream &os, ENUM_NAME const &e)     \
{                                                                      \
  switch (e) {                                                         \
    BOOST_PP_REPEAT(                                                   \
      CRYOLITE_PP_VA_ARITY(__VA_ARGS__),                               \
      CRYOLITE_DECL_ENUM_IMPL,                                         \
      (ENUM_NAME, (CRYOLITE_PP_VA_ARITY(__VA_ARGS__), (__VA_ARGS__)))) \
  }                                                                    \
  return os;                                                           \
}                                                                      \
/**********************************************************************/