cedric pushed a commit to branch master. http://git.enlightenment.org/core/efl.git/commit/?id=64c6c63725d96f03baf34b660ca71e13b29078c1
commit 64c6c63725d96f03baf34b660ca71e13b29078c1 Author: Felipe Magno de Almeida <[email protected]> Date: Sat May 3 00:52:47 2014 +0200 eina-cxx: add eina_integer_sequence, eina_optional and their tests. Summary: eina::optional mimics C++14 std::optional behavior and semantics. Reviewers: felipealmeida, cedric, smohanty, woohyun, raster CC: cedric Differential Revision: https://phab.enlightenment.org/D815 Signed-off-by: Cedric Bail <[email protected]> --- src/Makefile_Eina_Cxx.am | 7 +- src/bindings/eina_cxx/Eina.hh | 2 + src/bindings/eina_cxx/eina_integer_sequence.hh | 41 +++++ src/bindings/eina_cxx/eina_optional.hh | 199 +++++++++++++++++++++++++ src/tests/eina_cxx/eina_cxx_suite.cc | 2 + src/tests/eina_cxx/eina_cxx_test_optional.cc | 142 ++++++++++++++++++ 6 files changed, 391 insertions(+), 2 deletions(-) diff --git a/src/Makefile_Eina_Cxx.am b/src/Makefile_Eina_Cxx.am index d82493a..1e32656 100644 --- a/src/Makefile_Eina_Cxx.am +++ b/src/Makefile_Eina_Cxx.am @@ -1,20 +1,22 @@ ### Library -installed_einacxxmainheadersdir = $(includedir)/eina_cxx-@VMAJ@ +installed_einacxxmainheadersdir = $(includedir)/eina-cxx-@VMAJ@ dist_installed_einacxxmainheaders_DATA = \ bindings/eina_cxx/Eina.hh -installed_einacxxheadersdir = $(includedir)/eina_cxx-@VMAJ@/eina_cxx +installed_einacxxheadersdir = $(includedir)/eina-cxx-@VMAJ@/eina-cxx dist_installed_einacxxheaders_DATA = \ bindings/eina_cxx/eina_accessor.hh \ bindings/eina_cxx/eina_clone_allocators.hh \ bindings/eina_cxx/eina_error.hh \ bindings/eina_cxx/eina_inarray.hh \ bindings/eina_cxx/eina_inlist.hh \ +bindings/eina_cxx/eina_integer_sequence.hh \ bindings/eina_cxx/eina_iterator.hh \ bindings/eina_cxx/eina_lists_auxiliary.hh \ bindings/eina_cxx/eina_log.hh \ +bindings/eina_cxx/eina_optional.hh \ bindings/eina_cxx/eina_ptrarray.hh \ bindings/eina_cxx/eina_ptrlist.hh \ bindings/eina_cxx/eina_range_types.hh \ @@ -44,6 +46,7 @@ tests/eina_cxx/eina_cxx_test_stringshare.cc \ tests/eina_cxx/eina_cxx_test_error.cc \ tests/eina_cxx/eina_cxx_test_accessor.cc \ tests/eina_cxx/eina_cxx_test_thread.cc \ +tests/eina_cxx/eina_cxx_test_optional.cc \ tests/eina_cxx/eina_cxx_test_value.cc tests_eina_cxx_eina_cxx_suite_CXXFLAGS = -I$(top_builddir)/src/lib/efl \ diff --git a/src/bindings/eina_cxx/Eina.hh b/src/bindings/eina_cxx/Eina.hh index 37741b6..9ba0ca3 100644 --- a/src/bindings/eina_cxx/Eina.hh +++ b/src/bindings/eina_cxx/Eina.hh @@ -13,6 +13,8 @@ #include <eina_value.hh> #include <eina_ref.hh> #include <eina_log.hh> +#include <eina_optional.hh> +#include <eina_integer_sequence.hh> namespace efl { namespace eina { diff --git a/src/bindings/eina_cxx/eina_integer_sequence.hh b/src/bindings/eina_cxx/eina_integer_sequence.hh new file mode 100644 index 0000000..4ec1762 --- /dev/null +++ b/src/bindings/eina_cxx/eina_integer_sequence.hh @@ -0,0 +1,41 @@ +#ifndef EINA_CXX_EINA_INTEGER_SEQUENCE_HH +#define EINA_CXX_EINA_INTEGER_SEQUENCE_HH + +namespace efl { namespace eina { + +template <typename T, T... Ints> +struct integer_sequence +{ + typedef T value_type; + static constexpr std::size_t size() { return sizeof...(Ints); } + typedef integer_sequence<T, Ints...> type; +}; + +template<class S1, class S2> struct concat; + +template<typename T, T... I1, T... I2> +struct concat<integer_sequence<T, I1...>, integer_sequence<T, I2...> > + : integer_sequence<T, I1..., (sizeof...(I1)+I2)...> {}; + +template<class S1, class S2> +using Concat = typename concat<S1, S2>::type; + +template<typename T, T N> struct gen_seq; +template<typename T, T N> using make_integer_sequence = typename gen_seq<T, N>::type; + +template<typename T, T N> +struct gen_seq : Concat<make_integer_sequence<T, N/2> + , make_integer_sequence<T, N - N/2>>{}; + +template<> struct gen_seq<std::size_t, 0> : integer_sequence<std::size_t>{}; +template<> struct gen_seq<std::size_t, 1> : integer_sequence<std::size_t, 0>{}; + +template <std::size_t... I> +using index_sequence = integer_sequence<std::size_t, I...>; + +template <std::size_t I> +using make_index_sequence = make_integer_sequence<std::size_t, I>; + +} } + +#endif diff --git a/src/bindings/eina_cxx/eina_optional.hh b/src/bindings/eina_cxx/eina_optional.hh new file mode 100644 index 0000000..e6d853f --- /dev/null +++ b/src/bindings/eina_cxx/eina_optional.hh @@ -0,0 +1,199 @@ +#ifndef EINA_OPTIONAL_HH_ +#define EINA_OPTIONAL_HH_ + +#include <cstddef> +#include <algorithm> +#include <utility> + +namespace efl_eina_swap_adl { + +template <typename T> +void swap_impl(T& lhs, T& rhs) +{ + using namespace std; + swap(lhs, rhs); +} + +} + +namespace efl { namespace eina { + +template <typename T> +void adl_swap(T& lhs, T& rhs) +{ + ::efl_eina_swap_adl::swap_impl<T>(lhs, rhs); +} + +template <typename T> +struct optional +{ + typedef optional<T> _self_type; + + optional(std::nullptr_t) : engaged(false) + {} + optional() : engaged(false) + {} + optional(T&& other) : engaged(false) + { + _construct(std::move(other)); + } + optional(T const& other) : engaged(false) + { + _construct(std::move(other)); + } + optional(optional<T> const& other) + : engaged(false) + { + if(other.engaged) _construct(*other); + } + optional(optional<T>&& other) + : engaged(false) + { + _construct(std::move(*other)); + other._destroy(); + } + + _self_type& operator=(optional<T>&& other) + { + _destroy(); + engaged = other.engaged; + if(engaged) + _construct(std::move(*other)); + other._destroy(); + return *this; + } + _self_type& operator=(optional<T>const& other) + { + optional<T> tmp(other); + tmp.swap(*this); + return *this; + } + + ~optional() + { + _destroy(); + } + + explicit operator bool() const + { + return is_engaged(); + } + bool operator!() const + { + bool b ( *this ); + return !b; + } + + T* operator->() + { + assert(is_engaged()); + return static_cast<T*>(static_cast<void*>(&buffer)); + } + T const* operator->() const + { + return const_cast<_self_type&>(*this).operator->(); + } + + T& operator*() { return get(); } + T const& operator*() const { return get(); } + + T& get() { return *this->operator->(); } + T const& get() const { return *this->operator->(); } + + void swap(optional<T>& other) + { + if(is_engaged() && other.is_engaged()) + { + eina::adl_swap(**this, *other); + } + else if(is_engaged()) + { + other._construct(std::move(**this)); + _destroy(); + } + else if(other.is_engaged()) + { + _construct(std::move(*other)); + other._destroy(); + } + } + + bool is_engaged() const + { + return engaged; + } +private: + template <typename U> + void _construct(U&& object) + { + assert(!is_engaged()); + new (&buffer) T(std::move(object)); + engaged = true; + } + void _destroy() + { + if(is_engaged()) + { + static_cast<T*>(static_cast<void*>(&buffer))->~T(); + engaged = false; + } + } + + typedef typename std::aligned_storage + <sizeof(T),std::alignment_of<T>::value>::type buffer_type; + buffer_type buffer; + bool engaged; +}; + +template <typename T> +void swap(optional<T>& lhs, optional<T>& rhs) +{ + lhs.swap(rhs); +} + +template <typename T> +bool operator==(optional<T> const& lhs, optional<T> const& rhs) +{ + if(!lhs && !rhs) + return true; + else if(!lhs || !rhs) + return false; + else + return *lhs == *rhs; +} +template <typename T> +bool operator!=(optional<T> const& lhs, optional<T> const& rhs) +{ + return !(lhs == rhs); +} +template <typename T> +bool operator<(optional<T> const& lhs, optional<T> const& rhs) +{ + if(!lhs && !rhs) + return false; + else if(!lhs) + return true; + else if(!rhs) + return false; + else + return *lhs < *rhs; +} +template <typename T> +bool operator<=(optional<T> const& lhs, optional<T> const& rhs) +{ + return lhs < rhs || lhs == rhs; +} +template <typename T> +bool operator>(optional<T> const& lhs, optional<T> const& rhs) +{ + return !(lhs <= rhs); +} +template <typename T> +bool operator>=(optional<T> const& lhs, optional<T> const& rhs) +{ + return !(lhs < rhs); +} + +} } // efl::eina + +#endif // EINA_OPTIONAL_HH_ diff --git a/src/tests/eina_cxx/eina_cxx_suite.cc b/src/tests/eina_cxx/eina_cxx_suite.cc index 4ef8a9f..942d539 100644 --- a/src/tests/eina_cxx/eina_cxx_suite.cc +++ b/src/tests/eina_cxx/eina_cxx_suite.cc @@ -15,6 +15,7 @@ void eina_test_stringshare(TCase* tc); void eina_test_error(TCase* tc); void eina_test_accessor(TCase* tc); void eina_test_thread(TCase* tc); +void eina_test_optional(TCase* tc); void eina_test_value(TCase* tc); void eina_test_log(TCase* tc); @@ -35,6 +36,7 @@ static const Eina_Test_Case etc[] = { { "Error", eina_test_error }, { "Accessor", eina_test_accessor }, { "Thread", eina_test_thread }, + { "Optional", eina_test_optional }, { "Value", eina_test_value }, { "Log", eina_test_log }, { NULL, NULL } diff --git a/src/tests/eina_cxx/eina_cxx_test_optional.cc b/src/tests/eina_cxx/eina_cxx_test_optional.cc new file mode 100644 index 0000000..a809f66 --- /dev/null +++ b/src/tests/eina_cxx/eina_cxx_test_optional.cc @@ -0,0 +1,142 @@ + +#include "Eina.hh" + +#include <algorithm> + +#include <check.h> + +#include <iostream> + +std::size_t nonpod_constructed = 0u + , nonpod_destructed = 0u; + +struct nonpod +{ + nonpod() { nonpod_constructed++; } + nonpod(nonpod const&) { nonpod_constructed++; } + nonpod(nonpod&&) { nonpod_constructed++; } + ~nonpod() { nonpod_destructed++; } +}; + +START_TEST(eina_cxx_optional_constructors) +{ + namespace eina = efl::eina; + + eina::eina_init init; + + { + eina::optional<int> optional; + ck_assert(!optional); + } + + { + eina::optional<int> optional(nullptr); + ck_assert(!optional); + } + + { + eina::optional<int> optional(5); + ck_assert(!!optional); + ck_assert(*optional == 5); + } + + { + eina::optional<nonpod> optional; + ck_assert(!optional); + ck_assert(::nonpod_constructed == 0u); + } + + { + ::nonpod object; + eina::optional<nonpod> optional(object); + ck_assert(!!optional); + } + std::cout << "nonpod_constructed " << nonpod_constructed + << " nonpod_destructed " << nonpod_destructed << std::endl; + ck_assert(::nonpod_constructed == ::nonpod_destructed); +} +END_TEST + +START_TEST(eina_cxx_optional_rel_ops) +{ + namespace eina = efl::eina; + + eina::eina_init init; + + eina::optional<int> empty; + eina::optional<int> one(1); + eina::optional<int> two(2); + eina::optional<int> one_again(1); + + ck_assert(empty == empty); + ck_assert(one == one); + ck_assert(one == one_again); + ck_assert(one <= one_again); + ck_assert(one >= one_again); + ck_assert(empty < one); + ck_assert(one >= empty); + ck_assert(one > empty); + ck_assert(one < two); + ck_assert(one <= two); + ck_assert(two > one); + ck_assert(two >= one); + ck_assert(!(empty < empty)); + ck_assert(!(one < one_again)); + ck_assert(empty != one); + ck_assert(!(one != one)); + ck_assert(!(one != one_again)); +} +END_TEST + +START_TEST(eina_cxx_optional_assignment) +{ + namespace eina = efl::eina; + + eina::eina_init init; + + eina::optional<int> a; + eina::optional<int> b(1); + eina::optional<int> c(2); + eina::optional<int> d(1); + + assert(!a); assert(b); assert(c); assert(d); + + a = a; + ck_assert(a == a); + ck_assert(!a); + + assert(!a); assert(b); assert(c); assert(d); + + b = a; + ck_assert(b == a); + ck_assert(b != d); + ck_assert(!b); + + assert(!a); assert(!b); assert(c); assert(d); + + a = d; + ck_assert(a == d); + ck_assert(a != b); + ck_assert(!!a); + ck_assert(*a == 1); + + assert(a); assert(!b); assert(c); assert(d); + + c = d; + + ck_assert(c == d); + ck_assert(c != b); + ck_assert(!!c); + ck_assert(*c == 1); + + assert(a); assert(!b); assert(c); assert(d); +} +END_TEST + +void +eina_test_optional(TCase* tc) +{ + tcase_add_test(tc, eina_cxx_optional_constructors); + tcase_add_test(tc, eina_cxx_optional_rel_ops); + tcase_add_test(tc, eina_cxx_optional_assignment); +} --
