Skip to content

Commit

Permalink
runtime(DataBroker): Add SignalDescriptor
Browse files Browse the repository at this point in the history
  • Loading branch information
clsim committed Oct 20, 2023
1 parent c76a9dc commit ea0c075
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 1 deletion.
195 changes: 194 additions & 1 deletion runtime/include/cloe/data_broker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ struct LuaAutocompletionTag : MetaInformation::Tag<LuaAutocompletionTag> {
};

/**
* Signal-Descriptor
* Signal represents the properties of a signal at runtime
*
* \note: Design-Goals:
* - Design-#1: The class shall expose a uniform interface (via type-erasure)
Expand Down Expand Up @@ -1186,6 +1186,29 @@ std::size_t BasicContainer<T>::subscriber_count() const {
return signal_ != nullptr ? signal_->subscriber_count() : 0;
}

/**
* TypedSignal decorates Signal with a specific datatype.
*/
template <typename T>
class TypedSignal {
private:
SignalPtr signal_;

public:
TypedSignal(SignalPtr signal) : signal_{signal} {}
~TypedSignal() = default;

operator SignalPtr&() { return signal_; }
operator const SignalPtr&() const { return signal_; }

const T& value() const { return signal_->template value<T>(); }

template <typename TSetter>
void set_setter(TSetter setter) {
signal_->template set_setter<T>(std::move(setter));
}
};

/**
* Registry for type-erased signals.
*/
Expand Down Expand Up @@ -1686,6 +1709,20 @@ class DataBroker {
}
return *getter_fn;
}
/**
* Sets the getter-function of a signal.
*
* \tparam T Type of the signal
* \param name Name of the signal
* \param getter_fn getter-function of the signal
*/
template <typename T>
void set_getter(std::string_view name, const Signal::typed_get_value_function_t<T>& getter_fn) {
assert_static_type<T>();
using compatible_type = databroker::compatible_base_t<T>;

signal(name)->set_getter<compatible_type>(getter_fn);
}

/**
* Return the setter-function of a signal.
Expand All @@ -1706,8 +1743,164 @@ class DataBroker {
}
return *setter_fn;
}
/**
* Sets the setter-function of a signal.
*
* \tparam T Type of the signal
* \param name Name of the signal
* \param getter_fn setter-function of the signal
*/
template <typename T>
void set_setter(std::string_view name, const Signal::typed_set_value_function_t<T>& setter_fn) {
assert_static_type<T>();
using compatible_type = databroker::compatible_base_t<T>;

signal(name)->set_setter<compatible_type>(setter_fn);
}
};

