From 45f70cd03b831e6b1b83ebea6dad0622981ff542 Mon Sep 17 00:00:00 2001 From: bergmann Date: Tue, 25 Jun 2019 01:10:25 +0200 Subject: [PATCH] * Refactored indent.h * Implemented nullable.h --- include/cppcore.h | 8 +- include/cppcore/conversion.h | 8 +- include/cppcore/misc.h | 12 +- include/cppcore/misc/indent.h | 98 +++++----- include/cppcore/misc/indent.inl | 69 +++++++ include/cppcore/misc/nullable.h | 262 ++++++++++++++++++++++++++ include/cppcore/misc/utils.h | 2 +- include/cppcore/threading.h | 2 +- test/cppcore/misc/nullable_tests.cpp | 269 +++++++++++++++++++++++++++ 9 files changed, 671 insertions(+), 59 deletions(-) create mode 100644 include/cppcore/misc/indent.inl create mode 100644 include/cppcore/misc/nullable.h create mode 100644 test/cppcore/misc/nullable_tests.cpp diff --git a/include/cppcore.h b/include/cppcore.h index 458ddfa..12bafec 100644 --- a/include/cppcore.h +++ b/include/cppcore.h @@ -1,6 +1,6 @@ #pragma once -#include -#include -#include -#include +#include "cppcore/conversion.h" +#include "cppcore/define.h" +#include "cppcore/misc.h" +#include "cppcore/threading.h" diff --git a/include/cppcore/conversion.h b/include/cppcore/conversion.h index 3c7b7b7..323e2d2 100644 --- a/include/cppcore/conversion.h +++ b/include/cppcore/conversion.h @@ -1,6 +1,6 @@ #pragma once -#include -#include -#include -#include +#include "conversion/byteorder.h" +#include "conversion/enum.h" +#include "conversion/string.h" +#include "conversion/time.h" diff --git a/include/cppcore/misc.h b/include/cppcore/misc.h index 78f87c8..d872b42 100644 --- a/include/cppcore/misc.h +++ b/include/cppcore/misc.h @@ -1,8 +1,8 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include "misc/exception.h" +#include "misc/flags.h" +#include "misc/indent.h" +#include "misc/stream.h" +#include "misc/typehelper.h" +#include "misc/utils.h" diff --git a/include/cppcore/misc/indent.h b/include/cppcore/misc/indent.h index 273497a..be25825 100644 --- a/include/cppcore/misc/indent.h +++ b/include/cppcore/misc/indent.h @@ -7,64 +7,76 @@ namespace cppcore namespace __impl { + constexpr long default_indent = 4; - inline int indent_stream_index() - { - static const int value = std::ios::xalloc(); - return value; - } + /** + * @brief Returns the index where the indention value is stored inside the stream. + */ + inline int indent_stream_index(); - inline int indent_count_stream_index() - { - static const int value = std::ios::xalloc(); - return value; - } + /** + * @brief Returns the index where the current indention level is stored inside the stream. + */ + inline int indent_level_stream_index(); + /** + * @brief Helper class to set the stream indention. + */ struct set_indent_impl { int value; }; + } - inline auto setindent(int value) - { return __impl::set_indent_impl { value }; } + /** + * @brief Set the indention of a stream. + * + * @param[in] value New indention value. + * + * @return Helper class to set the indention value. + */ + inline auto setindent(int value); - inline std::ostream& incindent(std::ostream& os) - { - ++os.iword(__impl::indent_stream_index()); - return os; - } + /** + * @brief Increment the current indention level by one. + * + * @param[in] os Stream to increment the indention level for. + * + * @return Stream passed to this function. + */ + inline std::ostream& incindent(std::ostream& os); - inline std::ostream& decindent(std::ostream& os) - { - auto& indent = os.iword(__impl::indent_stream_index()); - if (--indent < 0) - indent = 0; - return os; - } + /** + * @brief Decrement the current indention level by one. + * + * @param[in] os Stream to decrement the indention level for. + * + * @return Stream passed to this function. + */ + inline std::ostream& decindent(std::ostream& os); - inline std::ostream& indent(std::ostream& os) - { - auto i = os.iword(__impl::indent_stream_index()); - auto c = __impl::default_indent + os.iword(__impl::indent_count_stream_index()); - i *= c; - if (i >= 0) - { - os << std::endl; - while (i--) - os.put(' '); - } - return os; - } + /** + * @brief Add a line break and indention to the stream. + * + * @param[in] os Stream to decrement the indention level for. + * + * @return Stream passed to this function. + */ + inline std::ostream& indent(std::ostream& os); } namespace std { + + /** + * @brief Write the helper class to stream using the << operator. + */ template - inline basic_ostream& operator<< (basic_ostream& os, const cppcore::__impl::set_indent_impl& i) - { - using namespace ::cppcore; - os.iword(__impl::indent_count_stream_index()) = i.value - __impl::default_indent; - return os; - } + inline basic_ostream& operator<< ( + basic_ostream& os, + const cppcore::__impl::set_indent_impl& i); + } + +#include "indent.inl" diff --git a/include/cppcore/misc/indent.inl b/include/cppcore/misc/indent.inl new file mode 100644 index 0000000..0bcb859 --- /dev/null +++ b/include/cppcore/misc/indent.inl @@ -0,0 +1,69 @@ +#pragma once + +#include "indent.h" + +namespace cppcore +{ + + namespace __impl + { + + int indent_stream_index() + { + static const int value = std::ios::xalloc(); + return value; + } + + int indent_level_stream_index() + { + static const int value = std::ios::xalloc(); + return value; + } + + } + + auto setindent(int value) + { return __impl::set_indent_impl { value }; } + + std::ostream& incindent(std::ostream& os) + { + ++os.iword(__impl::indent_stream_index()); + return os; + } + + std::ostream& decindent(std::ostream& os) + { + auto& indent = os.iword(__impl::indent_stream_index()); + if (--indent < 0) + indent = 0; + return os; + } + + std::ostream& indent(std::ostream& os) + { + auto i = os.iword(__impl::indent_stream_index()); + auto c = __impl::default_indent + os.iword(__impl::indent_level_stream_index()); + i *= c; + if (i >= 0) + { + os << std::endl; + while (i--) + os.put(' '); + } + return os; + } + +} + +namespace std +{ + + template + inline basic_ostream& operator<< (basic_ostream& os, const cppcore::__impl::set_indent_impl& i) + { + using namespace ::cppcore; + os.iword(__impl::indent_level_stream_index()) = i.value - __impl::default_indent; + return os; + } + +} diff --git a/include/cppcore/misc/nullable.h b/include/cppcore/misc/nullable.h new file mode 100644 index 0000000..eb341d6 --- /dev/null +++ b/include/cppcore/misc/nullable.h @@ -0,0 +1,262 @@ +#pragma once + +#include +#include "exception.h" + +namespace cppcore +{ + + template + struct nullable + { + public: + using value_type = T_value; + + struct value_container + { + value_type value; + + template + inline value_container(T_args&&... p_args) + : value(std::forward(p_args)...) + { } + }; + + using value_container_ptr_u = std::unique_ptr; + + private: + value_container_ptr_u _container; + + public: + /** + * @brief Default constructor. + */ + inline nullable() = default; + + /** + * @brief Construtor to create the object. Parameters will passed to the constructor of the value type. + */ + template + inline nullable(T_args&&... p_args) + : _container(new value_container(std::forward(p_args)...)) + { } + + /** + * @brief Copy constructor. + */ + inline nullable(nullable& other) + : _container(other + ? new value_container(*other) + : nullptr) + { } + + /** + * @brief Copy constructor. + */ + inline nullable(const nullable& other) + : _container(other + ? new value_container(*other) + : nullptr) + { } + + /** + * @brief Move constructor. + */ + inline nullable(nullable&& other) + : _container(std::move(other)._container) + { } + + public: + /** + * @brief Assignment constructor. + */ + inline decltype(auto) operator=(value_type&& t) + { + _container.reset(new value_container(std::forward(t))); + return *this; + } + + /** + * @brief Copy assignment constructor. + */ + inline decltype(auto) operator=(const nullable& other) + { + _container.reset(other + ? new value_container(*other) + : nullptr); + return *this; + } + + /** + * @brief Move assignment constructor. + */ + inline decltype(auto) operator=(nullable&& other) + { + _container = std::move(other._container); + return *this; + } + + public: + /** + * @brief Get the stored value. + */ + inline value_type& value() + { check(); return _container->value; } + + /** + * @brief Get the stored value. + */ + inline const value_type& value() const + { check(); return _container->value; } + + /** + * @brief Check if the object has an value assigned. + */ + inline bool has_value() const + { return static_cast(_container); } + + /** + * @brief Reset the object. THis will release the stored value. + */ + inline void reset() + { _container.reset(); } + + public: + /** + * @brief Operator to access the stored value. + */ + inline decltype(auto) operator*() + { return value(); } + + /** + * @brief Operator to access the stored value. + */ + inline decltype(auto) operator*() const + { return value(); } + + /** + * @brief Operator to access the stored value. + */ + inline decltype(auto) operator->() + { return &value(); } + + /** + * @brief Operator to access the stored value. + */ + inline decltype(auto) operator->() const + { return &value(); } + + /** + * @brief Operator to access the stored value. + */ + inline decltype(auto) operator()() + { return value(); } + + /** + * @brief Operator to access the soted value. + */ + inline decltype(auto) operator()() const + { return value(); } + + /** + * @brief Convert to boolean. + */ + inline explicit operator bool() const + { return has_value(); } + + private: + /** + * @brief Check if the object has an value assigned. If not raise an exception. + */ + inline void check() const + { + if (!has_value()) + throw invalid_operation_exception("nullable does not have a value"); + } + }; + + /* operator== */ + + template + inline bool operator==(const T_nullable& lhs, const typename T_nullable::value_type& rhs) + { return lhs.has_value() && *lhs == rhs; } + + template + inline bool operator==(const typename T_nullable::value_type& lhs, const T_nullable& rhs) + { return rhs.has_value() && lhs == *rhs; } + + template + inline bool operator==(const T_nullable& lhs, const T_nullable& rhs) + { return lhs.has_value() && rhs.has_value() && *lhs == *rhs; } + + /* operator!= */ + + template + inline bool operator!=(const T_nullable& lhs, const typename T_nullable::value_type& rhs) + { return lhs.has_value() && *lhs != rhs; } + + template + inline bool operator!=(const typename T_nullable::value_type& lhs, const T_nullable& rhs) + { return rhs.has_value() && lhs != *rhs; } + + template + inline bool operator!=(const T_nullable& lhs, const T_nullable& rhs) + { return lhs.has_value() && rhs.has_value() && *lhs != *rhs; } + + /* operator< */ + + template + inline bool operator<(const T_nullable& lhs, const typename T_nullable::value_type& rhs) + { return lhs.has_value() && *lhs < rhs; } + + template + inline bool operator<(const typename T_nullable::value_type& lhs, const T_nullable& rhs) + { return rhs.has_value() && lhs < *rhs; } + + template + inline bool operator<(const T_nullable& lhs, const T_nullable& rhs) + { return lhs.has_value() && rhs.has_value() && *lhs < *rhs; } + + /* operator> */ + + template + inline bool operator>(const T_nullable& lhs, const typename T_nullable::value_type& rhs) + { return lhs.has_value() && *lhs > rhs; } + + template + inline bool operator>(const typename T_nullable::value_type& lhs, const T_nullable& rhs) + { return rhs.has_value() && lhs > *rhs; } + + template + inline bool operator>(const T_nullable& lhs, const T_nullable& rhs) + { return lhs.has_value() && rhs.has_value() && *lhs > *rhs; } + + /* operator<= */ + + template + inline bool operator<=(const T_nullable& lhs, const typename T_nullable::value_type& rhs) + { return lhs.has_value() && *lhs <= rhs; } + + template + inline bool operator<=(const typename T_nullable::value_type& lhs, const T_nullable& rhs) + { return rhs.has_value() && lhs <= *rhs; } + + template + inline bool operator<=(const T_nullable& lhs, const T_nullable& rhs) + { return lhs.has_value() && rhs.has_value() && *lhs <= *rhs; } + + /* operator>= */ + + template + inline bool operator>=(const T_nullable& lhs, const typename T_nullable::value_type& rhs) + { return lhs.has_value() && *lhs >= rhs; } + + template + inline bool operator>=(const typename T_nullable::value_type& lhs, const T_nullable& rhs) + { return rhs.has_value() && lhs >= *rhs; } + + template + inline bool operator>=(const T_nullable& lhs, const T_nullable& rhs) + { return lhs.has_value() && rhs.has_value() && *lhs >= *rhs; } + +} diff --git a/include/cppcore/misc/utils.h b/include/cppcore/misc/utils.h index 0c5df36..0928fbd 100644 --- a/include/cppcore/misc/utils.h +++ b/include/cppcore/misc/utils.h @@ -9,7 +9,7 @@ namespace cppcore inline size_t bit_count(uint32_t u); /** - * @breif Try to dynamic cast the passed value to the given type. + * @brief Try to dynamic cast the passed value to the given type. * * @tparam T Type to cast. * @tparam S Type to cast to. diff --git a/include/cppcore/threading.h b/include/cppcore/threading.h index 1bf9ef9..8c29883 100644 --- a/include/cppcore/threading.h +++ b/include/cppcore/threading.h @@ -1,3 +1,3 @@ #pragma once -#include +#include "threading/cancellation_token.h" diff --git a/test/cppcore/misc/nullable_tests.cpp b/test/cppcore/misc/nullable_tests.cpp new file mode 100644 index 0000000..dc32e7b --- /dev/null +++ b/test/cppcore/misc/nullable_tests.cpp @@ -0,0 +1,269 @@ +#include +#include + +struct TestData +{ + static int ctorCount; + static int dtorCount; + + TestData() + { ++ctorCount; } + + ~TestData() + { ++dtorCount; } +}; + +struct NonCopyableTestData +{ + int value; + + NonCopyableTestData(int v) : + value(v) + { } + + NonCopyableTestData(NonCopyableTestData&& other) : + value(0) + { std::swap(value, other.value); } + + NonCopyableTestData(const NonCopyableTestData&) = delete; +}; + +using NullableInt = ::cppcore::nullable; +using NullableIntRef = ::cppcore::nullable; +using NullableString = ::cppcore::nullable; +using NullableTestData = ::cppcore::nullable; +using NullableNonCopyableTestData = ::cppcore::nullable; + +int TestData::ctorCount = 0; +int TestData::dtorCount = 0; + +using namespace ::cppcore; +using namespace ::testing; + +TEST(nullable_tests, ctor_empty) +{ + NullableInt n1; + NullableIntRef n2; + EXPECT_FALSE(static_cast(n1)); + EXPECT_FALSE(static_cast(n2)); +} + +TEST(nullable_tests, ctor_value) +{ + int i = 5; + NullableInt n1(i); + NullableIntRef n2(i); + EXPECT_TRUE(static_cast(n1)); + EXPECT_TRUE(static_cast(n2)); + EXPECT_EQ (5, n1()); + EXPECT_EQ (&i, &n2()); +} + +TEST(nullable_tests, ctor_copy) +{ + int i = 5; + NullableInt i1(i); + NullableIntRef i2(i); + NullableInt n1(i1); + NullableIntRef n2(i2); + EXPECT_TRUE(static_cast(n1)); + EXPECT_TRUE(static_cast(n2)); + EXPECT_EQ (5, n1()); + EXPECT_EQ (&i, &n2()); +} + +TEST(nullable_tests, ctor_move) +{ + NullableString i1("test"); + NullableString n1(std::move(i1)); + EXPECT_FALSE(static_cast(i1)); + ASSERT_TRUE (static_cast(n1)); + EXPECT_EQ (std::string("test"), n1()); +} + +TEST(nullable_tests, move_assignment) +{ + NullableString i1("test"); + NullableString n1; + n1 = std::move(i1); + EXPECT_FALSE(static_cast(i1)); + ASSERT_TRUE (static_cast(n1)); + EXPECT_EQ (std::string("test"), n1()); +} + +TEST(nullable_tests, movable_object) +{ + NonCopyableTestData data(5); + NullableNonCopyableTestData tmp; + tmp = std::move(data); + ASSERT_TRUE ( tmp.has_value() ); + ASSERT_EQ ( 5, tmp.value().value ); + ASSERT_EQ ( 0, data.value ); +} + +TEST(nullable_tests, hasValue_operatorBool) +{ + EXPECT_FALSE(static_cast(NullableInt())); + EXPECT_FALSE(NullableInt().has_value()); + EXPECT_TRUE (static_cast(NullableInt(5))); + EXPECT_TRUE (NullableInt(5).has_value()); +} + +TEST(nullable_tests, reset) +{ + NullableTestData n(TestData{}); + EXPECT_TRUE (n.has_value()); + int tmp = TestData::dtorCount; + n.reset(); + EXPECT_FALSE(n.has_value()); + EXPECT_EQ (tmp + 1, TestData::dtorCount); +} + +TEST(nullable_tests, value_functor) +{ + NullableInt n1(5); + NullableInt n2; + + EXPECT_EQ (5, n1()); + EXPECT_EQ (5, n1.value()); + EXPECT_ANY_THROW(n2()); + EXPECT_ANY_THROW(n2.value()); +} + +TEST(nullable_tests, equalityCompareOperator) +{ + NullableInt n1(5); + NullableInt n2(7); + NullableInt n3; + + EXPECT_FALSE(n2 == n1); + EXPECT_TRUE ( 5 == n1); + EXPECT_FALSE(n3 == n1); + + EXPECT_FALSE(n1 == n2); + EXPECT_FALSE( 5 == n2); + EXPECT_FALSE(n3 == n2); + + EXPECT_TRUE (n1 == 5); + EXPECT_FALSE(n2 == 5); + EXPECT_FALSE(n3 == 5); + + EXPECT_FALSE(n1 == n3); + EXPECT_FALSE(n2 == n3); + EXPECT_FALSE( 5 == n3); +} + +TEST(nullable_tests, unequalityCompareOperator) +{ + NullableInt n1(5); + NullableInt n2(7); + NullableInt n3; + + EXPECT_TRUE (n2 != n1); + EXPECT_FALSE( 5 != n1); + EXPECT_FALSE(n3 != n1); + + EXPECT_TRUE (n1 != n2); + EXPECT_TRUE ( 5 != n2); + EXPECT_FALSE(n3 != n2); + + EXPECT_FALSE(n1 != 5); + EXPECT_TRUE (n2 != 5); + EXPECT_FALSE(n3 != 5); + + EXPECT_FALSE(n1 != n3); + EXPECT_FALSE(n2 != n3); + EXPECT_FALSE( 5 != n3); +} + +TEST(nullable_tests, lessCompareOperator) +{ + NullableInt n1(5); + NullableInt n2(7); + NullableInt n3; + + EXPECT_FALSE(n2 < n1); + EXPECT_FALSE( 5 < n1); + EXPECT_FALSE(n3 < n1); + + EXPECT_TRUE (n1 < n2); + EXPECT_TRUE ( 5 < n2); + EXPECT_FALSE(n3 < n2); + + EXPECT_FALSE(n1 < 5); + EXPECT_FALSE(n2 < 5); + EXPECT_FALSE(n3 < 5); + + EXPECT_FALSE(n1 < n3); + EXPECT_FALSE(n2 < n3); + EXPECT_FALSE( 5 < n3); +} + +TEST(nullable_tests, lessEqualCompareOperator) +{ + NullableInt n1(5); + NullableInt n2(7); + NullableInt n3; + + EXPECT_FALSE(n2 <= n1); + EXPECT_TRUE ( 5 <= n1); + EXPECT_FALSE(n3 <= n1); + + EXPECT_TRUE (n1 <= n2); + EXPECT_TRUE ( 5 <= n2); + EXPECT_FALSE(n3 <= n2); + + EXPECT_TRUE (n1 <= 5); + EXPECT_FALSE(n2 <= 5); + EXPECT_FALSE(n3 <= 5); + + EXPECT_FALSE(n1 <= n3); + EXPECT_FALSE(n2 <= n3); + EXPECT_FALSE( 5 <= n3); +} + +TEST(nullable_tests, greaterCompareOperator) +{ + NullableInt n1(5); + NullableInt n2(7); + NullableInt n3; + + EXPECT_TRUE (n2 > n1); + EXPECT_FALSE( 5 > n1); + EXPECT_FALSE(n3 > n1); + + EXPECT_FALSE(n1 > n2); + EXPECT_FALSE( 5 > n2); + EXPECT_FALSE(n3 > n2); + + EXPECT_FALSE(n1 > 5); + EXPECT_TRUE (n2 > 5); + EXPECT_FALSE(n3 > 5); + + EXPECT_FALSE(n1 > n3); + EXPECT_FALSE(n2 > n3); + EXPECT_FALSE( 5 > n3); +} + +TEST(nullable_tests, greaterEqualCompareOperator) +{ + NullableInt n1(5); + NullableInt n2(7); + NullableInt n3; + + EXPECT_TRUE (n2 >= n1); + EXPECT_TRUE ( 5 >= n1); + EXPECT_FALSE(n3 >= n1); + + EXPECT_FALSE(n1 >= n2); + EXPECT_FALSE( 5 >= n2); + EXPECT_FALSE(n3 >= n2); + + EXPECT_TRUE (n1 >= 5); + EXPECT_TRUE (n2 >= 5); + EXPECT_FALSE(n3 >= 5); + + EXPECT_FALSE(n1 >= n3); + EXPECT_FALSE(n2 >= n3); + EXPECT_FALSE( 5 >= n3); +}