eolian-cxx: Implement future template class for C++

This commit is contained in:
Felipe Magno de Almeida 2016-08-07 16:52:48 -03:00
parent 98229bc5d6
commit a63cfcafc7
14 changed files with 1096 additions and 185 deletions

View File

@ -13,6 +13,7 @@ bindings/cxx/eo_cxx/Eo.hh \
bindings/cxx/eo_cxx/eo_init.hh \
bindings/cxx/eo_cxx/eo_ops.hh \
bindings/cxx/eo_cxx/eo_wref.hh \
bindings/cxx/eo_cxx/eo_promise.hh \
bindings/cxx/eo_cxx/eo_private.hh
### Elementary C++
@ -120,7 +121,8 @@ CLEANFILES += $(edje_eolian_cxx_hh) $(edje_eolian_cxx_impl) lib/edje/Edje.hh
### Ecore src/lib/ecore
installed_ecorecxxheadersdir = $(includedir)/ecore-cxx-@VMAJ@
dist_installed_ecorecxxheaders_DATA = \
bindings/cxx/ecore_cxx/Ecore.hh
bindings/cxx/ecore_cxx/Ecore.hh \
bindings/cxx/ecore_cxx/Ecore_Manual.hh
nodist_installed_ecorecxxheaders_DATA = $(ecore_eolian_cxx_hh) $(ecore_eolian_cxx_impl) \
lib/ecore/Ecore.eo.hh
@ -168,6 +170,7 @@ bindings/cxx/eina_cxx/eina_tuple_unwrap.hh \
bindings/cxx/eina_cxx/eina_type_traits.hh \
bindings/cxx/eina_cxx/eina_value.hh \
bindings/cxx/eina_cxx/eina_workarounds.hh \
bindings/cxx/eina_cxx/eina_copy_traits.hh \
bindings/cxx/eina_cxx/Eina.hh
### Eio
@ -186,9 +189,9 @@ CLEANFILES += $(eio_eolian_cxx_hh) $(eio_eolian_cxx_impl) lib/eio/Eio.hh
if EFL_ENABLE_TESTS
### Tests for Eina
check_PROGRAMS += tests/eina_cxx/eina_cxx_suite
TESTS += tests/eina_cxx/eina_cxx_suite
### Tests for Eina and Eo
check_PROGRAMS += tests/eina_cxx/eina_cxx_suite tests/eo_cxx/eo_cxx_suite
TESTS += tests/eina_cxx/eina_cxx_suite tests/eo_cxx/eo_cxx_suite
tests_eina_cxx_eina_cxx_suite_SOURCES = \
tests/eina_cxx/eina_cxx_suite.cc \
@ -232,11 +235,34 @@ tests_eina_cxx_eina_cxx_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
@CHECK_CFLAGS@ \
@EO_CFLAGS@ \
@ECORE_CFLAGS@ \
@ECORE_CXX_CFLAGS@ \
@EO_CXX_CFLAGS@ \
@EINA_CXX_CFLAGS@
tests_eina_cxx_eina_cxx_suite_LDADD = @CHECK_LIBS@ @USE_EINA_LIBS@ @USE_EO_LIBS@
tests_eina_cxx_eina_cxx_suite_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@ @USE_EO_INTERNAL_LIBS@
tests_eo_cxx_eo_cxx_suite_SOURCES = \
tests/eo_cxx/eo_cxx_suite.cc \
tests/eo_cxx/eo_cxx_test_promise.cc
tests_eo_cxx_eo_cxx_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
-DTESTS_WD=\"`pwd`\" \
-DTESTS_SRC_DIR=\"$(top_srcdir)/src/tests/eo_cxx\" \
-DPACKAGE_BUILD_DIR=\"$(abs_top_builddir)/src/tests/eo_cxx\" \
-DTESTS_BUILD_DIR=\"$(top_builddir)/src/tests/eo_cxx\" \
-I$(top_builddir)/src/lib/efl \
-I$(top_builddir)/src/lib/efl/interfaces \
-I$(top_srcdir)/src/bin/eina_cxx \
-I$(top_builddir)/src/tests/eina_cxx \
@CHECK_CFLAGS@ \
@EO_CFLAGS@ \
@ECORE_CFLAGS@ \
@ECORE_CXX_CFLAGS@ \
@EO_CXX_CFLAGS@ \
@EINA_CXX_CFLAGS@
tests_eo_cxx_eo_cxx_suite_LDADD = @CHECK_LIBS@ @USE_EINA_LIBS@ @USE_EO_LIBS@ @USE_ECORE_LIBS@
tests_eo_cxx_eo_cxx_suite_DEPENDENCIES = @USE_EINA_INTERNAL_LIBS@ @USE_EO_INTERNAL_LIBS@ @USE_ECORE_INTERNAL_LIBS@
### Tests for ecore
check_PROGRAMS += tests/ecore_cxx/ecore_cxx_suite tests/ecore_cxx/cxx_compile_test

View File

