#include "detail/TypeRegistry.hpp" #include "Data_Object.hpp" #include "cpp_api/String.hpp" #include namespace Rice { template Enum::Enum(char const* name, Module module) : Data_Type() { Data_Type klass = define_class_under(module, name); define_methods(klass); } template inline Enum& Enum::define_value(std::string name, Enum_T value) { // Save mapping from value to name valuesToNames_[value] = name; // Store value as class constant available to Ruby Data_Object object(value, true, Enum::klass()); this->const_set(name, object); return *this; } template inline void Enum::define_methods(Data_Type klass) { // First we need a constructor klass.define_constructor(Constructor()); // Instance methods klass.define_method("to_s", [](Enum_T& self) { // We have to return string because we don't know if std::string support has // been included by the user return String(valuesToNames_[self]); }) .define_method("to_i", [](Enum_T& self) -> Underlying_T { return (Underlying_T)self; }) .define_method("inspect", [](Enum_T& self) { std::stringstream result; VALUE rubyKlass = Enum::klass().value(); result << "#<" << detail::protect(rb_class2name, rubyKlass) << "::" << Enum::valuesToNames_[self] << ">"; // We have to return string because we don't know if std::string support has // been included by the user return String(result.str()); }) .define_method("<=>", [](Enum_T& self, Enum_T& other) { if (self == other) { return 0; } else if (self < other) { return -1; } else { return 1; } }) .define_method("hash", [](Enum_T& self) -> Underlying_T { return (Underlying_T)self; }) .define_method("eql?", [](Enum_T& self, Enum_T& other) { return self == other; }); // Add aliases rb_define_alias(klass, "===", "eql?"); // Add comparable support klass.include_module(rb_mComparable); // Singleton methods klass.define_singleton_method("each", [](VALUE klass) { for (auto& pair : valuesToNames_) { Enum_T enumValue = pair.first; VALUE value = detail::To_Ruby().convert(enumValue); detail::protect(rb_yield, value); } }) .define_singleton_method("from_int", [](VALUE klass, int32_t value) { auto iter = Enum::valuesToNames_.find((Enum_T)value); if (iter == Enum::valuesToNames_.end()) { throw std::runtime_error("Unknown enum value: " + std::to_string(value)); } std::string name = iter->second; return Class(klass).const_get(name); }); } template Enum define_enum(char const* name, Module module) { if (detail::TypeRegistry::isDefined()) { return Enum(); } return Enum(name, module); } }