/* * Copyright 2019 by its authors. See AUTHORS. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ELDBUS_CXX_ELDBUS_SERVICE_HH #define ELDBUS_CXX_ELDBUS_SERVICE_HH #include namespace efl { namespace eldbus { struct service_interface { service_interface(Eldbus_Service_Interface const* iface) : _iface(iface) {} typedef Eldbus_Service_Interface* native_handle_type; typedef Eldbus_Service_Interface const* const_native_handle_type; const_native_handle_type native_handle() { return _iface; } const_native_handle_type native_handle() const { return _iface; } private: const_native_handle_type _iface; }; namespace service { template struct method_specification { const char* name; F f; typedef F function_type; typedef Ins ins_type; typedef Outs outs_type; ins_type ins; outs_type outs; }; namespace _detail { template struct const_char_type { typedef const char* type; }; } template struct ins { ins(typename _detail::const_char_type::type... names) : _names({{names...}}) {} static constexpr std::size_t size() { return sizeof...(Args); } typedef std::tuple types; std::array _names; }; template struct outs { outs(typename _detail::const_char_type::type... names) : _names({{names...}}) {} static constexpr std::size_t size() { return sizeof...(Args); } typedef std::tuple types; std::array _names; }; void foo(int); template method_specification method(const char* name, F f, Ins ins, Outs outs) { return method_specification {name, f, ins, outs} ; } template method_specification, service::outs > method(const char* name, F f, service::outs outs) { return method_specification, service::outs > {name, f, service::ins<>(), outs} ; } template method_specification, service::outs<> > method(const char* name, F f, service::ins ins) { return method_specification, service::outs<> > {name, f, ins, service::outs<>()} ; } } template class params , typename ... Args> int _fill_method_impl(std::array& array , params const& , std::true_type) { array[I] = Eldbus_Arg_Info {0, 0}; return 0; } template class params , typename ... Args> int _fill_method_impl(std::array& array , params const& directional_methods , std::false_type) { typedef std::tuple params_type; static const char sig[2] = {_detail::signature_traits < typename std::tuple_element::type >::sig , '\0' }; array[I] = {sig , directional_methods._names[I] }; return 0; } template class params , typename ... Args> int _fill_method(std::array& array , params const& directional_methods) { _fill_method_impl(array, directional_methods , std::integral_constant()); return 0; } template void _foo(T...) {} template class params, typename ... Args , std::size_t... Seq> void _fill_methods_impl(std::array& array , params const& directional_methods , eina::index_sequence ) { eldbus::_foo(eldbus::_fill_method(array, directional_methods) ...); } template class params, typename ... Args> void _fill_methods(std::array& array , params const& directional_methods) { eldbus::_fill_methods_impl(array, directional_methods , eina::make_index_sequence()); } template void _create_methods_specification(Tuple const& , std::array::value+1>& methods , std::integral_constant , std::true_type) { methods[I] = Eldbus_Method2 {{0, 0, 0, 0, 0}, 0}; }; template T get_in(Eldbus_Message const* msg) { typename _detail::signature_traits::raw_type object; const char sig[2] = {_detail::signature_traits::sig, 0}; if (!eldbus_message_arguments_get(msg, sig, &object)) { printf("eldbus_message_arguments_get() error\n"); throw std::runtime_error(""); } else { return object; } } template Eldbus_Message* _method_callback_call (Callable const& callable, service::ins const&, service::outs const& , eina::index_sequence, eina::index_sequence , eina::index_sequence , Eldbus_Service_Interface const* iface, Eldbus_Message const* message , typename std::enable_if (nullptr) , *static_cast(nullptr) , std::get(std::tuple())... , &std::get (*static_cast*>(nullptr))... ) )>::value, int>::type) { typedef std::tuple tuple_ins; typename _detail::raw_tuple::type tuple_ins_raw; _detail::_init_raw_tuple<0u, tuple_ins> (eldbus_message_iter_get(message), tuple_ins_raw , std::integral_constant::value == 0u)>()); std::tuple tuple_outs; eldbus::const_message msg( ::eldbus_message_ref(const_cast(message))); eldbus::service_interface siface(iface); // If you have an error here, then you probably got your ins<...> // and/or outs<...> different from the arguments of your callable //typedef _detail::signature_traits traits; std::get<0u>(tuple_outs) = callable(msg, siface, std::get(tuple_ins_raw)..., &std::get(tuple_outs)...); Eldbus_Message *reply_ = eldbus_message_method_return_new(message); _detail::_append_tuple<0u>(reply_, tuple_outs, std::false_type()); return reply_; } template Eldbus_Message* _method_callback_call (Callable const& callable, service::ins const&, service::outs const& , eina::index_sequence, eina::index_sequence , eina::index_sequence , Eldbus_Service_Interface const* iface, Eldbus_Message const* message , long) { typedef std::tuple tuple_ins; typename _detail::raw_tuple::type tuple_ins_raw; _detail::_init_raw_tuple<0u, tuple_ins> (eldbus_message_iter_get(message), tuple_ins_raw , std::integral_constant::value == 0u)>()); typedef std::tuple tuple_outs_type; tuple_outs_type tuple_outs; eldbus::const_message msg( ::eldbus_message_ref(const_cast(message))); eldbus::service_interface siface(iface); // If you have an error here, then you probably got your ins<...> // and/or outs<...> different from the arguments of your callable callable(msg, siface, std::get(tuple_ins_raw)..., &std::get(tuple_outs)...); Eldbus_Message *reply_ = eldbus_message_method_return_new(message); _detail::_append_tuple<0u> (reply_, tuple_outs , std::integral_constant::value == 0u>()); return reply_; } template Eldbus_Message* _method_callback(void* data, Eldbus_Service_Interface const* iface, Eldbus_Message const* msg) { std::tuple* tuple = static_cast*>(data); return _method_callback_call(std::get<0u>(*tuple), std::get<1u>(*tuple), std::get<2u>(*tuple) , eina::make_index_sequence ::value>() , eina::make_index_sequence ::value>() , eina::pop_integer_sequence , eina::make_index_sequence ::value> >() , iface, msg, 0); } template void _create_methods_specification_impl(Method const& method, Eldbus_Method2& eldbus_method, std::false_type) { std::array* in_params = new std::array; eldbus::_fill_methods(*in_params, method.ins); std::array* out_params = new std::array; eldbus::_fill_methods(*out_params, method.outs); // NOTE: C pointer magic performed under the hood requires this conversion // between incompatible function pointer types. // C++ always raises a warning for such conversions, so this warning // can be disabled just here. #pragma GCC diagnostic push #if !defined(__clang__) && __GNUC__ >= 8 #pragma GCC diagnostic ignored "-Wcast-function-type" #endif Eldbus_Method_Cb method_cb = reinterpret_cast (static_cast (&_method_callback)); #pragma GCC diagnostic pop eldbus_method = {{method.name, &(*in_params)[0], &(*out_params)[0] , method_cb , ELDBUS_METHOD_FLAG_HAS_DATA} , new std::tuple(std::move(method.f), method.ins, method.outs)}; } template void _create_methods_specification_impl(Method const& method, Eldbus_Method2& eldbus_method, std::true_type) { std::array* in_params = new std::array; eldbus::_fill_methods(*in_params, method.ins); std::array* out_params = new std::array; eldbus::_fill_methods(*out_params, method.outs); eldbus_method = {method.name, &(*in_params)[0], &(*out_params)[0], method.f, 0, 0}; } template void _create_methods_specification(Tuple const& tuple , std::array::value+1>& methods , std::integral_constant , std::false_type) { typedef Eldbus_Message*(*function_type)(Eldbus_Service_Interface const*, Eldbus_Message const*); typedef typename std::tuple_element::type method_type; _create_methods_specification_impl(std::get(tuple), methods[I] , std::is_convertible()); return _create_methods_specification(tuple, methods , std::integral_constant() , std::integral_constant::value>()); }; template std::array::value+1> _create_methods_specification (Tuple const& tuple ) { typedef std::tuple_size tuple_size; std::array array; _create_methods_specification(tuple, array , std::integral_constant() , std::integral_constant::value>()); return array; }; template service_interface service_interface_register(connection& c, const char* path , const char* interface , Args... args ) { std::array* methods = new std::array ( _create_methods_specification(std::make_tuple(args...)) ); Eldbus_Service_Interface_Desc2 description = { {interface, 0, 0, 0, 0, 0}, ELDBUS_INTERFACE_DESCRIPTOR_VERSION, &(*methods)[0] }; Eldbus_Service_Interface* iface = ::eldbus_service_interface_register2(c.native_handle(), path, &description); return service_interface(iface); } } } #endif