blob: 6fb5aabba6c444b9beffdbff90d70f0878aef40e [file] [log] [blame]
#pragma once
#include <sdbusplus/bus.hpp>
#include <sdbusplus/sdbus.hpp>
#include <type_traits>
namespace sdbusplus
{
namespace server
{
namespace object
{
// Forward declaration.
template <class... Args>
struct object;
namespace details
{
// Forward declaration.
template <class... Args>
struct compose;
/** Template to identify if an inheritance is a "normal" type or a nested
* sdbusplus::object.
*/
template <class T>
struct compose_inherit
{
// This is a normal type.
using type = T;
// Normal types need emit_added called, if possible.
template <class S>
static void maybe_emit_iface_added(S* obj)
{
if constexpr (has_emit_added<S>())
{
obj->S::emit_added();
}
}
// Test if emit_added() exists in T return std::true_type.
template <class S>
static constexpr auto has_emit_added_helper(int)
-> decltype(std::declval<S>().emit_added(), std::true_type{});
// If the above test fails, fall back to this to return std::false_type
template <class>
static constexpr std::false_type has_emit_added_helper(...);
// Invoke the test with an int so it first resolves to
// has_emit_added_helper(int), and when it fails, it resovles to
// has_emit_added_helper(...) thanks to SFINAE.
// So the return type is std::true_type if emit_added() exists in T and
// std::false_type otherwise.
template <class S>
using has_emit_added = decltype(has_emit_added_helper<S>(0));
};
/** Template specialization for the sdbusplus::object nesting. */
template <class... Args>
struct compose_inherit<object<Args...>>
{
// Unravel the inheritance with a recursive compose.
using type = compose<Args...>;
// Redirect the interface added to the composed parts.
template <class T>
static void maybe_emit_iface_added(T* obj)
{
obj->compose<Args...>::maybe_emit_iface_added();
}
};
/** std-style _t alias for compose_inherit. */
template <class... Args>
using compose_inherit_t = typename compose_inherit<Args...>::type;
/** Templates to allow multiple inheritance via template parameters.
*
* These allow an object to group multiple dbus interface bindings into a
* single class.
*/
template <class T, class... Rest>
struct compose_impl : compose_inherit_t<T>, compose_impl<Rest...>
{
compose_impl(bus_t& bus, const char* path) :
T(bus, path), compose_impl<Rest...>(bus, path)
{}
void maybe_emit_iface_added()
{
compose_inherit<T>::maybe_emit_iface_added(
static_cast<compose_inherit_t<T>*>(this));
compose_impl<Rest...>::maybe_emit_iface_added();
}
};
/** Specialization for single element. */
template <class T>
struct compose_impl<T> : compose_inherit_t<T>
{
compose_impl(bus_t& bus, const char* path) : compose_inherit_t<T>(bus, path)
{}
void maybe_emit_iface_added()
{
compose_inherit<T>::maybe_emit_iface_added(
static_cast<compose_inherit_t<T>*>(this));
}
};
/** Default compose operation for variadic arguments. */
template <class... Args>
struct compose : compose_impl<Args...>
{
compose(bus_t& bus, const char* path) : compose_impl<Args...>(bus, path) {}
friend struct compose_inherit<object<Args...>>;
protected:
void maybe_emit_iface_added()
{
compose_impl<Args...>::maybe_emit_iface_added();
};
};
/** Specialization for zero variadic arguments. */
template <>
struct compose<>
{
compose(bus_t& /*bus*/, const char* /*path*/) {}
protected:
void maybe_emit_iface_added(){};
};
} // namespace details
/** Class to compose multiple dbus interfaces and object signals.
*
* Any number of classes representing a dbus interface may be composed into
* a single dbus object. The interfaces will be created first and hooked
* into the object space via the 'add_object_vtable' calls. Afterwards,
* a signal will be emitted for the whole object to indicate all new
* interfaces via 'sd_bus_emit_object_added'.
*
* Similar, on destruction, the interfaces are removed (unref'd) and the
* 'sd_bus_emit_object_removed' signals are emitted.
*
*/
template <class... Args>
struct object :
details::compose<Args...>,
private sdbusplus::bus::details::bus_friend
{
/* Define all of the basic class operations:
* Not allowed:
* - Default constructor to avoid nullptrs.
* - Copy operations due to internal unique_ptr.
* - Move operations.
* Allowed:
* - Destructor.
*/
object() = delete;
object(const object&) = delete;
object& operator=(const object&) = delete;
object(object&&) = delete;
object& operator=(object&&) = delete;
enum class action
{
/** sd_bus_emit_object_{added, removed} */
emit_object_added,
/** sd_bus_emit_interfaces_{added, removed} */
emit_interface_added,
/** no automatic added signal, but sd_bus_emit_object_removed on
* destruct */
defer_emit,
/** no interface signals */
emit_no_signals,
};
/** Construct an 'object' on a bus with a path.
*
* @param[in] bus - The bus to place the object on.
* @param[in] path - The path the object resides at.
* @param[in] act - Set to the desired InterfacesAdded signal behavior.
*/
object(bus_t& bus, const char* path,
action act = action::emit_object_added) :
details::compose<Args...>(bus, path),
__sdbusplus_server_object_bus(get_busp(bus), bus.getInterface()),
__sdbusplus_server_object_path(path),
__sdbusplus_server_object_intf(bus.getInterface())
{
// Default ctor
check_action(act);
}
~object()
{
if (__sdbusplus_server_object_signalstate != action::emit_no_signals)
{
__sdbusplus_server_object_intf->sd_bus_emit_object_removed(
get_busp(__sdbusplus_server_object_bus),
__sdbusplus_server_object_path.c_str());
}
}
/** Emit the 'object-added' signal, if not already sent. */
void emit_object_added()
{
if (__sdbusplus_server_object_signalstate == action::defer_emit)
{
__sdbusplus_server_object_intf->sd_bus_emit_object_added(
get_busp(__sdbusplus_server_object_bus),
__sdbusplus_server_object_path.c_str());
__sdbusplus_server_object_signalstate = action::emit_object_added;
}
}
private:
// These member names are purposefully chosen as long and, hopefully,
// unique. Since an object is 'composed' via multiple-inheritance,
// all members need to have unique names to ensure there is no
// ambiguity.
bus_t __sdbusplus_server_object_bus;
std::string __sdbusplus_server_object_path;
action __sdbusplus_server_object_signalstate = action::defer_emit;
SdBusInterface* __sdbusplus_server_object_intf;
/** Check and run the action */
void check_action(action act)
{
switch (act)
{
case action::emit_object_added:
// We are wanting to call emit_object_added to set up in
// deferred state temporarily and then emit the signal.
__sdbusplus_server_object_signalstate = action::defer_emit;
emit_object_added();
break;
case action::emit_interface_added:
details::compose<Args...>::maybe_emit_iface_added();
// If we are emitting at an interface level, we should never
// also emit at the object level.
__sdbusplus_server_object_signalstate = action::emit_no_signals;
break;
case action::defer_emit:
__sdbusplus_server_object_signalstate = action::defer_emit;
break;
case action::emit_no_signals:
__sdbusplus_server_object_signalstate = action::emit_no_signals;
break;
}
}
};
} // namespace object
template <class... Args>
using object_t = object::object<Args...>;
} // namespace server
} // namespace sdbusplus