diff --git a/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.cpp b/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.cpp index f987630..2f4a08c 100644 --- a/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.cpp +++ b/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.cpp @@ -18,6 +18,7 @@ namespace cct Write("#include "); Write("#include \"Concerto/Reflection/GlobalNamespace/GlobalNamespace.hpp\""); Write("#include "); + Write("#include "); Write("#include \"{}Package.gen.hpp\"", package.name); for (auto& header : args) @@ -29,7 +30,7 @@ namespace cct Write("using namespace cct::refl;"); for (auto& enum_ : package.enums) - GenerateEnum(enum_); + GenerateEnum(enum_, ""); for (auto& klass : package.classes) { if (klass.isGenericClass) @@ -53,7 +54,7 @@ namespace cct for (auto& nestedNs : ns.namespaces) GenerateNamespace(nestedNs, namespaceChain + "::"s + std::string(ns.name)); for (auto& enum_ : ns.enums) - GenerateEnum(enum_); + GenerateEnum(enum_, namespaceChain + "::"s + std::string(ns.name)); for (auto& klass : ns.classes) { if (klass.isGenericClass) @@ -103,6 +104,10 @@ namespace cct EnterScope(); Write("ns->LoadClasses();"); LeaveScope(); + for (auto& enum_ : ns.enums) + { + Write("AddClass(std::make_unique<{}::Internal{}EnumerationClass>());", ns.name, enum_.name); + } for (auto& klass : ns.classes) { if (klass.isGenericClass) @@ -676,41 +681,55 @@ namespace cct } } - void CppGenerator::GenerateEnum(const Enum& enum_) + void CppGenerator::GenerateEnum(const Enum& enum_, std::string_view ns) { - Write("std::string_view {}ToString({} value)", enum_.name, enum_.name); + + Write("class Internal{}EnumerationClass : public cct::refl::EnumerationClass", enum_.name); EnterScope(); { - Write("switch(value)"); + Write("public:"); + Write("Internal{}EnumerationClass()", enum_.name); + Write(": cct::refl::EnumerationClass(nullptr, \"{}\"s)", enum_.name); EnterScope(); { + } + LeaveScope(); - for (auto& elem : enum_.elements) + NewLine(); + Write("~Internal{}EnumerationClass() override", enum_.name); + EnterScope(); + { + } + LeaveScope(); + + NewLine(); + Write("void Initialize() override"); + EnterScope(); + { + for (const auto& elem : enum_.elements) { - Write("case {}::{}:", enum_.name, elem.name); - Write("return \"{}\"sv;", elem.name); + Write("AddEnumValue(\"{}\", {});", elem.name, elem.value); } } LeaveScope(); - Write("CCT_ASSERT_FALSE(\"Invalid enum value\");"); - Write("return {{}};"); - } - LeaveScope(); - Write("{} {}FromString(std::string_view value)", enum_.name, enum_.name); - EnterScope(); - { - for (auto& elem : enum_.elements) + NewLine(); + Write("cct::refl::Object* GetMemberVariable(std::size_t, const cct::refl::Object&) const override"); + EnterScope(); { - Write("if (value == \"{}\"sv)", elem.name); - EnterScope(); - Write("return {}::{};", enum_.name, elem.name); - LeaveScope(); + Write("return nullptr;"); + } + LeaveScope(); + + NewLine(); + Write("void* GetNativeMemberVariable(std::size_t, const cct::refl::Object&) const override"); + EnterScope(); + { + Write("return nullptr;"); } - Write("CCT_ASSERT_FALSE(\"Invalid enum value: {{}}\", value);"); - Write("return {{}};"); + LeaveScope(); } - LeaveScope(); + LeaveScope(";"); } void CppGenerator::GeneratePackage(const Package& pkg) diff --git a/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.hpp b/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.hpp index 3980271..c6740a4 100644 --- a/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.hpp +++ b/Src/Concerto/PackageGenerator/CppGenerator/CppGenerator.hpp @@ -20,7 +20,7 @@ namespace cct void GenerateGenericClass(std::string_view ns, const Class& klass); void GenerateTemplateClass(std::string_view ns, const Class& klass); void GenerateClassMethod(std::string_view className, const Class::Method& method, std::string_view ns, std::size_t methodIndex); - void GenerateEnum(const Enum& enum_); + void GenerateEnum(const Enum& enum_, std::string_view ns = ""); void GeneratePackage(const Package& pkg); }; } diff --git a/Src/Concerto/Reflection/EnumValue/EnumValue.cpp b/Src/Concerto/Reflection/EnumValue/EnumValue.cpp new file mode 100644 index 0000000..f4e7a0b --- /dev/null +++ b/Src/Concerto/Reflection/EnumValue/EnumValue.cpp @@ -0,0 +1,34 @@ +// +// Created by arthur on 31/12/2025 +// + +#include "Concerto/Reflection/EnumValue/EnumValue.hpp" +#include "Concerto/Core/Assert.hpp" + +namespace cct::refl +{ + EnumValue::EnumValue(std::string name, cct::Int64 value, std::size_t index) + : m_name(std::move(name)), m_value(value), m_index(index) + { + } + + bool EnumValue::HasAttribute(std::string_view attribute) const + { + return m_attributes.find(std::string(attribute)) != m_attributes.end(); + } + + std::string_view EnumValue::GetAttribute(std::string_view attribute) const + { + const auto it = m_attributes.find(std::string(attribute)); + if (it != m_attributes.end()) + return it->second; + + CCT_ASSERT_FALSE("EnumValue::GetAttribute: attribute '{}' not found", attribute); + return ""; + } + + void EnumValue::AddAttribute(std::string name, std::string value) + { + m_attributes[std::move(name)] = std::move(value); + } +} diff --git a/Src/Concerto/Reflection/EnumValue/EnumValue.hpp b/Src/Concerto/Reflection/EnumValue/EnumValue.hpp new file mode 100644 index 0000000..66361cc --- /dev/null +++ b/Src/Concerto/Reflection/EnumValue/EnumValue.hpp @@ -0,0 +1,52 @@ +// +// Created by arthur on 31/12/2025 +// + +#ifndef CONCERTO_REFLECTION_ENUM_VALUE_HPP +#define CONCERTO_REFLECTION_ENUM_VALUE_HPP + +#include +#include +#include + +#include + +#include "Concerto/Reflection/Defines.hpp" + +namespace cct::refl +{ + class CCT_REFLECTION_API EnumValue + { + public: + EnumValue(std::string name, cct::Int64 value, std::size_t index); + ~EnumValue() = default; + + EnumValue(const EnumValue&) = delete; + EnumValue(EnumValue&&) = default; + + EnumValue& operator=(const EnumValue&) = delete; + EnumValue& operator=(EnumValue&&) = default; + + [[nodiscard]] std::string_view GetName() const; + [[nodiscard]] cct::Int64 GetValue() const; + [[nodiscard]] std::size_t GetIndex() const; + + [[nodiscard]] bool HasAttribute(std::string_view attribute) const; + [[nodiscard]] std::string_view GetAttribute(std::string_view attribute) const; + + protected: + void AddAttribute(std::string name, std::string value); + + private: + std::string m_name; + cct::Int64 m_value; + std::size_t m_index; + std::unordered_map m_attributes; + + friend class Enumeration; + }; +} + +#include "Concerto/Reflection/EnumValue/EnumValue.inl" + +#endif // CONCERTO_REFLECTION_ENUM_VALUE_HPP diff --git a/Src/Concerto/Reflection/EnumValue/EnumValue.inl b/Src/Concerto/Reflection/EnumValue/EnumValue.inl new file mode 100644 index 0000000..adc560e --- /dev/null +++ b/Src/Concerto/Reflection/EnumValue/EnumValue.inl @@ -0,0 +1,23 @@ +// +// Created by arthur on 31/12/2025 +// + +#pragma once + +namespace cct::refl +{ + inline std::string_view EnumValue::GetName() const + { + return m_name; + } + + inline cct::Int64 EnumValue::GetValue() const + { + return m_value; + } + + inline std::size_t EnumValue::GetIndex() const + { + return m_index; + } +} diff --git a/Src/Concerto/Reflection/Enumeration/EnumIterator.hpp b/Src/Concerto/Reflection/Enumeration/EnumIterator.hpp new file mode 100644 index 0000000..10c67e0 --- /dev/null +++ b/Src/Concerto/Reflection/Enumeration/EnumIterator.hpp @@ -0,0 +1,98 @@ +// +// Created by arthur on 31/12/2025 +// + +#ifndef CONCERTO_REFLECTION_ENUM_ITERATOR_HPP +#define CONCERTO_REFLECTION_ENUM_ITERATOR_HPP + +#include +#include +#include + +#include + +namespace cct::refl +{ + class EnumValue; + + class EnumIterator + { + public: + explicit EnumIterator(std::span> enumValues, std::size_t index = 0) + : m_enumValues(enumValues), m_index(index) + { + } + + inline std::string_view GetName() const; + inline cct::Int64 GetValue() const; + inline std::size_t GetIndex() const; + + inline EnumIterator& operator++(); + inline bool operator!=(const EnumIterator& other) const; + inline EnumIterator operator*() const { return *this; } + + private: + std::span> m_enumValues; + std::size_t m_index; + + friend class EnumerationClass; + friend class Enumeration; + }; + + class EnumIterable + { + public: + explicit EnumIterable(std::span> enumValues) + : m_enumValues(enumValues) + { + } + + EnumIterator begin() const + { + return EnumIterator(m_enumValues, 0); + } + + EnumIterator end() const + { + return EnumIterator(m_enumValues, m_enumValues.size()); + } + + private: + std::span> m_enumValues; + }; +} + +namespace std +{ + template<> + struct tuple_size : std::integral_constant {}; + + template<> + struct tuple_element<0, cct::refl::EnumIterator> + { + using type = std::string_view; + }; + + template<> + struct tuple_element<1, cct::refl::EnumIterator> + { + using type = cct::Int64; + }; +} + +namespace cct::refl +{ + template + auto get(const EnumIterator& it) noexcept + { + static_assert(N < 2, "EnumIterator has only 2 elements: name and value"); + if constexpr (N == 0) + return it.GetName(); + else if constexpr (N == 1) + return it.GetValue(); + } +} + +#include "Concerto/Reflection/Enumeration/EnumIterator.inl" + +#endif // CONCERTO_REFLECTION_ENUM_ITERATOR_HPP diff --git a/Src/Concerto/Reflection/Enumeration/EnumIterator.inl b/Src/Concerto/Reflection/Enumeration/EnumIterator.inl new file mode 100644 index 0000000..fd0e4ac --- /dev/null +++ b/Src/Concerto/Reflection/Enumeration/EnumIterator.inl @@ -0,0 +1,43 @@ +// +// Created by arthur on 31/12/2025 +// + +#pragma once + +#include "Concerto/Reflection/Enumeration/EnumIterator.hpp" +#include "Concerto/Reflection/EnumValue/EnumValue.hpp" + +namespace cct::refl +{ + inline std::string_view EnumIterator::GetName() const + { + if (m_index >= m_enumValues.size() || !m_enumValues[m_index]) + return {}; + return m_enumValues[m_index]->GetName(); + } + + inline cct::Int64 EnumIterator::GetValue() const + { + if (m_index >= m_enumValues.size() || !m_enumValues[m_index]) + return 0; + return m_enumValues[m_index]->GetValue(); + } + + inline std::size_t EnumIterator::GetIndex() const + { + if (m_index >= m_enumValues.size() || !m_enumValues[m_index]) + return 0; + return m_enumValues[m_index]->GetIndex(); + } + + inline EnumIterator& EnumIterator::operator++() + { + ++m_index; + return *this; + } + + inline bool EnumIterator::operator!=(const EnumIterator& other) const + { + return m_index != other.m_index; + } +} diff --git a/Src/Concerto/Reflection/Enumeration/Enumeration.cpp b/Src/Concerto/Reflection/Enumeration/Enumeration.cpp new file mode 100644 index 0000000..2af0536 --- /dev/null +++ b/Src/Concerto/Reflection/Enumeration/Enumeration.cpp @@ -0,0 +1,87 @@ +// +// Created by arthur on 31/12/2025 +// + +#include "Concerto/Reflection/Enumeration/Enumeration.hpp" +#include "Concerto/Core/Assert.hpp" +#include "Concerto/Reflection/Enumeration/EnumerationClass.hpp" +#include "Concerto/Reflection/Enumeration/EnumIterator.hpp" + +namespace cct::refl +{ + Enumeration::Enumeration() + : m_value(0) + { + m_dynamicClass = m_class; + } + + std::string Enumeration::ToString(cct::Int64 value) const + { + if (const auto* enumClass = dynamic_cast(m_dynamicClass)) + { + const EnumValue* enumValue = enumClass->GetEnumValue(value); + if (enumValue) + return std::string(enumValue->GetName()); + } + + const EnumValue* enumValue = GetEnumValue(value); + if (enumValue) + return std::string(enumValue->GetName()); + + CCT_ASSERT_FALSE("Enumeration::ToString: enum value not found: {}", value); + return std::to_string(value); + } + + std::string Enumeration::ToString() const + { + return ToString(m_value); + } + + cct::Int64 Enumeration::FromString(std::string_view name) const + { + if (const auto* enumClass = dynamic_cast(m_dynamicClass)) + { + const EnumValue* enumValue = enumClass->GetEnumValue(name); + if (enumValue) + return enumValue->GetValue(); + } + + const EnumValue* enumValue = GetEnumValue(name); + if (enumValue) + return enumValue->GetValue(); + + CCT_ASSERT_FALSE("Enumeration::FromString: enum value not found: {}", name); + return 0; + } + + bool Enumeration::HasFlag(cct::Int64 value, cct::Int64 flag) const + { + return (value & flag) == flag; + } + + cct::Int64 Enumeration::SetFlag(cct::Int64 value, cct::Int64 flag) const + { + return value | flag; + } + + cct::Int64 Enumeration::ToggleFlag(cct::Int64 value, cct::Int64 flag) const + { + return value ^ flag; + } + + EnumIterable Enumeration::Enumerate() const + { + return EnumIterable(m_enumValues); + } + + void Enumeration::AddEnumValue(std::string name, cct::Int64 value) + { + std::size_t index = m_enumValues.size(); + m_enumValues.emplace_back(std::make_unique(std::move(name), value, index)); + } + + void Enumeration::SetEnumValue(cct::Int64 value) + { + m_value = value; + } +} diff --git a/Src/Concerto/Reflection/Enumeration/Enumeration.hpp b/Src/Concerto/Reflection/Enumeration/Enumeration.hpp new file mode 100644 index 0000000..79857c1 --- /dev/null +++ b/Src/Concerto/Reflection/Enumeration/Enumeration.hpp @@ -0,0 +1,68 @@ +// +// Created by arthur on 31/12/2025 +// + +#ifndef CONCERTO_REFLECTION_ENUMERATION_HPP +#define CONCERTO_REFLECTION_ENUMERATION_HPP + +#include +#include +#include +#include +#include + +#include + +#include "Concerto/Reflection/Object/Object.refl.hpp" +#include "Concerto/Reflection/EnumValue/EnumValue.hpp" +#include "Concerto/Reflection/Enumeration/EnumIterator.hpp" + +namespace cct::refl +{ + class CCT_REFL_CLASS() CCT_REFLECTION_API Enumeration : public Object + { + public: + Enumeration(); + ~Enumeration() override = default; + + Enumeration(const Enumeration&) = delete; + Enumeration(Enumeration&&) = default; + + Enumeration& operator=(const Enumeration&) = delete; + Enumeration& operator=(Enumeration&&) = default; + + [[nodiscard]] std::span> GetEnumValues() const; + [[nodiscard]] std::size_t GetEnumValueCount() const; + + [[nodiscard]] const EnumValue* GetEnumValue(std::size_t index) const; + [[nodiscard]] const EnumValue* GetEnumValue(std::string_view name) const; + [[nodiscard]] const EnumValue* GetEnumValue(cct::Int64 value) const; + + [[nodiscard]] EnumIterable Enumerate() const; + + [[nodiscard]] std::string ToString(cct::Int64 value) const; + [[nodiscard]] std::string ToString() const; + + [[nodiscard]] cct::Int64 FromString(std::string_view name) const; + + [[nodiscard]] bool HasFlag(cct::Int64 value, cct::Int64 flag) const; + cct::Int64 SetFlag(cct::Int64 value, cct::Int64 flag) const; + cct::Int64 ToggleFlag(cct::Int64 value, cct::Int64 flag) const; + + CCT_OBJECT(Enumeration); + + protected: + void AddEnumValue(std::string name, cct::Int64 value); + void SetEnumValue(cct::Int64 value); + + private: + std::vector> m_enumValues; + cct::Int64 m_value; + + friend class EnumerationClass; + }; +} + +#include "Concerto/Reflection/Enumeration/Enumeration.inl" + +#endif // CONCERTO_REFLECTION_ENUMERATION_HPP diff --git a/Src/Concerto/Reflection/Enumeration/Enumeration.inl b/Src/Concerto/Reflection/Enumeration/Enumeration.inl new file mode 100644 index 0000000..5224fa3 --- /dev/null +++ b/Src/Concerto/Reflection/Enumeration/Enumeration.inl @@ -0,0 +1,54 @@ +// +// Created by arthur on 31/12/2025 +// + +#pragma once + +#include "Concerto/Core/Assert.hpp" + +namespace cct::refl +{ + inline std::span> Enumeration::GetEnumValues() const + { + return m_enumValues; + } + + inline std::size_t Enumeration::GetEnumValueCount() const + { + return m_enumValues.size(); + } + + inline const EnumValue* Enumeration::GetEnumValue(std::size_t index) const + { + if (index >= m_enumValues.size()) + { + CCT_ASSERT_FALSE("Index out of range: {}", index); + return nullptr; + } + return m_enumValues[index].get(); + } + + inline const EnumValue* Enumeration::GetEnumValue(std::string_view name) const + { + for (const auto& enumValue : m_enumValues) + { + if (enumValue && enumValue->GetName() == name) + return enumValue.get(); + } + + CCT_ASSERT_FALSE("Could not find enum value with name: {}", name); + return nullptr; + } + + inline const EnumValue* Enumeration::GetEnumValue(cct::Int64 value) const + { + for (const auto& enumValue : m_enumValues) + { + if (enumValue && enumValue->GetValue() == value) + return enumValue.get(); + } + + CCT_ASSERT_FALSE("Could not find enum value with value: {}", value); + return nullptr; + } +} diff --git a/Src/Concerto/Reflection/Enumeration/EnumerationClass.cpp b/Src/Concerto/Reflection/Enumeration/EnumerationClass.cpp new file mode 100644 index 0000000..e5a17c0 --- /dev/null +++ b/Src/Concerto/Reflection/Enumeration/EnumerationClass.cpp @@ -0,0 +1,79 @@ +// +// Created by arthur on 31/12/2025 +// + +#include "Concerto/Reflection/Enumeration/EnumerationClass.hpp" +#include "Concerto/Reflection/Enumeration/Enumeration.hpp" +#include "Concerto/Reflection/Enumeration/EnumIterator.hpp" +#include "Concerto/Reflection/Namespace/Namespace.hpp" + +namespace cct::refl +{ + EnumerationClass::EnumerationClass(Namespace* nameSpace, std::string name) + : Class(nameSpace, std::move(name), nullptr) + { + } + + std::span> EnumerationClass::GetEnumValues() const + { + return m_enumValues; + } + + std::size_t EnumerationClass::GetEnumValueCount() const + { + return m_enumValues.size(); + } + + const EnumValue* EnumerationClass::GetEnumValue(std::size_t index) const + { + if (index >= m_enumValues.size()) + return nullptr; + return m_enumValues[index].get(); + } + + const EnumValue* EnumerationClass::GetEnumValue(std::string_view name) const + { + for (const auto& enumValue : m_enumValues) + { + if (enumValue && enumValue->GetName() == name) + return enumValue.get(); + } + + CCT_ASSERT_FALSE("Could not find enum value with name: {}", name); + return nullptr; + } + + const EnumValue* EnumerationClass::GetEnumValue(cct::Int64 value) const + { + for (const auto& enumValue : m_enumValues) + { + if (enumValue && enumValue->GetValue() == value) + return enumValue.get(); + } + + CCT_ASSERT_FALSE("Could not find enum value with value: {}", value); + return nullptr; + } + + EnumIterable EnumerationClass::Enumerate() const + { + return EnumIterable(m_enumValues); + } + + std::unique_ptr EnumerationClass::CreateDefaultObject() const + { + auto enumeration = std::make_unique(); + enumeration->SetDynamicClass(this); + return enumeration; + } + + void EnumerationClass::AddEnumValue(std::string name, cct::Int64 value) + { + std::size_t index = m_enumValues.size(); + m_enumValues.emplace_back(std::make_unique(std::move(name), value, index)); + } + + void EnumerationClass::Initialize() + { + } +} diff --git a/Src/Concerto/Reflection/Enumeration/EnumerationClass.hpp b/Src/Concerto/Reflection/Enumeration/EnumerationClass.hpp new file mode 100644 index 0000000..0f12cad --- /dev/null +++ b/Src/Concerto/Reflection/Enumeration/EnumerationClass.hpp @@ -0,0 +1,53 @@ +// +// Created by arthur on 31/12/2025 +// + +#ifndef CONCERTO_REFLECTION_ENUMERATION_CLASS_HPP +#define CONCERTO_REFLECTION_ENUMERATION_CLASS_HPP + +#include +#include +#include +#include +#include + +#include "Concerto/Reflection/Class/Class.hpp" +#include "Concerto/Reflection/EnumValue/EnumValue.hpp" +#include "Concerto/Reflection/Enumeration/EnumIterator.hpp" + +namespace cct::refl +{ + class CCT_REFLECTION_API EnumerationClass : public Class + { + public: + EnumerationClass(Namespace* nameSpace, std::string name); + ~EnumerationClass() override = default; + + EnumerationClass(const EnumerationClass&) = delete; + EnumerationClass(EnumerationClass&&) = default; + + EnumerationClass& operator=(const EnumerationClass&) = delete; + EnumerationClass& operator=(EnumerationClass&&) = default; + + [[nodiscard]] std::span> GetEnumValues() const; + [[nodiscard]] std::size_t GetEnumValueCount() const; + + [[nodiscard]] const EnumValue* GetEnumValue(std::size_t index) const; + [[nodiscard]] const EnumValue* GetEnumValue(std::string_view name) const; + [[nodiscard]] const EnumValue* GetEnumValue(cct::Int64 value) const; + + [[nodiscard]] EnumIterable Enumerate() const; + + [[nodiscard]] std::unique_ptr CreateDefaultObject() const override; + + protected: + void AddEnumValue(std::string name, cct::Int64 value); + + private: + virtual void Initialize() override; + + std::vector> m_enumValues; + }; +} + +#endif // CONCERTO_REFLECTION_ENUMERATION_CLASS_HPP diff --git a/Src/Concerto/Reflection/Reflection.hpp b/Src/Concerto/Reflection/Reflection.hpp index 7d7e3a5..36e75e6 100644 --- a/Src/Concerto/Reflection/Reflection.hpp +++ b/Src/Concerto/Reflection/Reflection.hpp @@ -11,5 +11,9 @@ #include "Concerto/Reflection/Namespace/Namespace.hpp" #include "Concerto/Reflection/GlobalNamespace/GlobalNamespace.hpp" #include "Concerto/Reflection/Object/Object.refl.hpp" +#include "Concerto/Reflection/EnumValue/EnumValue.hpp" +#include "Concerto/Reflection/Enumeration/Enumeration.hpp" +#include "Concerto/Reflection/Enumeration/EnumerationClass.hpp" +#include "Concerto/Reflection/Enumeration/EnumIterator.hpp" #endif //CONCERTO_REFLECTION_REFLECTION_HPP \ No newline at end of file diff --git a/Src/Tests/Class.cpp b/Src/Tests/Class.cpp index 013322e..b5ea50a 100644 --- a/Src/Tests/Class.cpp +++ b/Src/Tests/Class.cpp @@ -22,7 +22,7 @@ SCENARIO("Class metadata verification") REQUIRE(packageLoader.AddPackage(CreateConcertoReflectionTestsPackage())); packageLoader.LoadPackages(); - CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 11); + CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 12); CHECK(cct::refl::GlobalNamespace::Get().GetNamespaceCount() == 1); THEN("We are getting the class Object") diff --git a/Src/Tests/Enum.cpp b/Src/Tests/Enum.cpp new file mode 100644 index 0000000..50375f9 --- /dev/null +++ b/Src/Tests/Enum.cpp @@ -0,0 +1,159 @@ +// +// Created by arthur on 12/31/2024. +// +#include + +#include +#include + +#include +#include +#include +#include + +SCENARIO("Enumeration metadata verification") +{ + using namespace std::string_view_literals; + GIVEN("The core package") + { + WHEN("The package is initialized") + { + cct::refl::PackageLoader packageLoader; + REQUIRE(packageLoader.AddPackage(CreateConcertoReflectionPackage())); + REQUIRE(packageLoader.AddPackage(CreateConcertoReflectionTestsPackage())); + packageLoader.LoadPackages(); + + THEN("We can get the SampleEnum enumeration class") + { + const auto* enumClass = cct::refl::GetClassByName("cct::sample::SampleEnum"); + REQUIRE(enumClass); + CHECK(enumClass->GetName() == "SampleEnum"sv); + + const auto* enumClassPtr = dynamic_cast(enumClass); + REQUIRE(enumClassPtr); + + THEN("We can verify the enum values") + { + CHECK(enumClassPtr->GetEnumValueCount() == 2); + + const auto* fooValue = enumClassPtr->GetEnumValue("Foo"sv); + REQUIRE(fooValue); + CHECK(fooValue->GetName() == "Foo"sv); + CHECK(fooValue->GetValue() == 0); + CHECK(fooValue->GetIndex() == 0); + + const auto* barValue = enumClassPtr->GetEnumValue("Bar"sv); + REQUIRE(barValue); + CHECK(barValue->GetName() == "Bar"sv); + CHECK(barValue->GetValue() == 1); + CHECK(barValue->GetIndex() == 1); + + const auto* fooByValue = enumClassPtr->GetEnumValue(static_cast(0)); + REQUIRE(fooByValue); + CHECK(fooByValue->GetName() == "Foo"sv); + + const auto* barByValue = enumClassPtr->GetEnumValue(static_cast(1)); + REQUIRE(barByValue); + CHECK(barByValue->GetName() == "Bar"sv); + + const auto* fooByIndex = enumClassPtr->GetEnumValue(static_cast(0)); + REQUIRE(fooByIndex); + CHECK(fooByIndex->GetName() == "Foo"sv); + + const auto* barByIndex = enumClassPtr->GetEnumValue(static_cast(1)); + REQUIRE(barByIndex); + CHECK(barByIndex->GetName() == "Bar"sv); + } + + THEN("We can create and use an enumeration instance") + { + auto enumInstance = enumClassPtr->CreateDefaultObject(); + REQUIRE(enumInstance); + + auto* enumPtr = dynamic_cast(enumInstance.get()); + REQUIRE(enumPtr); + + THEN("We can convert enum values to strings") + { + CHECK(enumPtr->ToString(0) == "Foo"sv); + CHECK(enumPtr->ToString(1) == "Bar"sv); + // Unknown value should return numeric string + std::string unknownStr = enumPtr->ToString(999); + CHECK((unknownStr == "999"sv)); + } + + THEN("We can convert enum names to values") + { + CHECK(enumPtr->FromString("Foo"sv) == 0); + CHECK(enumPtr->FromString("Bar"sv) == 1); + CHECK(enumPtr->FromString("Unknown"sv) == 0); + } + + THEN("We can test flag operations") + { + CHECK(enumPtr->HasFlag(0, 0) == true); // 0 & 0 == 0, true + CHECK(enumPtr->HasFlag(1, 1) == true); // 1 & 1 == 1, true + CHECK(enumPtr->HasFlag(1, 0) == true); // 1 & 0 == 0, true (flag 0 is always present) + + cct::Int64 combined = enumPtr->SetFlag(0, 1); + CHECK(combined == 1); // 0 | 1 = 1 + + combined = enumPtr->SetFlag(0, 0); + CHECK(combined == 0); // 0 | 0 = 0 + + cct::Int64 toggled = enumPtr->ToggleFlag(0, 1); + CHECK(toggled == 1); // 0 ^ 1 = 1 + + toggled = enumPtr->ToggleFlag(1, 1); + CHECK(toggled == 0); // 1 ^ 1 = 0 + } + } + } + + THEN("We can retrieve enum values from the enumeration class") + { + const auto* enumClass = dynamic_cast( + cct::refl::GetClassByName("cct::sample::SampleEnum") + ); + REQUIRE(enumClass); + + auto enumValues = enumClass->GetEnumValues(); + CHECK(enumValues.size() == 2); + + if (enumValues.size() >= 2) + { + CHECK(enumValues[0]->GetName() == "Foo"sv); + CHECK(enumValues[0]->GetValue() == 0); + + CHECK(enumValues[1]->GetName() == "Bar"sv); + CHECK(enumValues[1]->GetValue() == 1); + } + } + + THEN("We can iterate over enum values with range-based for loops") + { + const auto* enumClass = dynamic_cast( + cct::refl::GetClassByName("cct::sample::SampleEnum") + ); + REQUIRE(enumClass); + + std::size_t count = 0; + for (auto [name, value] : enumClass->Enumerate()) + { + count++; + if (count == 1) + { + CHECK(name == "Foo"sv); + CHECK(value == 0); + } + else if (count == 2) + { + CHECK(name == "Bar"sv); + CHECK(value == 1); + } + } + CHECK(count == 2); + } + } + } +} diff --git a/Src/Tests/Method.cpp b/Src/Tests/Method.cpp index 889228a..4cdb473 100644 --- a/Src/Tests/Method.cpp +++ b/Src/Tests/Method.cpp @@ -23,7 +23,7 @@ SCENARIO("Method") REQUIRE(packageLoader.AddPackage(CreateConcertoReflectionTestsPackage())); packageLoader.LoadPackages(); - CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 11); + CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 12); CHECK(cct::refl::GlobalNamespace::Get().GetNamespaceCount() == 1); THEN("We are invoking a method named 'Bar'") diff --git a/Src/Tests/Namespace.cpp b/Src/Tests/Namespace.cpp index 0dde6eb..900f8ed 100644 --- a/Src/Tests/Namespace.cpp +++ b/Src/Tests/Namespace.cpp @@ -21,7 +21,7 @@ SCENARIO("Namespace") REQUIRE(packageLoader.AddPackage(CreateConcertoReflectionTestsPackage())); packageLoader.LoadPackages(); - CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 11); + CHECK(cct::refl::GlobalNamespace::Get().GetClassCount() == 12); CHECK(cct::refl::GlobalNamespace::Get().GetNamespaceCount() == 1); THEN("We are getting the cct namespace") diff --git a/xmake.lua b/xmake.lua index f27d92f..daa8432 100644 --- a/xmake.lua +++ b/xmake.lua @@ -123,6 +123,9 @@ target("concerto-reflection") local files = { ".", "Class", + "Enumeration", + "EnumIterator", + "EnumValue", "GenericClass", "GlobalNamespace", "MemberVariable",