diff --git a/include/asenum/asenum.h b/include/asenum/asenum.h index 5e3f2d2..175fb82 100644 --- a/include/asenum/asenum.h +++ b/include/asenum/asenum.h @@ -82,8 +82,8 @@ namespace asenum @param value Value related to specified enum case. @return AsEnum instance holding value of specified case. */ - template , void>::value>::type> - static AsEnum create(T value); + template , void>::value>::type> + static AsEnum create(UnderlyingType value) { return createImpl>(std::move(value)); } /** Creates AsEnum instance of specific case with 'void' associated type. @@ -103,7 +103,7 @@ namespace asenum template bool isCase() const; -#error add example +//#error add example /** Unwraps AsEnum and provides access to value that it holds. @@ -114,7 +114,7 @@ namespace asenum template bool ifCase(const Handler& handler) const; -#error add example +//#error add example /** Performs switch-like action allowing to wotk with values of different cases. @@ -122,10 +122,10 @@ namespace asenum details::AsSwitch> doSwitch() const; private: - template ::type> - AsEnum(const Enum relatedCase, T&& value); + AsEnum(const Enum relatedCase, std::shared_ptr value); - explicit AsEnum(const Enum relatedCase); + template + static AsEnum createImpl(T&& value); template static typename std::enable_if::value, void>::type call(const void*, const Handler& handler); @@ -219,16 +219,23 @@ constexpr typename asenum::AsEnum::Enum asenum::AsEnum:: template template ::Enum Case, typename T> -asenum::AsEnum asenum::AsEnum::create(T value) +asenum::AsEnum asenum::AsEnum::createImpl(T&& value) { - return asenum::AsEnum(Case, value); + std::shared_ptr internalValue(new T(std::forward(value)), [] (void* ptr) { + if (ptr) + { + delete reinterpret_cast(ptr); + } + }); + + return asenum::AsEnum(Case, internalValue); } template template ::Enum Case, typename T> asenum::AsEnum asenum::AsEnum::create() { - return asenum::AsEnum(Case); + return asenum::AsEnum(Case, nullptr); } template @@ -267,21 +274,9 @@ asenum::details::AsSwitch::Enum, asenum::AsE // AsEnum private template -template -asenum::AsEnum::AsEnum(const Enum relatedCase, T&& value) -: m_enumCase(relatedCase) -, m_value(new RealT(std::forward(value)), [] (void* ptr) { - if (ptr) - { - delete reinterpret_cast(ptr); - } -}) -{} - -template -asenum::AsEnum::AsEnum(const Enum relatedCase) +asenum::AsEnum::AsEnum(const Enum relatedCase, std::shared_ptr value) : m_enumCase(relatedCase) -, m_value(nullptr) +, m_value(value) {} template diff --git a/tests/AsEnumTest.cpp b/tests/AsEnumTest.cpp index de73438..d4015fe 100644 --- a/tests/AsEnumTest.cpp +++ b/tests/AsEnumTest.cpp @@ -24,7 +24,7 @@ #include -#include +#include #include @@ -36,129 +36,131 @@ namespace { Unknown, StringOpt, - BoolOpt + VoidOpt }; - ASENUM_DECLARE(TestAsEnum, TestEnum) - { - ASENUM_DEFINE_STRUCTORS(); - - ASENUM_CASE(Unknown, int); - ASENUM_CASE(StringOpt, std::string); - ASENUM_CASE(BoolOpt, bool); - }; + using TestAsEnum = asenum::AsEnum< + asenum::Case11, + asenum::Case11, + asenum::Case11 + >; static_assert(std::is_same, int>::value, "Invalid underlying type"); static_assert(std::is_same, std::string>::value, "Invalid underlying type"); - static_assert(std::is_same, bool>::value, "Invalid underlying type"); -} - -TEST(AsEnum, NamedGetter) -{ - const TestAsEnum value1 = TestAsEnum::createStringOpt("test"); - const TestAsEnum value2 = TestAsEnum::createBoolOpt(true); - const TestAsEnum value3 = TestAsEnum::createUnknown(-100500); - - EXPECT_EQ(value1.type(), TestEnum::StringOpt); - EXPECT_EQ(value2.type(), TestEnum::BoolOpt); - EXPECT_EQ(value3.type(), TestEnum::Unknown); - - EXPECT_EQ(value1.asStringOpt(), "test"); - EXPECT_THROW(value1.asUnknown(), std::exception); - EXPECT_THROW(value1.asBoolOpt(), std::exception); - - EXPECT_EQ(value2.asBoolOpt(), true); - EXPECT_THROW(value2.asUnknown(), std::exception); - EXPECT_THROW(value2.asStringOpt(), std::exception); - - EXPECT_EQ(value3.asUnknown(), -100500); - EXPECT_THROW(value3.asStringOpt(), std::exception); - EXPECT_THROW(value3.asBoolOpt(), std::exception); - + static_assert(std::is_same, void>::value, "Invalid underlying type"); + + static_assert(GTEST_ARRAY_SIZE_(TestAsEnum::AllCases) == 3, "Invalid number of cases"); + static_assert(TestAsEnum::AllCases[0] == TestEnum::Unknown, "Invalid enum case"); + static_assert(TestAsEnum::AllCases[1] == TestEnum::StringOpt, "Invalid enum case"); + static_assert(TestAsEnum::AllCases[2] == TestEnum::VoidOpt, "Invalid enum case"); } -TEST(AsEnum, EnumGetter) +TEST(AsEnum, IfCase) { const TestAsEnum value1 = TestAsEnum::create("test"); - const TestAsEnum value2 = TestAsEnum::create(true); + const TestAsEnum value2 = TestAsEnum::create(); const TestAsEnum value3 = TestAsEnum::create(-100500); - EXPECT_EQ(value1.type(), TestEnum::StringOpt); - EXPECT_EQ(value2.type(), TestEnum::BoolOpt); - EXPECT_EQ(value3.type(), TestEnum::Unknown); + MockFunction handler1; + MockFunction handler2; + MockFunction handler3; - EXPECT_EQ(value1.as(), "test"); - EXPECT_THROW(value1.as(), std::exception); - EXPECT_THROW(value1.as(), std::exception); + EXPECT_CALL(handler1, Call("test")) + .WillOnce(Return()); + EXPECT_CALL(handler2, Call()) + .WillOnce(Return()); + EXPECT_CALL(handler3, Call(-100500)) + .WillOnce(Return()); - EXPECT_EQ(value2.as(), true); - EXPECT_THROW(value2.as(), std::exception); - EXPECT_THROW(value2.as(), std::exception); + EXPECT_TRUE(value1.ifCase(handler1.AsStdFunction())); + EXPECT_FALSE(value1.ifCase(handler2.AsStdFunction())); + EXPECT_FALSE(value1.ifCase(handler3.AsStdFunction())); - EXPECT_EQ(value3.as(), -100500); - EXPECT_THROW(value3.as(), std::exception); - EXPECT_THROW(value3.as(), std::exception); + EXPECT_FALSE(value2.ifCase(handler1.AsStdFunction())); + EXPECT_TRUE(value2.ifCase(handler2.AsStdFunction())); + EXPECT_FALSE(value2.ifCase(handler3.AsStdFunction())); + EXPECT_FALSE(value3.ifCase(handler1.AsStdFunction())); + EXPECT_FALSE(value3.ifCase(handler2.AsStdFunction())); + EXPECT_TRUE(value3.ifCase(handler3.AsStdFunction())); } -TEST(AsEnum, Is) +TEST(AsEnum, IsCase) { - const TestAsEnum value = TestAsEnum::create("test"); + const TestAsEnum value1 = TestAsEnum::create("test"); + const TestAsEnum value2 = TestAsEnum::create(); + const TestAsEnum value3 = TestAsEnum::create(-100500); + + EXPECT_EQ(value1.enumCase(), TestEnum::StringOpt); + EXPECT_EQ(value2.enumCase(), TestEnum::VoidOpt); + EXPECT_EQ(value3.enumCase(), TestEnum::Unknown); - EXPECT_TRUE(value.is()); - EXPECT_TRUE(value.isStringOpt()); + EXPECT_TRUE(value1.isCase()); + EXPECT_FALSE(value1.isCase()); + EXPECT_FALSE(value1.isCase()); - EXPECT_FALSE(value.is()); - EXPECT_FALSE(value.isUnknown()); + EXPECT_FALSE(value2.isCase()); + EXPECT_TRUE(value2.isCase()); + EXPECT_FALSE(value2.isCase()); - EXPECT_FALSE(value.is()); - EXPECT_FALSE(value.isBoolOpt()); + EXPECT_FALSE(value3.isCase()); + EXPECT_FALSE(value3.isCase()); + EXPECT_TRUE(value3.isCase()); } TEST(AsEnum, Switch_Full) { - const TestAsEnum value1 = TestAsEnum::createStringOpt("test"); + const TestAsEnum value = TestAsEnum::create("test"); - value1.doSwitch() - .asCase([] (const std::string& value) { - EXPECT_EQ(value, "test"); - }) - .asCase([] (const bool& value) { + MockFunction handler; + + EXPECT_CALL(handler, Call("test")) + .WillOnce(Return()); + + value.doSwitch() + .ifCase(handler.AsStdFunction()) + .ifCase([] { EXPECT_TRUE(false); }) - .asCase([] (const int& value) { + .ifCase([] (const int& value) { EXPECT_TRUE(false); }) - .asDefault([] () { + .ifDefault([] () { EXPECT_TRUE(false); }); } TEST(AsEnum, Switch_Partial) { - const TestAsEnum value1 = TestAsEnum::createStringOpt("test"); + const TestAsEnum value = TestAsEnum::create("test"); - value1.doSwitch() - .asCase([] (const std::string& value) { - EXPECT_EQ(value, "test"); - }) - .asCase([] (const bool& value) { + MockFunction handler; + + EXPECT_CALL(handler, Call("test")) + .WillOnce(Return()); + + value.doSwitch() + .ifCase(handler.AsStdFunction()) + .ifCase([] { EXPECT_TRUE(false); }); } TEST(AsEnum, Switch_Default) { - const TestAsEnum value1 = TestAsEnum::createStringOpt("test"); + const TestAsEnum value = TestAsEnum::create("test"); + + MockFunction handler; + + EXPECT_CALL(handler, Call()) + .WillOnce(Return()); - value1.doSwitch() - .asCase([] (const int& value) { + value.doSwitch() + .ifCase([] (const int& value) { EXPECT_TRUE(false); }) - .asCase([] (const bool& value) { + .ifCase([] { EXPECT_TRUE(false); }) - .asDefault([] () { - EXPECT_TRUE(true); - });; + .ifDefault(handler.AsStdFunction()); }