@ -6,7 +6,7 @@ ecore_eolian_files_legacy = \
lib/ecore/ecore_exe.eo \
lib/ecore/efl_loop_timer.eo
ecore_eolian_files = \
ecore_eolian_files_public = \
lib/ecore/efl_loop.eo \
lib/ecore/efl_loop_user.eo \
lib/ecore/efl_loop_fd.eo \
@ -20,8 +20,11 @@ ecore_eolian_files = \
lib/ecore/efl_io_stderr.eo \
lib/ecore/efl_io_file.eo \
lib/ecore/efl_io_copier.eo \
lib/ecore/ecore_parent.eo
ecore_eolian_files = \
$(ecore_eolian_files_public) \
lib/ecore/efl_promise.eo \
lib/ecore/ecore_parent.eo \
$(ecore_eolian_files_legacy)
ecore_eolian_type_files = \
@ -300,7 +303,7 @@ endif
if HAVE_JS
generated_ecore_js_bindings = $(ecore_eolian_files:%.eo=%.eo.js.cc)
generated_ecore_js_bindings = $(ecore_eolian_files_public:%.eo=%.eo.js.cc)
GENERATED_JS_BINDINGS += $(generated_ecore_js_bindings)
@ -308,7 +311,7 @@ endif
if HAVE_CXX11
ecore_eolian_cxx_hh = $(ecore_eolian_files:%.eo=%.eo.hh)
ecore_eolian_cxx_impl = $(ecore_eolian_files:%.eo=%.eo.impl.hh)
ecore_eolian_cxx_hh = $(ecore_eolian_files_public:%.eo=%.eo.hh)
ecore_eolian_cxx_impl = $(ecore_eolian_files_public:%.eo=%.eo.impl.hh)
endif

View File

@ -57,7 +57,7 @@ bin_eolian_cxx_eolian_cxx_SOURCES = \
bin_eolian_cxx_eolian_cxx_CXXFLAGS = -I$(top_builddir)/src/lib/efl \
-I$(top_srcdir)/src/lib/eolian_cxx/ \
@CHECK_CFLAGS@ @EINA_CFLAGS@ @EINA_CXX_CFLAGS@ @EO_CFLAGS@ \
@CHECK_CFLAGS@ @EINA_CFLAGS@ @EINA_CXX_CFLAGS@ @EO_CXX_CFLAGS@ @ECORE_CXX_CFLAGS@ @EO_CFLAGS@ \
-I$(top_srcdir)/src/bindings/cxx/eina_cxx \
@EOLIAN_CXX_CFLAGS@ @EOLIAN_CFLAGS@
@ -159,7 +159,7 @@ tests_eolian_cxx_eolian_cxx_suite_CXXFLAGS = \
-DTESTS_BUILD_DIR=\"$(top_builddir)/src/tests/eolian_cxx\" \
@CHECK_CFLAGS@ @EOLIAN_CXX_CFLAGS@ @EINA_CXX_CFLAGS@ \
@EOLIAN_CFLAGS@ @EINA_CFLAGS@ @EO_CFLAGS@ @ECORE_CFLAGS@ \
@EO_CXX_CFLAGS@
@EO_CXX_CFLAGS@ @ECORE_CXX_CFLAGS@
tests_eolian_cxx_eolian_cxx_suite_CFLAGS = ${tests_eolian_cxx_eolian_cxx_suite_CXXFLAGS}
tests_eolian_cxx_eolian_cxx_suite_CPPFLAGS = ${tests_eolian_cxx_eolian_cxx_suite_CXXFLAGS}

View File

@ -13,168 +13,6 @@
#ifdef EFL_BETA_API_SUPPORT
#include <Ecore.eo.hh>
#endif
namespace efl { namespace ecore {
template <typename T>
struct _identity
{
typedef T type;
};
template <typename F>
void _ecore_main_loop_thread_safe_call_async_callback(void* data)
{
std::unique_ptr<F> f (static_cast<F*>(data));
try
{
(*f)();
}
catch(std::bad_alloc const& e)
{
eina_error_set(ENOMEM);
}
catch(std::system_error const& e)
{
efl::eina::set_error_code(e.code());
}
catch(...)
{
eina_error_set( efl::eina::unknown_error() );
}
}
template <typename T>
struct _return_buffer
{
typename std::aligned_storage<sizeof(T),std::alignment_of<T>::value>::type buffer;
};
template <>
struct _return_buffer<void>
{
};
template <typename F>
struct _data
{
F& f;
std::exception_ptr exception;
typedef typename std::result_of<F()>::type result_type;
_return_buffer<result_type> return_buffer;
};
template <typename F>
void* _ecore_main_loop_thread_safe_call_sync_callback_aux(_data<F>* d, _identity<void>)
{
d->f();
if(eina_error_get())
d->exception = make_exception_ptr(std::system_error(efl::eina::get_error_code()));
return 0;
}
template <typename F, typename R>
void* _ecore_main_loop_thread_safe_call_sync_callback_aux(_data<F>* d, _identity<R>)
{
typedef R result_type;
new (&d->return_buffer.buffer) result_type ( std::move(d->f()) );
if(eina_error_get())
{
d->exception = make_exception_ptr(std::system_error(efl::eina::get_error_code()));
eina_error_set(0);
result_type* p = static_cast<result_type*>(static_cast<void*>(&d->return_buffer.buffer));
p->~result_type();
}
return 0;
}
template <typename F>
void* _ecore_main_loop_thread_safe_call_sync_callback(void* data)
{
_data<F>* d = static_cast<_data<F>*>(data);
try
{
return _ecore_main_loop_thread_safe_call_sync_callback_aux
(d, _identity<typename std::result_of<F()>::type>());
}
catch(std::bad_alloc const& e)
{
d->exception = std::current_exception();
}
catch(std::system_error const& e)
{
d->exception = std::current_exception();
}
catch(...)
{
d->exception = std::current_exception();
}
return 0;
}
template <typename F>
void main_loop_thread_safe_call_async(F&& f)
{
::ecore_main_loop_thread_safe_call_async( &ecore::_ecore_main_loop_thread_safe_call_async_callback<F>
, new F(std::forward<F>(f)) );
}
template <typename F>
void _get_return_value(_data<F>& data, _identity<void>)
{
if(data.exception)
{
std::rethrow_exception(data.exception);
}
}
template <typename F, typename R>
R _get_return_value(_data<F>& data, _identity<R>)
{
if(!data.exception)
{
R* b_ = static_cast<R*>(static_cast<void*>(&data.return_buffer.buffer));
struct destroy
{
destroy(R* x_) : p_(x_)
{}
~destroy()
{
p_->~R();
}
R* p_;
} destroy_temp(b_);
return std::move(*b_);
}
else
{
std::rethrow_exception(data.exception);
}
}
template <typename F>
typename std::result_of<F()>::type
main_loop_thread_safe_call_sync(F&& f)
{
typedef typename std::result_of<F()>::type result_type;
_data<F> data {f, nullptr, {}};
::ecore_main_loop_thread_safe_call_sync
(&ecore::_ecore_main_loop_thread_safe_call_sync_callback<F>, &data);
return _get_return_value(data, _identity<result_type>());
}
struct ecore_init
{
ecore_init()
{
::ecore_init();
}
~ecore_init()
{
::ecore_shutdown();
}
};
} }
#include <Ecore_Manual.hh>
#endif

