diff --git a/src/Makefile.am b/src/Makefile.am index 9abc8a919d..b2ac30d76a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,6 +27,7 @@ include Makefile_Eo.am include Makefile_Eet.am include Makefile_Evas.am include Makefile_Ecore.am +include Makefile_Ecore_Cxx.am include Makefile_Ecore_Con.am include Makefile_Ecore_Ipc.am include Makefile_Ecore_File.am diff --git a/src/Makefile_Ecore_Cxx.am b/src/Makefile_Ecore_Cxx.am new file mode 100644 index 0000000000..ff8efdca86 --- /dev/null +++ b/src/Makefile_Ecore_Cxx.am @@ -0,0 +1,36 @@ + +### Library + +installed_ecorecxxmainheadersdir = $(includedir)/ecore_cxx-@VMAJ@ +dist_installed_ecorecxxmainheaders_DATA = \ +bindings/ecore_cxx/Ecore.hh + +### Unit tests + +if EFL_ENABLE_TESTS +if HAVE_CXX11 + +check_PROGRAMS += tests/ecore_cxx/ecore_cxx_suite +TESTS += tests/ecore_cxx/ecore_cxx_suite + +tests_ecore_cxx_ecore_cxx_suite_SOURCES = \ +tests/ecore_cxx/ecore_cxx_suite.cc \ +tests/ecore_cxx/ecore_cxx_test_safe_call.cc + +tests_ecore_cxx_ecore_cxx_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ +-I$(top_srcdir)/src/bindings/ecore_cxx \ +-I$(top_srcdir)/src/bindings/eina_cxx \ +-DTESTS_SRC_DIR=\"$(top_srcdir)/src/tests/ecore_cxx\" \ +-DTESTS_BUILD_DIR=\"$(top_builddir)/src/tests/ecore_cxx\" \ +@CHECK_CFLAGS@ \ +@ECORE_CFLAGS@ \ +@EINA_CFLAGS@ + +tests_ecore_cxx_ecore_cxx_suite_LDADD = \ +@CHECK_LIBS@ \ +@USE_ECORE_LIBS@ +tests_ecore_cxx_ecore_cxx_suite_DEPENDENCIES = \ +@USE_ECORE_INTERNAL_LIBS@ + +endif +endif diff --git a/src/bindings/ecore_cxx/Ecore.hh b/src/bindings/ecore_cxx/Ecore.hh new file mode 100644 index 0000000000..9d2dd0bf78 --- /dev/null +++ b/src/bindings/ecore_cxx/Ecore.hh @@ -0,0 +1,123 @@ +#ifndef _EFL_ECORE_CXX_ECORE_HH +#define _EFL_ECORE_CXX_ECORE_HH + +#include + +#include +#include +#include +#include + +namespace efl { namespace ecore { + +template +struct _ecore_result_type_marshaller; + +template +struct _ecore_result_type_marshaller + ::value>::type> +{ + static void* to_void(T o) + { + return static_cast(o); + } + static T from_void(void* o) + { + return static_cast(o); + } +}; + +template +struct _ecore_result_type_marshaller + ::value && std::is_pod::value + && sizeof(T) <= sizeof(void*)>::type> +{ + static void* to_void(T&& o) + { + unsigned char buf[sizeof(void*)]; + T* p = static_cast(static_cast(&buf[0])); + new (p) T(std::move(o)); + void* store; + std::memcpy(&store, buf, sizeof(void*)); + return store; + } + static T from_void(void* store) + { + T* p = static_cast(static_cast(&store)); + struct destroy_T + { + destroy_T(T& p) + : p(p) {} + ~destroy_T() + { + p.~T(); + } + T& p; + } destroy(*p); + return T(std::move(*p)); + } +}; + +template +struct _ecore_result_type_marshaller + sizeof(void*)) || !std::is_pod::value>::type> +{ + static void* to_void(T&& o) + { + return new T(o); + } + static T from_void(void* store) + { + std::unique_ptr p(static_cast(store)); + return T(std::move(*p.get())); + } +}; + +template +void _ecore_main_loop_thread_safe_call_async_callback(void* data) +{ + F* f = static_cast(data); + (*f)(); + delete f; +} + +template +void* _ecore_main_loop_thread_safe_call_sync_callback(void* data) +{ + F* f = static_cast(data); + typedef typename std::result_of::type result_type; + return _ecore_result_type_marshaller::to_void((*f)()); +} + +template +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 + , new F(std::move(f)) ); +} + +template +typename std::result_of::type +main_loop_thread_safe_call_sync(F&& f) +{ + void* d = ::ecore_main_loop_thread_safe_call_sync + (&ecore::_ecore_main_loop_thread_safe_call_sync_callback, &f); + typedef typename std::result_of::type result_type; + return _ecore_result_type_marshaller::from_void(d); +} + +struct ecore_init +{ + ecore_init() + { + ::ecore_init(); + } + ~ecore_init() + { + ::ecore_shutdown(); + } +}; + +} } + +#endif diff --git a/src/tests/ecore_cxx/ecore_cxx_suite.cc b/src/tests/ecore_cxx/ecore_cxx_suite.cc new file mode 100644 index 0000000000..9394672cd5 --- /dev/null +++ b/src/tests/ecore_cxx/ecore_cxx_suite.cc @@ -0,0 +1,103 @@ + +#include "Ecore.hh" + +#include +#include + +#include + +void ecore_test_safe_call(TCase* tc); + +typedef struct _Ecore_Test_Case Ecore_Test_Case; +struct _Ecore_Test_Case +{ + const char *test_case; + void (*build)(TCase *tc); +}; + +static const Ecore_Test_Case etc[] = { + { "Safe_Call", ecore_test_safe_call }, + { NULL, NULL } +}; + +static void +_list_tests(void) +{ + const Ecore_Test_Case *itr = etc; + fputs("Available Test Cases:\n", stderr); + for (; itr->test_case; itr++) + fprintf(stderr, "\t%s\n", itr->test_case); +} + +static Eina_Bool +_use_test(int argc, const char **argv, const char *test_case) +{ + if (argc < 1) + return 1; + + for (; argc > 0; argc--, argv++) + if (strcmp(test_case, *argv) == 0) + return 1; + + return 0; +} + +Suite * +ecore_build_suite(int argc, const char **argv) +{ + TCase *tc; + Suite *s; + int i; + + s = suite_create("Ecore"); + + for (i = 0; etc[i].test_case; ++i) + { + if (!_use_test(argc, argv, etc[i].test_case)) + continue; + + tc = tcase_create(etc[i].test_case); + tcase_set_timeout(tc, 0); + + etc[i].build(tc); + suite_add_tcase(s, tc); + } + + return s; +} + +int main(int argc, char* argv[]) +{ + Suite *s; + SRunner *sr; + int i, failed_count; + + for (i = 1; i < argc; i++) + if ((strcmp(argv[i], "-h") == 0) || + (strcmp(argv[i], "--help") == 0)) + { + fprintf(stderr, "Usage:\n\t%s [test_case1 .. [test_caseN]]\n", + argv[0]); + _list_tests(); + return 0; + } + else if ((strcmp(argv[i], "-l") == 0) || + (strcmp(argv[i], "--list") == 0)) + { + _list_tests(); + return 0; + } + + putenv(const_cast("EFL_RUN_IN_TREE=1")); + + s = ecore_build_suite(argc - 1, (const char **)argv + 1); + sr = srunner_create(s); + + srunner_set_xml(sr, TESTS_BUILD_DIR "/check-results.xml"); + + srunner_run_all(sr, CK_ENV); + failed_count = srunner_ntests_failed(sr); + srunner_free(sr); + + return (failed_count == 0) ? 0 : 255; +} diff --git a/src/tests/ecore_cxx/ecore_cxx_test_safe_call.cc b/src/tests/ecore_cxx/ecore_cxx_test_safe_call.cc new file mode 100644 index 0000000000..bc2b78d4f3 --- /dev/null +++ b/src/tests/ecore_cxx/ecore_cxx_test_safe_call.cc @@ -0,0 +1,193 @@ + +#include "Ecore.hh" +#include "Eina.hh" + +#include + +#include + +#include + +void call_async(efl::eina::mutex& mutex, efl::eina::condition_variable& cond, bool& done) +{ + efl::ecore::main_loop_thread_safe_call_async + ( + [&mutex,&cond,&done] + { + std::cout << "yeah" << std::endl; + ecore_main_loop_quit(); + efl::eina::unique_lock l(mutex); + std::cout << "mutex locked" << std::endl; + done = true; + cond.notify_one(); + std::cout << "exiting" << std::endl; + } + ); +} + +START_TEST(ecore_cxx_safe_call_async) +{ + efl::ecore::ecore_init init; + + efl::eina::mutex mutex; + efl::eina::condition_variable cond; + bool done = false; + efl::eina::thread thread(&call_async, std::ref(mutex), std::ref(cond), std::ref(done)); + + ecore_main_loop_begin(); + + std::cout << "joining" << std::endl; + + thread.join(); + + std::cout << "joined" << std::endl; + + efl::eina::unique_lock l(mutex); + while(!done) + { + std::cout << "waiting" << std::endl; + cond.wait(l); + std::cout << "waited" << std::endl; + } + + std::cout << "end of ecore_cxx_safe_call_async" << std::endl; +} +END_TEST + +struct big_pod +{ + double x; + double y; +}; + +int constructor_called = 0 + , destructor_called = 0; + +struct small_nonpod +{ + small_nonpod() + : c(5) + { + constructor_called++; + } + small_nonpod(small_nonpod const& other) + : c(5) + { + ck_assert(other.c == 5); + constructor_called++; + } + ~small_nonpod() + { + ck_assert(c == 5); + destructor_called++; + } + char c; +}; + +struct big_nonpod : big_pod +{ + big_nonpod() + { + constructor_called++; + x = 2.0; + y = 1.0; + } + big_nonpod(big_nonpod const& other) + { + x = 2.0; + y = 1.0; + ck_assert(other.x == 2.0); + ck_assert(other.y == 1.0); + constructor_called++; + } + ~big_nonpod() + { + ck_assert(x == 2.0); + ck_assert(y == 1.0); + destructor_called++; + } + char c; +}; + +void call_sync_int() +{ + std::cout << "call_sync_init" << std::endl; + int r1 = + efl::ecore::main_loop_thread_safe_call_sync + ( + [] () -> int + { + return 1; + } + ); + ck_assert(r1 == 1); + + std::cout << "big_pod" << std::endl; + + big_pod r2 = + efl::ecore::main_loop_thread_safe_call_sync + ( + [] () -> big_pod + { + return {1.0, 2.0}; + } + ); + ck_assert(r2.x == 1.0); + ck_assert(r2.y == 2.0); + + std::cout << "small_nonpod" << std::endl; + + { + small_nonpod r3 = + efl::ecore::main_loop_thread_safe_call_sync + ( + [] () -> small_nonpod + { + return small_nonpod(); + } + ); + } + std::cout << "constructor_called: " << constructor_called << std::endl; + std::cout << "destructor_called: " << destructor_called << std::endl; + ck_assert(constructor_called == destructor_called); + + std::cout << "big_nonpod" << std::endl; + + { + big_nonpod r3 = + efl::ecore::main_loop_thread_safe_call_sync + ( + [] () -> big_nonpod + { + std::cout << "before quit" << std::endl; + ecore_main_loop_quit(); + std::cout << "are we calling here" << std::endl; + return big_nonpod(); + } + ); + } + std::cout << "constructor_called: " << constructor_called << std::endl; + std::cout << "destructor_called: " << destructor_called << std::endl; + ck_assert(constructor_called == destructor_called); +} + +START_TEST(ecore_cxx_safe_call_sync) +{ + efl::ecore::ecore_init init; + + efl::eina::thread thread(&call_sync_int); + + ecore_main_loop_begin(); + + std::cout << "out of the loop" << std::endl; + + thread.join(); +} +END_TEST + +void +ecore_test_safe_call(TCase* tc) +{ + tcase_add_test(tc, ecore_cxx_safe_call_async); + tcase_add_test(tc, ecore_cxx_safe_call_sync); +}