diff --git a/flang/include/flang/Common/enum-set.h b/flang/include/flang/Common/enum-set.h --- a/flang/include/flang/Common/enum-set.h +++ b/flang/include/flang/Common/enum-set.h @@ -206,7 +206,8 @@ } template - STREAM &Dump(STREAM &o, std::string EnumToString(enumerationType)) const { + STREAM &Dump( + STREAM &o, const std::string &EnumToString(enumerationType)) const { char sep{'{'}; IterateOverMembers([&](auto e) { o << sep << EnumToString(e); diff --git a/flang/include/flang/Common/idioms.h b/flang/include/flang/Common/idioms.h --- a/flang/include/flang/Common/idioms.h +++ b/flang/include/flang/Common/idioms.h @@ -24,6 +24,7 @@ #endif #include "visit.h" +#include #include #include #include @@ -110,12 +111,14 @@ } \ template constexpr bool T{class_trait_ns_##T::trait_value()}; -// Define enum class NAME with the given enumerators, a static -// function EnumToString() that maps enumerators to std::string, -// and a constant NAME_enumSize that captures the number of items -// in the enum class. +// Define enum class NAME with the given enumerators, +// - a static function EnumToString() that maps enumerators to std::string, +// - a constant NAME_enumSize that captures the number of items in the enum, +// - a struct NAME_struct that implements a Meyers singleton to hold the mapping +// from index to names -std::string EnumIndexToString(int index, const char *names); +void BuildIndexToString( + const char *commaSeparated, std::string enumNames[], int enumSize); template struct ListItemCount { constexpr ListItemCount(std::initializer_list list) : value{list.size()} {} @@ -128,9 +131,24 @@ enum { __VA_ARGS__ }; \ return Fortran::common::ListItemCount{__VA_ARGS__}.value; \ }()}; \ - [[maybe_unused]] static inline std::string EnumToString(NAME e) { \ - return Fortran::common::EnumIndexToString( \ - static_cast(e), #__VA_ARGS__); \ + struct NAME##_struct { \ + NAME##_struct(const NAME##_struct &) = delete; \ + NAME##_struct &operator=(const NAME##_struct &) = delete; \ + static NAME##_struct &instance() { \ + static NAME##_struct s; \ + return s; \ + } \ + std::array _enumNames; \ +\ + private: \ + NAME##_struct() { \ + Fortran::common::BuildIndexToString( \ + #__VA_ARGS__, _enumNames.data(), NAME##_enumSize); \ + } \ + ~NAME##_struct() {} \ + }; \ + [[maybe_unused]] static inline const std::string &EnumToString(NAME e) { \ + return NAME##_struct::instance()._enumNames[static_cast(e)]; \ } // Check that a pointer is non-null and dereference it diff --git a/flang/lib/Common/idioms.cpp b/flang/lib/Common/idioms.cpp --- a/flang/lib/Common/idioms.cpp +++ b/flang/lib/Common/idioms.cpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace Fortran::common { @@ -23,21 +24,22 @@ std::abort(); } -// Convert the int index of an enumerator to a string. -// enumNames is a list of the names, separated by commas with optional spaces. -// This is intended for use from the expansion of ENUM_CLASS. -std::string EnumIndexToString(int index, const char *enumNames) { - const char *p{enumNames}; - for (; index > 0; --index, ++p) { - for (; *p && *p != ','; ++p) { - } - } - for (; *p == ' '; ++p) { - } - CHECK(*p != '\0'); - const char *q = p; - for (; *q && *q != ' ' && *q != ','; ++q) { +// Converts the comma separated list of enumerators into tokens which are then +// stored into the provided array of strings. This is intended for use from the +// expansion of ENUM_CLASS. +void BuildIndexToString( + const char *commaSeparated, std::string enumNames[], int enumSize) { + std::string input(commaSeparated); + std::regex reg("\\s*,\\s*"); + + std::sregex_token_iterator iter(input.begin(), input.end(), reg, -1); + std::sregex_token_iterator end; + int index = 0; + while (iter != end) { + enumNames[index] = *iter; + iter++; + index++; } - return std::string(p, q - p); + CHECK(index == enumSize); } } // namespace Fortran::common