forked from enlightenment/efl
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
406 lines
14 KiB
406 lines
14 KiB
/* |
|
* 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 <array> |
|
|
|
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 <typename F, typename Ins, typename Outs> |
|
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 <typename T> |
|
struct const_char_type |
|
{ |
|
typedef const char* type; |
|
}; |
|
|
|
} |
|
|
|
template <typename... Args> |
|
struct ins |
|
{ |
|
ins(typename _detail::const_char_type<Args>::type... names) : _names({{names...}}) {} |
|
|
|
static constexpr std::size_t size() { return sizeof...(Args); } |
|
|
|
typedef std::tuple<Args...> types; |
|
std::array<const char*, sizeof...(Args)> _names; |
|
}; |
|
|
|
template <typename... Args> |
|
struct outs |
|
{ |
|
outs(typename _detail::const_char_type<Args>::type... names) : _names({{names...}}) {} |
|
|
|
static constexpr std::size_t size() { return sizeof...(Args); } |
|
|
|
typedef std::tuple<Args...> types; |
|
std::array<const char*, sizeof...(Args)> _names; |
|
}; |
|
|
|
void foo(int); |
|
|
|
template <typename F, typename Ins, typename Outs> |
|
method_specification<F, Ins, Outs> |
|
method(const char* name, F f, Ins ins, Outs outs) |
|
{ |
|
return method_specification<F, Ins, Outs> |
|
{name, f, ins, outs} |
|
; |
|
} |
|
|
|
template <typename F, typename... Outs> |
|
method_specification<F, service::ins<>, service::outs<Outs...> > |
|
method(const char* name, F f, service::outs<Outs...> outs) |
|
{ |
|
return method_specification<F, service::ins<>, service::outs<Outs...> > |
|
{name, f, service::ins<>(), outs} |
|
; |
|
} |
|
|
|
template <typename F, typename... Ins> |
|
method_specification<F, service::ins<Ins...>, service::outs<> > |
|
method(const char* name, F f, service::ins<Ins...> ins) |
|
{ |
|
return method_specification<F, service::ins<Ins...>, service::outs<> > |
|
{name, f, ins, service::outs<>()} |
|
; |
|
} |
|
|
|
} |
|
|
|
template <std::size_t I, std::size_t N, template <typename...> class params |
|
, typename ... Args> |
|
int _fill_method_impl(std::array<Eldbus_Arg_Info, N>& array |
|
, params<Args...> const& |
|
, std::true_type) |
|
{ |
|
array[I] = Eldbus_Arg_Info {0, 0}; |
|
return 0; |
|
} |
|
|
|
|
|
template <std::size_t I, std::size_t N, template <typename...> class params |
|
, typename ... Args> |
|
int _fill_method_impl(std::array<Eldbus_Arg_Info, N>& array |
|
, params<Args...> const& directional_methods |
|
, std::false_type) |
|
{ |
|
typedef std::tuple<Args...> params_type; |
|
static const char sig[2] |
|
= {_detail::signature_traits |
|
< |
|
typename std::tuple_element<I, params_type>::type |
|
>::sig |
|
, '\0' |
|
}; |
|
array[I] = {sig |
|
, directional_methods._names[I] |
|
}; |
|
return 0; |
|
} |
|
|
|
|
|
template <std::size_t I, std::size_t N, template <typename...> class params |
|
, typename ... Args> |
|
int _fill_method(std::array<Eldbus_Arg_Info, N>& array |
|
, params<Args...> const& directional_methods) |
|
{ |
|
_fill_method_impl<I>(array, directional_methods |
|
, std::integral_constant<bool, N-1 == I>()); |
|
return 0; |
|
} |
|
|
|
template <typename... T> |
|
void _foo(T...) {} |
|
|
|
template <std::size_t N, template <typename...> class params, typename ... Args |
|
, std::size_t... Seq> |
|
void _fill_methods_impl(std::array<Eldbus_Arg_Info, N>& array |
|
, params<Args...> const& directional_methods |
|
, eina::index_sequence<Seq...> |
|
) |
|
{ |
|
eldbus::_foo(eldbus::_fill_method<Seq>(array, directional_methods) ...); |
|
} |
|
|
|
template <std::size_t N, template <typename...> class params, typename ... Args> |
|
void _fill_methods(std::array<Eldbus_Arg_Info, N>& array |
|
, params<Args...> const& directional_methods) |
|
{ |
|
eldbus::_fill_methods_impl(array, directional_methods |
|
, eina::make_index_sequence<N>()); |
|
} |
|
|
|
template <typename Tuple, std::size_t I> |
|
void _create_methods_specification(Tuple const& |
|
, std::array<Eldbus_Method2, std::tuple_size<Tuple>::value+1>& methods |
|
, std::integral_constant<std::size_t, I> |
|
, std::true_type) |
|
{ |
|
methods[I] = Eldbus_Method2 {{0, 0, 0, 0, 0}, 0}; |
|
}; |
|
|
|
template <std::size_t, typename T> |
|
T get_in(Eldbus_Message const* msg) |
|
{ |
|
typename _detail::signature_traits<T>::raw_type object; |
|
|
|
const char sig[2] = {_detail::signature_traits<T>::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 <typename Callable, typename... Ins, typename... Outs |
|
, std::size_t... IndexIns, std::size_t... IndexOuts |
|
, std::size_t... IndexOuts2> |
|
Eldbus_Message* _method_callback_call |
|
(Callable const& callable, service::ins<Ins...> const&, service::outs<Outs...> const& |
|
, eina::index_sequence<IndexIns...>, eina::index_sequence<IndexOuts...> |
|
, eina::index_sequence<IndexOuts2...> |
|
, Eldbus_Service_Interface const* iface, Eldbus_Message const* message |
|
, typename std::enable_if |
|
<!std::is_void |
|
<decltype(callable |
|
(*static_cast<eldbus::message*>(nullptr) |
|
, *static_cast<eldbus::service_interface*>(nullptr) |
|
, std::get<IndexIns>(std::tuple<Ins...>())... |
|
, &std::get<IndexOuts2> |
|
(*static_cast<std::tuple<Outs...>*>(nullptr))... |
|
) |
|
)>::value, int>::type) |
|
{ |
|
typedef std::tuple<Ins...> tuple_ins; |
|
typename _detail::raw_tuple<tuple_ins>::type tuple_ins_raw; |
|
_detail::_init_raw_tuple<0u, tuple_ins> |
|
(eldbus_message_iter_get(message), tuple_ins_raw |
|
, std::integral_constant<bool, (std::tuple_size<tuple_ins>::value == 0u)>()); |
|
|
|
std::tuple<Outs...> tuple_outs; |
|
|
|
eldbus::const_message msg( ::eldbus_message_ref(const_cast<Eldbus_Message*>(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<result_type> traits; |
|
std::get<0u>(tuple_outs) = |
|
callable(msg, siface, std::get<IndexIns>(tuple_ins_raw)..., &std::get<IndexOuts2>(tuple_outs)...); |
|
|
|
Eldbus_Message *reply_ = eldbus_message_method_return_new(message); |
|
_detail::_append_tuple<0u>(reply_, tuple_outs, std::false_type()); |
|
return reply_; |
|
} |
|
|
|
template <typename Callable, typename... Ins, typename... Outs |
|
, std::size_t... IndexIns, std::size_t... IndexOuts |
|
, std::size_t... IndexOuts2> |
|
Eldbus_Message* _method_callback_call |
|
(Callable const& callable, service::ins<Ins...> const&, service::outs<Outs...> const& |
|
, eina::index_sequence<IndexIns...>, eina::index_sequence<IndexOuts...> |
|
, eina::index_sequence<IndexOuts2...> |
|
, Eldbus_Service_Interface const* iface, Eldbus_Message const* message |
|
, long) |
|
{ |
|
typedef std::tuple<Ins...> tuple_ins; |
|
typename _detail::raw_tuple<tuple_ins>::type tuple_ins_raw; |
|
_detail::_init_raw_tuple<0u, tuple_ins> |
|
(eldbus_message_iter_get(message), tuple_ins_raw |
|
, std::integral_constant<bool, (std::tuple_size<tuple_ins>::value == 0u)>()); |
|
|
|
typedef std::tuple<Outs...> tuple_outs_type; |
|
tuple_outs_type tuple_outs; |
|
|
|
eldbus::const_message msg( ::eldbus_message_ref(const_cast<Eldbus_Message*>(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<IndexIns>(tuple_ins_raw)..., &std::get<IndexOuts>(tuple_outs)...); |
|
|
|
Eldbus_Message *reply_ = eldbus_message_method_return_new(message); |
|
_detail::_append_tuple<0u> |
|
(reply_, tuple_outs |
|
, std::integral_constant<bool, std::tuple_size<tuple_outs_type>::value == 0u>()); |
|
return reply_; |
|
} |
|
|
|
template <typename Callable, typename Ins, typename Outs> |
|
Eldbus_Message* _method_callback(void* data, Eldbus_Service_Interface const* iface, Eldbus_Message const* msg) |
|
{ |
|
std::tuple<Callable, Ins, Outs>* |
|
tuple = static_cast<std::tuple<Callable, Ins, Outs>*>(data); |
|
|
|
return _method_callback_call(std::get<0u>(*tuple), std::get<1u>(*tuple), std::get<2u>(*tuple) |
|
, eina::make_index_sequence |
|
<std::tuple_size<typename Ins::types>::value>() |
|
, eina::make_index_sequence |
|
<std::tuple_size<typename Outs::types>::value>() |
|
, eina::pop_integer_sequence |
|
<eina::make_index_sequence<0u> |
|
, eina::make_index_sequence |
|
<std::tuple_size<typename Outs::types>::value> |
|
>() |
|
, iface, msg, 0); |
|
} |
|
|
|
template <typename Method> |
|
void _create_methods_specification_impl(Method const& method, Eldbus_Method2& eldbus_method, std::false_type) |
|
{ |
|
std::array<Eldbus_Arg_Info, Method::ins_type::size()+1>* |
|
in_params = new std::array<Eldbus_Arg_Info, Method::ins_type::size()+1>; |
|
|
|
eldbus::_fill_methods(*in_params, method.ins); |
|
|
|
std::array<Eldbus_Arg_Info, Method::outs_type::size()+1>* |
|
out_params = new std::array<Eldbus_Arg_Info, Method::outs_type::size()+1>; |
|
|
|
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<Eldbus_Method_Cb> |
|
(static_cast<Eldbus_Method_Data_Cb> |
|
(&_method_callback<typename Method::function_type |
|
, typename Method::ins_type, typename Method::outs_type>)); |
|
#pragma GCC diagnostic pop |
|
|
|
eldbus_method = {{method.name, &(*in_params)[0], &(*out_params)[0] |
|
, method_cb |
|
, ELDBUS_METHOD_FLAG_HAS_DATA} |
|
, new std::tuple<typename Method::function_type |
|
, typename Method::ins_type, typename Method::outs_type |
|
>(std::move(method.f), method.ins, method.outs)}; |
|
} |
|
|
|
template <typename Method> |
|
void _create_methods_specification_impl(Method const& method, Eldbus_Method2& eldbus_method, std::true_type) |
|
{ |
|
std::array<Eldbus_Arg_Info, Method::ins_type::size()+1>* |
|
in_params = new std::array<Eldbus_Arg_Info, Method::ins_type::size()+1>; |
|
|
|
eldbus::_fill_methods(*in_params, method.ins); |
|
|
|
std::array<Eldbus_Arg_Info, Method::outs_type::size()+1>* |
|
out_params = new std::array<Eldbus_Arg_Info, Method::outs_type::size()+1>; |
|
|
|
eldbus::_fill_methods(*out_params, method.outs); |
|
|
|
eldbus_method = {method.name, &(*in_params)[0], &(*out_params)[0], method.f, 0, 0}; |
|
} |
|
|
|
template <typename Tuple, std::size_t I> |
|
void _create_methods_specification(Tuple const& tuple |
|
, std::array<Eldbus_Method2, std::tuple_size<Tuple>::value+1>& methods |
|
, std::integral_constant<std::size_t, I> |
|
, std::false_type) |
|
{ |
|
typedef Eldbus_Message*(*function_type)(Eldbus_Service_Interface const*, Eldbus_Message const*); |
|
|
|
typedef typename std::tuple_element<I, Tuple>::type method_type; |
|
|
|
_create_methods_specification_impl(std::get<I>(tuple), methods[I] |
|
, std::is_convertible<typename method_type::function_type |
|
, function_type>()); |
|
|
|
return _create_methods_specification(tuple, methods |
|
, std::integral_constant<std::size_t, I+1>() |
|
, std::integral_constant<bool, I+1 == std::tuple_size<Tuple>::value>()); |
|
}; |
|
|
|
template <typename Tuple> |
|
std::array<Eldbus_Method2, std::tuple_size<Tuple>::value+1> _create_methods_specification |
|
(Tuple const& tuple |
|
) |
|
{ |
|
typedef std::tuple_size<Tuple> tuple_size; |
|
std::array<Eldbus_Method2, tuple_size::value+1> array; |
|
|
|
_create_methods_specification(tuple, array |
|
, std::integral_constant<std::size_t, 0u>() |
|
, std::integral_constant<bool, 0 == std::tuple_size<Tuple>::value>()); |
|
return array; |
|
}; |
|
|
|
template <typename... Args> |
|
service_interface service_interface_register(connection& c, const char* path |
|
, const char* interface |
|
, Args... args |
|
) |
|
{ |
|
std::array<Eldbus_Method2, sizeof...(Args) + 1>* methods |
|
= new std::array<Eldbus_Method2, sizeof...(Args) + 1> |
|
( |
|
_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
|
|
|