View File

@ -0,0 +1,176 @@
#ifndef _EFL_ECORE_CXX_ECORE_MANUAL_HH
#define _EFL_ECORE_CXX_ECORE_MANUAL_HH
#include <Ecore.h>
#include <Eina.hh>
#include <utility>
#include <type_traits>
#include <memory>
#include <cstring>
namespace efl { namespace ecore {
template <typename T>
struct _identity
{
typedef T type;
};
template <typename F>
void _ecore_main_loop_thread_safe_call_async_callback(void* data)
{
std::unique_ptr<F> f (static_cast<F*>(data));
try
{
(*f)();
}
catch(std::bad_alloc const& e)
{
eina_error_set( ::EINA_ERROR_OUT_OF_MEMORY);
}
catch(std::system_error const& e)
{
efl::eina::set_error_code(e.code());
}
catch(...)
{
eina_error_set( efl::eina::unknown_error() );
}
}
template <typename T>
struct _return_buffer
{
typename std::aligned_storage<sizeof(T),std::alignment_of<T>::value>::type buffer;
};
template <>
struct _return_buffer<void>
{
};
template <typename F>
struct _data
{
F& f;
std::exception_ptr exception;
typedef typename std::result_of<F()>::type result_type;
_return_buffer<result_type> return_buffer;
};
template <typename F>
void* _ecore_main_loop_thread_safe_call_sync_callback_aux(_data<F>* d, _identity<void>)
{
d->f();
if(eina_error_get())
d->exception = make_exception_ptr(std::system_error(efl::eina::get_error_code()));
return 0;
}
template <typename F, typename R>
void* _ecore_main_loop_thread_safe_call_sync_callback_aux(_data<F>* d, _identity<R>)
{
typedef R result_type;
new (&d->return_buffer.buffer) result_type ( std::move(d->f()) );
if(eina_error_get())
{
d->exception = make_exception_ptr(std::system_error(efl::eina::get_error_code()));
eina_error_set(0);
result_type* p = static_cast<result_type*>(static_cast<void*>(&d->return_buffer.buffer));
p->~result_type();
}
return 0;
}
template <typename F>
void* _ecore_main_loop_thread_safe_call_sync_callback(void* data)
{
_data<F>* d = static_cast<_data<F>*>(data);
try
{
return _ecore_main_loop_thread_safe_call_sync_callback_aux
(d, _identity<typename std::result_of<F()>::type>());
}
catch(std::bad_alloc const& e)
{
d->exception = std::current_exception();
}
catch(std::system_error const& e)
{
d->exception = std::current_exception();
}
catch(...)
{
d->exception = std::current_exception();
}
return 0;
}
template <typename F>
void main_loop_thread_safe_call_async(F&& f)
{
::ecore_main_loop_thread_safe_call_async( &ecore::_ecore_main_loop_thread_safe_call_async_callback<F>
, new F(std::forward<F>(f)) );
}
template <typename F>
void _get_return_value(_data<F>& data, _identity<void>)
{
if(data.exception)
{
std::rethrow_exception(data.exception);
}
}
template <typename F, typename R>
R _get_return_value(_data<F>& data, _identity<R>)
{
if(!data.exception)
{
R* b_ = static_cast<R*>(static_cast<void*>(&data.return_buffer.buffer));
struct destroy
{
destroy(R* x_) : p_(x_)
{}
~destroy()
{
p_->~R();
}
R* p_;
} destroy_temp(b_);
return std::move(*b_);
}
else
{
std::rethrow_exception(data.exception);
}
}
template <typename F>
typename std::result_of<F()>::type
main_loop_thread_safe_call_sync(F&& f)
{
typedef typename std::result_of<F()>::type result_type;
_data<F> data {f, nullptr, {}};
::ecore_main_loop_thread_safe_call_sync
(&ecore::_ecore_main_loop_thread_safe_call_sync_callback<F>, &data);
return _get_return_value(data, _identity<result_type>());
}
struct ecore_init
{
ecore_init()
{
::ecore_init();
}
~ecore_init()
{
::ecore_shutdown();
}
};
} }
#endif

