diff --git a/include/asenum/asenum.h b/include/asenum/asenum.h index a3948e1..21c3b1d 100644 --- a/include/asenum/asenum.h +++ b/include/asenum/asenum.h @@ -57,6 +57,9 @@ namespace asenum template class AsSwitch; + + template + class AsMap; } /** @@ -114,11 +117,27 @@ namespace asenum */ template bool ifCase(const Handler& handler) const; - + + /** + @warning Usually ou don't want to use this method. Use safer 'ifCase'. + Force unwraps AsEnum and provides direct access to value that it holds. + + @return Const reference to underlying value. + @throws std::invalid_argument exception if 'Case' doesn't correspond to stored case. + */ + template , typename = typename std::enable_if::value>::type> + const R& forceAsCase() const; + /** Performs switch-like action allowing to wotk with values of different cases. */ details::AsSwitch> doSwitch() const; + + /** + Maps (converts) AsEnum value depends on stored case to type 'T'. + */ + template + details::AsMap> doMap() const; private: AsEnum(const Enum relatedCase, std::shared_ptr value); @@ -142,16 +161,54 @@ namespace asenum namespace details { + template + constexpr size_t ArraySize(T (&array)[N]) + { + return sizeof(array) / sizeof(array[0]); + } + + template + struct AsMapResultMaker; + + template + struct AsMapResultMaker + { + template + static T + makeResult(const ConcreteAsEnum&, std::unique_ptr result) + { + if (!result) + { + throw std::logic_error("Unexpected empty result. Please contact author and attach an example."); + } + + return std::move(*result); + } + }; + + template + struct AsMapResultMaker + { + template + static AsMap + makeResult(const ConcreteAsEnum& asEnum, std::unique_ptr result) + { + return AsMap(asEnum, std::move(result)); + } + }; + + + template struct Contains; + template struct Contains { static const bool value = false; }; + template struct Contains { static const bool value = T_type1 == T_type2; }; + + template + struct Contains { static const bool value = Contains::value || Contains::value; }; + + template class AsSwitch { - template struct Contains; - template struct Contains { static const bool value = false; }; - template struct Contains { static const bool value = T_type1 == T_type2; }; - - template - struct Contains { static const bool value = Contains::value || Contains::value; }; - public: AsSwitch(const ConcreteAsEnum& asEnum, const bool handled); explicit AsSwitch(const ConcreteAsEnum& asEnum); @@ -166,7 +223,43 @@ namespace asenum const ConcreteAsEnum& m_asEnum; bool m_handled; }; - + + template + class AsMap + { + static constexpr size_t AllCaseCount = ArraySize(ConcreteAsEnum::AllCases); + static constexpr size_t CurrentCaseCount = sizeof...(Types); + static constexpr bool IsPreLastCase = AllCaseCount == CurrentCaseCount + 1; + using ResultMaker = AsMapResultMaker; + + template + using IfCaseResult = typename std::conditional>::type; + + template + using UnderlyingType = typename ConcreteAsEnum::template UnderlyingType; + + public: + AsMap(const ConcreteAsEnum& asEnum, std::unique_ptr result); + explicit AsMap(const ConcreteAsEnum& asEnum); + + template > + static typename std::enable_if::value, void>::type + ifCaseCall(const ConcreteAsEnum& asEnum, std::unique_ptr& result, const Handler& handler); + + template > + static typename std::enable_if::value, void>::type + ifCaseCall(const ConcreteAsEnum& asEnum, std::unique_ptr& result, const Handler& handler); + + template + T ifDefault(const Handler& handler); + + template > + R ifCase(const CaseHandler& handler); + + private: + std::unique_ptr m_result; + const ConcreteAsEnum& m_asEnum; + }; template struct CaseSet @@ -263,12 +356,31 @@ bool asenum::AsEnum::ifCase(const Handler& handler) const return isType; } +template +template ::Enum Case, typename R, typename> +const R& asenum::AsEnum::forceAsCase() const +{ + if (!isCase()) + { + throw std::invalid_argument("Unwrapping case does not correspond to stored case."); + } + + return *reinterpret_cast*>(m_value.get()); +} + template asenum::details::AsSwitch::Enum, asenum::AsEnum> asenum::AsEnum::doSwitch() const { return details::AsSwitch>(*this); } +template +template +asenum::details::AsMap::Enum, asenum::AsEnum> asenum::AsEnum::doMap() const +{ + return details::AsMap>(*this); +} + // AsEnum private @@ -293,7 +405,7 @@ typename std::enable_if::value, void>::type asenum::AsEnu } -// Private details +// Private details - AsSwitch template asenum::details::AsSwitch::AsSwitch(const ConcreteAsEnum& asEnum, const bool handled) @@ -310,7 +422,7 @@ template template asenum::details::AsSwitch asenum::details::AsSwitch::ifCase(const CaseHandler& handler) { - static_assert(!Contains::value, "Duplicated switch case"); + static_assert(!Contains::value, "Duplicated switch case."); if (!m_handled) { @@ -330,3 +442,57 @@ void asenum::details::AsSwitch::ifDefault(const handler(); } } + +// Private details - AsMap + +template +asenum::details::AsMap::AsMap(const ConcreteAsEnum& asEnum, std::unique_ptr result) +: m_result(std::move(result)) +, m_asEnum(asEnum) +{} + +template +asenum::details::AsMap::AsMap(const ConcreteAsEnum& asEnum) +: m_asEnum(asEnum) +{} + +template +template +typename std::enable_if::value, void>::type +asenum::details::AsMap::ifCaseCall(const ConcreteAsEnum& asEnum, std::unique_ptr& result, const Handler& handler) +{ + asEnum.template ifCase([&] { + result.reset(new T(handler())); + }); +} + +template +template +typename std::enable_if::value, void>::type +asenum::details::AsMap::ifCaseCall(const ConcreteAsEnum& asEnum, std::unique_ptr& result, const Handler& handler) +{ + asEnum.template ifCase([&] (const UT& value) { + result.reset(new T(handler(value))); + }); +} + +template +template +T asenum::details::AsMap::ifDefault(const Handler& handler) +{ + return m_result ? std::move(*m_result) : handler(); +} + +template +template +R asenum::details::AsMap::ifCase(const CaseHandler& handler) +{ + static_assert(!Contains::value, "Duplicated map case"); + + if (!m_result) + { + ifCaseCall(m_asEnum, m_result, handler); + } + + return ResultMaker::template makeResult(m_asEnum, std::move(m_result)); +}