forked from enlightenment/efl
407 lines
14 KiB
C++
407 lines
14 KiB
C++
/*
|
|
* 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
|