May not be the best solution ever, but this does the job pretty well.
The names of the enums are lazy loaded, so after the first call to to_string
it will be loaded and kept in memory.
The method from_string
was not implemented as it was not requested here, but it can be easily implemented by calling get_enum_names
, searching the name in the vector, and casting its position to the enum type.
Please add the definition of get_enum_names
in a cpp file (only the declaration should be in the header file).
It should work fine with C++ >= 11
Tested in gcc and MSVC
Implementation:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <unordered_map>
// Add the definition of this method into a cpp file. (only the declaration in the header)
const std::vector<std::string>& get_enum_names(const std::string& en_key, const std::string& en_str)
{
static std::unordered_map<std::string, std::vector<std::string>> en_names_map;
const auto it = en_names_map.find(en_key);
if (it != en_names_map.end())
return it->second;
constexpr auto delim(',');
std::vector<std::string> en_names;
std::size_t start{};
auto end = en_str.find(delim);
while (end != std::string::npos)
{
while (en_str[start] == ' ')
++start;
en_names.push_back(en_str.substr(start, end - start));
start = end + 1;
end = en_str.find(delim, start);
}
while (en_str[start] == ' ')
++start;
en_names.push_back(en_str.substr(start));
return en_names_map.emplace(en_key, std::move(en_names)).first->second;
}
#define DECLARE_ENUM(ENUM_NAME, ...) \
enum class ENUM_NAME{ __VA_ARGS__ }; \
inline std::string to_string(ENUM_NAME en) \
{ \
const auto& names = get_enum_names(#ENUM_NAME #__VA_ARGS__, #__VA_ARGS__); \
return names[static_cast<std::size_t>(en)]; \
}
Usage:
DECLARE_ENUM(WeekEnum, Mon, Tue, Wed, Thu, Fri, Sat, Sun);
int main()
{
WeekEnum weekDay = WeekEnum::Wed;
std::cout << to_string(weekDay) << std::endl; // prints Wed
std::cout << to_string(WeekEnum::Sat) << std::endl; // prints Sat
return 0;
}