View File

@ -26,6 +26,7 @@
#include <eina_workarounds.hh>
#include <eina_future.hh>
#include <eina_deleter.hh>
#include <eina_copy_traits.hh>
/**
* @page eina_cxx_main Eina C++ (BETA)

View File

@ -0,0 +1,43 @@
///
/// @file eo_concrete.hh
///
#ifndef EFL_CXX_EINA_COPY_TRAITS_HH
#define EFL_CXX_EINA_COPY_TRAITS_HH
namespace efl { namespace eina {
template <typename T, typename Enable = void>
struct copy_from_c_traits;
template <typename T>
struct copy_from_c_traits<T, typename std::enable_if<std::is_fundamental<T>::value>::type>
{
static void copy_to_unitialized(T* storage, void const* data)
{
std::memcpy(storage, data, sizeof(T));
}
};
template <typename T, typename Enable = void>
struct alloc_to_c_traits;
template <typename T>
struct alloc_to_c_traits<T, typename std::enable_if<std::is_fundamental<T>::value>::type>
{
typedef T c_type;
static c_type* copy_alloc(T const& value)
{
c_type* v = static_cast<c_type*>(malloc(sizeof(c_type)));
std::memcpy(v, &value, sizeof(c_type));
return v;
}
static void free_alloc(void* data)
{
::free(data);
}
};
} }
#endif

View File

@ -4,8 +4,9 @@
#include <eo_concrete.hh>
#include <eo_init.hh>
#include <eo_wref.hh>
// #include <eo_inherit.hh>
//#include <eo_inherit.hh>
//#include <eo_own_ptr.hh>
#include <eo_promise.hh>
#include <eo_cxx_interop.hh>
#include <eo_event.hh>

View File

@ -55,14 +55,14 @@ struct out_traits<eina::optional<T&>> { typedef eina::optional<T&> type; };
template <>
struct out_traits<void*> { typedef void*& type; };
template <typename T>
struct out_traits<efl::eina::future<T>> { typedef efl::eina::future<T>& type; };
struct out_traits<efl::shared_future<T>> { typedef efl::shared_future<T>& type; };
template <typename T>
struct inout_traits { typedef T& type; };
template <>
struct inout_traits<void> { typedef void* type; };
template <typename T>
struct inout_traits<efl::eina::future<T>> { typedef efl::eina::future<T>& type; };
struct inout_traits<efl::shared_future<T>> { typedef efl::shared_future<T>& type; };
template <typename T>
struct return_traits { typedef T type; };
@ -126,7 +126,11 @@ void assign_out_impl(T& lhs, Eo const* rhs, tag<T&, Eo const*>
lhs._reset(const_cast<Eo*>(rhs));
}
template <typename T>
void assign_out_impl(efl::eina::future<T>& /*v*/, Eina_Promise*, tag<efl::eina::future<T>&, Eina_Promise*>)
void assign_out_impl(efl::promise<T>& /*v*/, Eina_Promise*, tag<efl::promise<T>&, Eina_Promise*>)
{
}
template <typename T>
void assign_out_impl(efl::shared_future<T>& /*v*/, Efl_Future*, tag<efl::shared_future<T>&, Efl_Future*>)
{
}
template <typename Tag>
@ -257,7 +261,12 @@ Eo const* convert_inout_impl(T v, tag<T, Eo const*>
return v._eo_ptr();
}
template <typename T>
Eina_Promise* convert_inout_impl(efl::eina::future<T>& /*v*/, tag<efl::eina::future<T>, Eina_Promise*>)
Eina_Promise* convert_inout_impl(efl::promise<T>& /*v*/, tag<efl::promise<T>, Eina_Promise*>)
{
return nullptr;
}
template <typename T>
Efl_Future* convert_inout_impl(efl::shared_future<T>& /*v*/, tag<efl::shared_future<T>, Efl_Future*>)
{
return nullptr;
}
@ -511,7 +520,12 @@ inline const char* convert_to_c_impl(efl::eina::stringshare x, tag<const char*,
return eina_stringshare_ref(x.c_str());
}
template <typename T>
Eina_Promise* convert_to_c_impl(efl::eina::future<T> const&, tag<Eina_Promise*, efl::eina::future<T>const&>)
Eina_Promise* convert_to_c_impl(efl::promise<T> const&, tag<Eina_Promise*, efl::promise<T>const&>)
{
std::abort();
}
template <typename T>
Efl_Future* convert_to_c_impl(efl::shared_future<T> const&, tag<Efl_Future*, efl::shared_future<T>const&>)
{
std::abort();
}
@ -653,11 +667,13 @@ eina::accessor<T> convert_to_return(Eina_Accessor* value, tag<Eina_Accessor*, ei
return eina::accessor<T>{ value };
}
template <typename T>
struct is_future : std::false_type {};
efl::promise<T> convert_to_return(Eina_Promise* /*value*/, tag<Eina_Promise*, efl::promise<T>>)
{
std::abort();
return {};
}
template <typename T>
struct is_future<efl::eina::future<T>> : std::true_type {};
template <typename T>
T convert_to_return(Eina_Promise* /*value*/, tag<Eina_Promise*, T>, typename std::enable_if<is_future<T>::value>::type* = 0)
efl::shared_future<T> convert_to_return(Efl_Future* /*value*/, tag<Efl_Future*, efl::shared_future<T>>)
{
std::abort();
return {};

View File

@ -0,0 +1,416 @@
///
/// @file eo_concrete.hh
///
#ifndef EFL_CXX_EO_PROMISE_HH
#define EFL_CXX_EO_PROMISE_HH
#include <Efl.h>
#include <Eina.hh>
#include <Ecore_Manual.hh>
#include <mutex>
#include <condition_variable>
namespace efl {
namespace _impl {
template <typename F, typename...Args>
struct future_invoke_result_of;
template <typename F, typename A0>
struct future_invoke_result_of<F, A0>
{
typedef typename std::result_of<F(A0)>::type type;
};
template <typename F>
struct future_invoke_result_of<F, void>
{
typedef typename std::result_of<F()>::type type;
};
template <typename A0, typename F>
typename std::enable_if
<
!std::is_same<A0, void>::value
&& !std::is_same<typename std::result_of<F(A0)>::type, void>::value
>::type
future_invoke(F f, Efl_Event const* event)
{
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
try
{
typename std::aligned_storage<sizeof(A0), alignof(A0)>::type storage;
eina::copy_from_c_traits<A0>::copy_to_unitialized
(static_cast<A0*>(static_cast<void*>(&storage)), info->value);
auto r = f(*static_cast<A0*>(static_cast<void*>(&storage)));
typedef decltype(r) result_type;
typedef typename eina::alloc_to_c_traits<result_type>::c_type c_type;
c_type* c_value = eina::alloc_to_c_traits<result_type>::copy_alloc(r);
efl_promise_value_set(info->next, c_value, & eina::alloc_to_c_traits<result_type>::free_alloc);
}
catch(...)
{
}
}
template <typename A0, typename F>
typename std::enable_if<std::is_same<A0, void>::value>::type
future_invoke(F f, Efl_Event const* event)
{
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
try
{
f();
}
catch(...)
{
}
}
template <typename A0, typename A1, typename...OtherArgs, typename F>
// typename future_invoke_result_of<F, A0, A1, OtherArgs...>::type
void
future_invoke(F f, Efl_Event const* event)
{
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
}
}
template <typename T>
struct progress;
namespace _impl {
template <typename V = char>
struct wait_state
{
bool available = false;
bool has_failed = false;
std::mutex mutex;
std::condition_variable cv;
typename std::aligned_storage<sizeof(V), alignof(V)>::type storage;
Eina_Error error;
};
static void get_error_cb(void* data, Efl_Event const* event)
{
struct wait_state<>* wait_state = static_cast<struct wait_state<>*>(data);
Efl_Future_Event_Failure* info = static_cast<Efl_Future_Event_Failure*>(event->info);
std::unique_lock<std::mutex> l(wait_state->mutex);
wait_state->error = info->error;
wait_state->has_failed = true;
wait_state->available = true;
wait_state->cv.notify_one();
}
struct shared_future_common
{
explicit shared_future_common(Efl_Future* future)
: _future(future) {}
shared_future_common()
: _future(nullptr) {}
~shared_future_common()
{
if(_future)
efl_unref(_future);
}
shared_future_common(shared_future_common const& future)
: _future(efl_ref(future._future))
{
}
shared_future_common& operator=(shared_future_common const& other)
{
_self_type tmp(other);
tmp.swap(*this);
return *this;
}
shared_future_common(shared_future_common&& future)
: _future(future._future)
{
future._future = nullptr;
}
shared_future_common& operator=(shared_future_common&& other)
{
other.swap(*this);
return *this;
}
void swap(shared_future_common& other)
{
std::swap(_future, other._future);
}
bool valid() const noexcept
{
return _future != nullptr;
}
void wait() const
{
if(eina_main_loop_is())
throw std::runtime_error("Deadlock");
struct wait_state<> wait_state;
efl::ecore::main_loop_thread_safe_call_async
([&]
{
efl_future_then(this->_future, &wait_success, &wait_success, nullptr, &wait_state);
});
std::unique_lock<std::mutex> lock(wait_state.mutex);
while(!wait_state.available)
wait_state.cv.wait(lock);
}
static void wait_success(void* data, Efl_Event const*)
{
struct wait_state<>* wait_state = static_cast<struct wait_state<>*>(data);
std::unique_lock<std::mutex> l(wait_state->mutex);
wait_state->available = true;
wait_state->cv.notify_one();
}
typedef Efl_Future* native_handle_type;
typedef Efl_Future const* const_native_handle_type;
native_handle_type native_handle() noexcept { return _future; }
const_native_handle_type native_handle() const noexcept { return _future; }
typedef shared_future_common _self_type;
Efl_Future* _future;
};
template <typename T>
struct shared_future_1_type : private shared_future_common
{
typedef shared_future_common _base_type;
using _base_type::_base_type;
using _base_type::swap;
using _base_type::valid;
using _base_type::native_handle;
using _base_type::wait;
typedef _base_type::native_handle_type native_handle_type;
T get() const
{
if(eina_main_loop_is())
throw std::runtime_error("Deadlock");
struct wait_state<T> wait_state;
efl::ecore::main_loop_thread_safe_call_async
([&]
{
efl_future_then(this->_future, &get_success, &_impl::get_error_cb, nullptr, &wait_state);
});
{
std::unique_lock<std::mutex> lock(wait_state.mutex);
while(!wait_state.available)
wait_state.cv.wait(lock);
}
if(wait_state.has_failed)
EFL_CXX_THROW(eina::system_error(eina::error_code(wait_state.error, eina::eina_error_category()), "EFL Eina Error"));
return *static_cast<T*>(static_cast<void*>(&wait_state.storage));
}
static void get_success(void* data, Efl_Event const* event)
{
struct wait_state<T>* wait_state = static_cast<struct wait_state<T>*>(data);
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
std::unique_lock<std::mutex> l(wait_state->mutex);
eina::copy_from_c_traits<T>::copy_to_unitialized
(static_cast<T*>(static_cast<void*>(&wait_state->storage)), info->value);
wait_state->available = true;
wait_state->cv.notify_one();
}
typedef shared_future_1_type<T> _self_type;
};
template <typename...Args>
struct shared_future_varargs_type : private shared_future_common
{
typedef shared_future_common _base_type;
using _base_type::_base_type;
using _base_type::swap;
using _base_type::valid;
using _base_type::native_handle;
using _base_type::wait;
typedef _base_type::native_handle_type native_handle_type;
typedef std::tuple<Args...> tuple_type;
std::tuple<Args...> get() const
{
if(eina_main_loop_is())
throw std::runtime_error("Deadlock");
struct wait_state<tuple_type> wait_state;
efl::ecore::main_loop_thread_safe_call_async
([&]
{
efl_future_then(this->_future, &get_success, &_impl::get_error_cb, nullptr, &wait_state);
});
{
std::unique_lock<std::mutex> lock(wait_state.mutex);
while(!wait_state.available)
wait_state.cv.wait(lock);
}
if(wait_state.has_failed)
EFL_CXX_THROW(eina::system_error(eina::error_code(wait_state.error, eina::eina_error_category()), "EFL Eina Error"));
return *static_cast<tuple_type*>(static_cast<void*>(&wait_state.storage));
}
template <std::size_t N>
static void read_accessor(Eina_Accessor* accessor
, std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...>& storage_tuple
, wait_state<tuple_type>* wait_state
, std::false_type)
{
typedef typename std::tuple_element<N, tuple_type>::type type;
void* value;
if(eina_accessor_data_get(accessor, N, &value))
{
eina::copy_from_c_traits<type>::copy_to_unitialized
(static_cast<type*>(static_cast<void*>(&std::get<N>(storage_tuple))), value);
std::cout << "copied value " << *static_cast<type*>(static_cast<void*>(&std::get<N>(storage_tuple)))
<< std::endl;
_self_type::read_accessor<N+1>(accessor, storage_tuple, wait_state
, std::integral_constant<bool, (N+1 == sizeof...(Args))>());
}
else
{
std::abort();
// some error
}
}
template <std::size_t N, std::size_t...I>
static void read_accessor_end(std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...>& storage_tuple
, wait_state<tuple_type>* wait_state
, eina::index_sequence<I...>)
{
std::unique_lock<std::mutex> l(wait_state->mutex);
new (&wait_state->storage) tuple_type{(*static_cast<typename std::tuple_element<I, tuple_type>::type*>
(static_cast<void*>(&std::get<I>(storage_tuple))))...};
wait_state->available = true;
wait_state->cv.notify_one();
}
template <std::size_t N>
static void read_accessor(Eina_Accessor*
, std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...>& storage_tuple
, wait_state<tuple_type>* wait_state
, std::true_type)
{
_self_type::read_accessor_end<N>(storage_tuple, wait_state, eina::make_index_sequence<sizeof...(Args)>{});
}
static void get_success(void* data, Efl_Event const* event)
{
struct wait_state<tuple_type>* wait_state = static_cast<struct wait_state<tuple_type>*>(data);
Efl_Future_Event_Success* info = static_cast<Efl_Future_Event_Success*>(event->info);
Eina_Accessor* accessor = static_cast<Eina_Accessor*>(info->value);
std::tuple<typename std::aligned_storage<sizeof(Args), alignof(Args)>::type...> storage_tuple;
_self_type::read_accessor<0u>(accessor, storage_tuple, wait_state, std::false_type());
}
typedef shared_future_varargs_type<Args...> _self_type;
};
}
template <typename...Args>
struct shared_future : private std::conditional<sizeof...(Args) == 1, _impl::shared_future_1_type<typename std::tuple_element<0u, std::tuple<Args...>>::type>, _impl::shared_future_varargs_type<Args...>>::type
{
typedef typename std::conditional<sizeof...(Args) == 1, _impl::shared_future_1_type<typename std::tuple_element<0u, std::tuple<Args...>>::type>, _impl::shared_future_varargs_type<Args...>>::type _base_type;
using _base_type::_base_type;
using _base_type::swap;
using _base_type::valid;
using _base_type::get;
using _base_type::wait;
using _base_type::native_handle;
typedef typename _base_type::native_handle_type native_handle_type;
};
template <typename...Args, typename Success, typename Error>
shared_future
<
typename std::enable_if
<
!std::is_same<void, typename std::tuple_element<0, std::tuple<Args...>>::type>::value
&& !std::is_same<void, typename std::result_of<Success(Args...)>::type>::value
, typename std::result_of<Success(Args...)>::type
>::type
> then(shared_future<Args...> future, Success success_cb, Error error_cb)
{
struct private_data
{
Success success_cb;
Error error_cb;
shared_future<Args...> future;
};
private_data* pdata = new private_data
{std::move(success_cb), std::move(error_cb), std::move(future)};
Efl_Event_Cb raw_success_cb =
[] (void* data, Efl_Event const* event)
{
private_data* pdata = static_cast<private_data*>(data);
try
{
_impl::future_invoke<Args...>(pdata->success_cb, event);
// should value_set the next promise
}
catch(...)
{
// should fail the next promise
}
delete pdata;
};
Efl_Event_Cb raw_error_cb =
[] (void* data, Efl_Event const* event)
{
private_data* pdata = static_cast<private_data*>(data);
Efl_Future_Event_Failure* info = static_cast<Efl_Future_Event_Failure*>(event->info);
pdata->error_cb(eina::error_code(info->error, eina::eina_error_category()));
// should error the next promise (or should the promise do that for me automatically?)
delete pdata;
};
assert(pdata->future.valid());
Efl_Future* new_future
= efl_future_then(pdata->future.native_handle(), raw_success_cb, raw_error_cb, nullptr, pdata);
return shared_future<typename std::result_of<Success(Args...)>::type>{efl_ref(new_future)};
}
template <typename...Args, typename F>
void then(shared_future<Args...> future, F function)
{
}
template <typename...Args>
struct promise
{
};
}
#endif

View File

@ -256,7 +256,13 @@ struct visitor_generate
, {"promise", nullptr, nullptr, [&]
{
return replace_outer
(complex, regular_type_def{" ::efl::eina::future", complex.outer.base_qualifier, {}});
(complex, regular_type_def{" ::efl::promise", complex.outer.base_qualifier, {}});
}
}
, {"future", nullptr, nullptr, [&]
{
return replace_outer
(complex, regular_type_def{" ::efl::shared_future", complex.outer.base_qualifier, {}});
}
}
, {"iterator", nullptr, nullptr, [&]

View File

@ -0,0 +1,27 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "eo_cxx_suite.h"
#include "../efl_check.h"
static const Efl_Test_Case etc[] = {
{ "Promise", eo_cxx_test_promise },
{ NULL, NULL }
};
int
main(int argc, char* argv[])
{
int failed_count;
if (!_efl_test_option_disp(argc, argv, etc))
return 0;
putenv(const_cast<char*>("EFL_RUN_IN_TREE=1"));
failed_count = _efl_suite_build_and_run(argc - 1, (const char **)argv + 1,
"Eo C++", etc);
return (failed_count == 0) ? 0 : 255;
}

View File

@ -0,0 +1,11 @@
#ifndef _EINA_CXX_SUITE_H
#define _EINA_CXX_SUITE_H
#include <cassert>
#include <algorithm>
#include <check.h>
void eo_cxx_test_promise(TCase* tc);
#endif /* _EINA_CXX_SUITE_H */

View File

@ -0,0 +1,347 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Eina.hh>
#include <Eo.hh>
#include <Ecore.hh>
#include "eo_cxx_suite.h"
START_TEST(eo_cxx_future_construct_and_destroy)
{
Efl_Promise *p;
Efl_Future *f;
ecore_init();
{
p = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get());
fail_if(!p);
f = efl_promise_future_get(p);
fail_if(!f);
efl::shared_future<int> future(efl_ref(f));
}
ecore_shutdown();
}
END_TEST
START_TEST(eo_cxx_future_wait)
{
Efl_Promise *p;
Efl_Future *f;
ecore_init();
p = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get());
fail_if(!p);
f = efl_promise_future_get(p);
fail_if(!f);
{
efl::shared_future<int> future(efl_ref(f));
std::thread thread([&]
{
efl::ecore::main_loop_thread_safe_call_sync([]{}); // wait for ecore_main_loop_begin() call to start
efl::ecore::main_loop_thread_safe_call_async
([&]
{
int* i = static_cast<int*>(malloc(sizeof(int)));
*i = 5;
efl_promise_value_set(p, i, & ::free);
});
future.wait();
efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); });
});
ecore_main_loop_begin();
thread.join();
}
ecore_shutdown();
}
END_TEST
START_TEST(eo_cxx_future_get)
{
Efl_Promise *p;
Efl_Future *f;
ecore_init();
p = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get());
fail_if(!p);
f = efl_promise_future_get(p);
fail_if(!f);
{
efl::shared_future<int> future(efl_ref(f));
std::thread thread([&]
{
efl::ecore::main_loop_thread_safe_call_sync([]{}); // wait for ecore_main_loop_begin() call to start
efl::ecore::main_loop_thread_safe_call_async
([&]
{
int* i = static_cast<int*>(malloc(sizeof(int)));
*i = 5;
efl_promise_value_set(p, i, & ::free);
});
int i = future.get();
ck_assert_int_eq(i, 5);
efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); });
});
ecore_main_loop_begin();
thread.join();
}
ecore_shutdown();
}
END_TEST
START_TEST(eo_cxx_future_get_error)
{
Efl_Promise *p;
Efl_Future *f;
ecore_init();
p = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get());
fail_if(!p);
f = efl_promise_future_get(p);
fail_if(!f);
{
efl::shared_future<int> future(efl_ref(f));
std::thread thread([&]
{
efl::ecore::main_loop_thread_safe_call_sync([]{}); // wait for ecore_main_loop_begin() call to start
efl::ecore::main_loop_thread_safe_call_async
([&]
{
efl_promise_failed_set(p, EINA_ERROR_OUT_OF_MEMORY);
});
try {
future.get();
ck_abort_msg("Execution should not have continued, exception expected");
}
catch(std::system_error const& e)
{
}
efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); });
});
ecore_main_loop_begin();
thread.join();
}
ecore_shutdown();
}
END_TEST
START_TEST(eo_cxx_future_then_value)
{
Efl_Promise *promise;
Efl_Future *f;
ecore_init();
promise = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get());
fail_if(!promise);
f = efl_promise_future_get(promise);
fail_if(!f);
{
efl::shared_future<int> future(efl_ref(f)), rfuture;
std::thread thread
([&]
{
efl::ecore::main_loop_thread_safe_call_sync
([&]
{
rfuture = then
(future, [] (int i) -> int
{
ck_assert_int_eq(i, 5);
return 42;
}, [] (std::error_code)
{
throw std::bad_alloc();
});
});
efl::ecore::main_loop_thread_safe_call_async
([&]
{
int* i = static_cast<int*>(malloc(sizeof(int)));
*i = 5;
efl_promise_value_set(promise, i, &::free);
});
int i = rfuture.get();
ck_assert_int_eq(i, 42);
efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); });
});
ecore_main_loop_begin();
thread.join();
}
ecore_shutdown();
}
END_TEST
START_TEST(eo_cxx_future_composite_construct_and_destroy)
{
ecore_init();
{
Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get());
fail_if(!p1);
Efl_Future *f1 = efl_promise_future_get(p1);
fail_if(!f1);
Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get());
fail_if(!p1);
Efl_Future *f2 = efl_promise_future_get(p2);
fail_if(!f2);
Efl_Future *f3 = efl_future_all(f1, f2);
fail_if(!f3);
efl::shared_future<int> future1(efl_ref(f1))
, future2(efl_ref(f2));
efl::shared_future<int, int> future3(efl_ref(f3));
}
ecore_shutdown();
}
END_TEST
START_TEST(eo_cxx_future_composite_wait)
{
ecore_init();
{
Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get());
fail_if(!p1);
Efl_Future* f1 = efl_promise_future_get(p1);
fail_if(!f1);
Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get());
fail_if(!p2);
Efl_Future* f2 = efl_promise_future_get(p2);
fail_if(!f2);
Efl_Future *f3 = efl_future_all(f1, f2);
fail_if(!f3);
efl::shared_future<int> future1(efl_ref(f1))
, future2(efl_ref(f2));
efl::shared_future<int, int> future3(efl_ref(f3));
std::thread thread([&]
{
efl::ecore::main_loop_thread_safe_call_sync([]{}); // wait for ecore_main_loop_begin() call to start
efl::ecore::main_loop_thread_safe_call_async
([&]
{
int* i1 = static_cast<int*>(malloc(sizeof(int)));
*i1 = 5;
efl_promise_value_set(p1, i1, & ::free);
int* i2 = static_cast<int*>(malloc(sizeof(int)));
*i2 = 42;
efl_promise_value_set(p2, i2, & ::free);
});
future3.wait();
efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); });
});
ecore_main_loop_begin();
thread.join();
}
ecore_shutdown();
}
END_TEST
START_TEST(eo_cxx_future_composite_get)
{
ecore_init();
{
Efl_Promise *p1 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get());
fail_if(!p1);
Efl_Future *f1 = efl_promise_future_get(p1);
fail_if(!f1);
Efl_Promise *p2 = efl_add(EFL_PROMISE_CLASS, ecore_main_loop_get());
fail_if(!p2);
Efl_Future *f2 = efl_promise_future_get(p2);
fail_if(!f2);
Efl_Future *f3 = efl_future_all(f1, f2);
fail_if(!f3);
efl::shared_future<int> future1(efl_ref(f1))
, future2(efl_ref(f2));
efl::shared_future<int, int> future3(efl_ref(f3));
std::thread thread([&]
{
efl::ecore::main_loop_thread_safe_call_sync([]{}); // wait for ecore_main_loop_begin() call to start
efl::ecore::main_loop_thread_safe_call_async
([&]
{
int* i1 = static_cast<int*>(malloc(sizeof(int)));
*i1 = 5;
efl_promise_value_set(p1, i1, & ::free);
int* i2 = static_cast<int*>(malloc(sizeof(int)));
*i2 = 42;
efl_promise_value_set(p2, i2, & ::free);
});
std::tuple<int, int> tuple = future3.get();
ck_assert_int_eq(std::get<0>(tuple), 5);
ck_assert_int_eq(std::get<1>(tuple), 42);
efl::ecore::main_loop_thread_safe_call_sync([] { ecore_main_loop_quit(); });
});
ecore_main_loop_begin();
thread.join();
}
ecore_shutdown();
}
END_TEST
void
eo_cxx_test_promise(TCase* tc)
{
tcase_add_test(tc, eo_cxx_future_construct_and_destroy);
tcase_add_test(tc, eo_cxx_future_wait);
tcase_add_test(tc, eo_cxx_future_get);
tcase_add_test(tc, eo_cxx_future_get_error);
tcase_add_test(tc, eo_cxx_future_then_value);
tcase_add_test(tc, eo_cxx_future_composite_construct_and_destroy);
tcase_add_test(tc, eo_cxx_future_composite_wait);
tcase_add_test(tc, eo_cxx_future_composite_get);
}