namespace databroker {

/**
* Tags a signal
*/
template <typename T>
struct SignalDescriptorBase {
using type = T;
};
template <typename T, const char*...>
struct SignalDescriptorImpl : public SignalDescriptorBase<T> {
using type = typename SignalDescriptorBase<T>::type;

template <typename TFirstName, typename... TNames>
static auto implement(DataBroker& db, TFirstName firstName, TNames... names) {
auto signal = TypedSignal<T>(db.implement<type>(firstName));
((db.alias(signal, names)), ...);
return signal;
}
template <typename TFirstName, typename... TNames>
static auto declare(DataBroker& db, TFirstName firstName, TNames... names) {
auto signal = TypedSignal<T>(db.declare<type>(firstName));
((db.alias(signal, names)), ...);
return signal;
}
};

template <typename T, const char* FIRSTNAME, const char*... NAMES>
struct SignalDescriptorImpl<T, FIRSTNAME, NAMES...> : public SignalDescriptorBase<T> {
using type = typename SignalDescriptorBase<T>::type;

static constexpr const char* Name() { return FIRSTNAME; }

/**
* Implements the signal
*
* \param db Instance of the DataBroker
* \return Container<type>, the container of the signal
*/
static auto implement(DataBroker& db) {
auto container = db.implement<type>(Name());
auto signal = db.signal(Name());
((db.alias(signal, NAMES)), ...);
return container;
}
/**
* Declares the signal
*
* \param db Instance of the DataBroker
* \return TypeSignal<type>, the signal
*/
static auto declare(DataBroker& db) {
auto signal = TypedSignal<T>(db.declare<type>(Name()));
((db.alias(signal, NAMES)), ...);
return TypedSignal<type>(std::move(signal));
}

/**
* Return the getter-function of a signal.
*
* \param db Instance of the DataBroker
* \return TypeSignal<type>, the signal
*/
static auto signal(DataBroker& db) {
auto signal = db.signal(Name());
return TypedSignal<type>(std::move(signal));
}

/**
* Return the getter-function of a signal.
*
* \param db Instance of the DataBroker
* \return const Signal::typed_get_value_function_t<type>&, getter-function of the signal
*/
static auto getter(DataBroker& db) { return db.getter<type>(Name()); }
/**
* Sets the getter-function of a signal.
*
* \param db Instance of the DataBroker
* \param get_value_fn getter-function of the signal
*/
static void set_getter(DataBroker& db, Signal::typed_get_value_function_t<type> get_value_fn) {
db.set_getter<type>(Name(), std::move(get_value_fn));
}
/**
* Return the setter-function of a signal.
*
* \param db Instance of the DataBroker
* \return const Signal::typed_set_value_function_t<type>&, setter-function of the signal
*/
static auto setter(DataBroker& db) { return db.setter<type>(Name()); }
/**
* Sets the setter-function of a signal.
*
* \param db Instance of the DataBroker
* \param set_value_fn setter-function of the signal
*/
static void set_setter(DataBroker& db, Signal::typed_set_value_function_t<type> set_value_fn) {
db.set_setter<type>(Name(), std::move(set_value_fn));
}

/**
* Return the value of a signal.
*
* \param db Instance of the DataBroker
* \return Pointer to the value of the signal, nullptr if the signal does not exist
*/
static auto value(DataBroker& db) { return db.value<type>(Name()); }
/**
* Set the value of a signal.
*
* \param db Instance of the DataBroker
* \param value Value to be assigned to the signal
*/
static auto set_value(DataBroker& db, const T& value) { db.set_value<type>(value); }
};

/**
* SignalDescriptor reflects properties of a signal at compile-time
*
* \note: Design-Goals:
* - Design-#1: Datatype of a signal shall be available at compile time
* \note: Remarks:
* - The declaration of a descriptor does not imply the availability of the coresponding signal at runtime.
* Likewise a C/C++ header does not imply that the coresponding symbols can be resolved at runtime.
*/
template <typename T, const char*... NAMES>
struct SignalDescriptor : public SignalDescriptorImpl<T, NAMES...> {
using type = typename SignalDescriptorImpl<T, NAMES...>::type;
};

template <typename T>
struct IsSignalDescriptor : std::false_type {};
template <typename T, const char*... NAMES>
struct IsSignalDescriptor<SignalDescriptor<T, NAMES...>> : std::true_type {};
template <typename T>
constexpr bool is_signal_descriptor_v() {
return IsSignalDescriptor<T>::value;
}

} // namespace databroker

// Design-Goals:
// -g1: Prepare for mass usage of Signals, Containers & Databroker
// ==> Allow instantiation of all templates for relevant datatypes
Expand Down
25 changes: 25 additions & 0 deletions runtime/src/cloe/data_broker_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -969,3 +969,28 @@ TEST(metainformations, metainformation_4) {
//EXPECT_EQ(*metainformations.get<shared_tag_1>(), metainformation);
EXPECT_EQ(metainformations.get<shared_tag_2>()->get(), metainformation);
}

// Test Scenario: positive-test
// Test Case Description: Statically declare & obtain a signal via a descriptor
// Test Steps: 1) Create a signal via a SignalDescriptor
// 2) Obtain a signal via a SignalDescriptor
// Prerequisite: -
// Test Data: -
// Expected Result: I) The value of the tag is unchanged

using signal_type = int;
// using signal_type = std::optional<int>;
char int1_name[] = "int_1";
struct signal_int1 : ::cloe::databroker::SignalDescriptor<signal_type, int1_name> {};
char int2_name[] = "int_2";
struct signal_int2 : ::cloe::databroker::SignalDescriptor<signal_type, int2_name> {};

TEST(descriptors, descriptors_1) {
DataBroker db;

// step 1
signal_int1::declare(db);
// step 2
auto signal1 = signal_int1::signal(db);
EXPECT_THROW(signal_int2::signal(db), std::out_of_range);
}

0 comments on commit ea0c075

Please sign in to comment.