From 64c6c63725d96f03baf34b660ca71e13b29078c1 Mon Sep 17 00:00:00 2001 From: Felipe Magno de Almeida Date: Sat, 3 May 2014 00:52:47 +0200 Subject: [PATCH] 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 --- src/Makefile_Eina_Cxx.am | 7 +- src/bindings/eina_cxx/Eina.hh | 2 + .../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(-) create mode 100644 src/bindings/eina_cxx/eina_integer_sequence.hh create mode 100644 src/bindings/eina_cxx/eina_optional.hh create mode 100644 src/tests/eina_cxx/eina_cxx_test_optional.cc diff --git a/src/Makefile_Eina_Cxx.am b/src/Makefile_Eina_Cxx.am index d82493af13..1e3265612e 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 37741b642f..9ba0ca3ebc 100644 --- a/src/bindings/eina_cxx/Eina.hh +++ b/src/bindings/eina_cxx/Eina.hh @@ -13,6 +13,8 @@ #include #include #include +#include +#include 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 0000000000..4ec17621f7 --- /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 +struct integer_sequence +{ + typedef T value_type; + static constexpr std::size_t size() { return sizeof...(Ints); } + typedef integer_sequence type; +}; + +template struct concat; + +template +struct concat, integer_sequence > + : integer_sequence {}; + +template +using Concat = typename concat::type; + +template struct gen_seq; +template using make_integer_sequence = typename gen_seq::type; + +template +struct gen_seq : Concat + , make_integer_sequence>{}; + +template<> struct gen_seq : integer_sequence{}; +template<> struct gen_seq : integer_sequence{}; + +template +using index_sequence = integer_sequence; + +template +using make_index_sequence = make_integer_sequence; + +} } + +#endif diff --git a/src/bindings/eina_cxx/eina_optional.hh b/src/bindings/eina_cxx/eina_optional.hh new file mode 100644 index 0000000000..e6d853f1bb --- /dev/null +++ b/src/bindings/eina_cxx/eina_optional.hh @@ -0,0 +1,199 @@ +#ifndef EINA_OPTIONAL_HH_ +#define EINA_OPTIONAL_HH_ + +#include +#include +#include + +namespace efl_eina_swap_adl { + +template +void swap_impl(T& lhs, T& rhs) +{ + using namespace std; + swap(lhs, rhs); +} + +} + +namespace efl { namespace eina { + +template +void adl_swap(T& lhs, T& rhs) +{ + ::efl_eina_swap_adl::swap_impl(lhs, rhs); +} + +template +struct optional +{ + typedef optional _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 const& other) + : engaged(false) + { + if(other.engaged) _construct(*other); + } + optional(optional&& other) + : engaged(false) + { + _construct(std::move(*other)); + other._destroy(); + } + + _self_type& operator=(optional&& other) + { + _destroy(); + engaged = other.engaged; + if(engaged) + _construct(std::move(*other)); + other._destroy(); + return *this; + } + _self_type& operator=(optionalconst& other) + { + optional 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(static_cast(&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& 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 + void _construct(U&& object) + { + assert(!is_engaged()); + new (&buffer) T(std::move(object)); + engaged = true; + } + void _destroy() + { + if(is_engaged()) + { + static_cast(static_cast(&buffer))->~T(); + engaged = false; + } + } + + typedef typename std::aligned_storage + ::value>::type buffer_type; + buffer_type buffer; + bool engaged; +}; + +template +void swap(optional& lhs, optional& rhs) +{ + lhs.swap(rhs); +} + +template +bool operator==(optional const& lhs, optional const& rhs) +{ + if(!lhs && !rhs) + return true; + else if(!lhs || !rhs) + return false; + else + return *lhs == *rhs; +} +template +bool operator!=(optional const& lhs, optional const& rhs) +{ + return !(lhs == rhs); +} +template +bool operator<(optional const& lhs, optional const& rhs) +{ + if(!lhs && !rhs) + return false; + else if(!lhs) + return true; + else if(!rhs) + return false; + else + return *lhs < *rhs; +} +template +bool operator<=(optional const& lhs, optional const& rhs) +{ + return lhs < rhs || lhs == rhs; +} +template +bool operator>(optional const& lhs, optional const& rhs) +{ + return !(lhs <= rhs); +} +template +bool operator>=(optional const& lhs, optional 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 4ef8a9f45d..942d539512 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 0000000000..a809f6622a --- /dev/null +++ b/src/tests/eina_cxx/eina_cxx_test_optional.cc @@ -0,0 +1,142 @@ + +#include "Eina.hh" + +#include + +#include + +#include + +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 optional; + ck_assert(!optional); + } + + { + eina::optional optional(nullptr); + ck_assert(!optional); + } + + { + eina::optional optional(5); + ck_assert(!!optional); + ck_assert(*optional == 5); + } + + { + eina::optional optional; + ck_assert(!optional); + ck_assert(::nonpod_constructed == 0u); + } + + { + ::nonpod object; + eina::optional 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 empty; + eina::optional one(1); + eina::optional two(2); + eina::optional 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 a; + eina::optional b(1); + eina::optional c(2); + eina::optional 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); +}