diff --git a/runtime/include/cloe/data_broker.hpp b/runtime/include/cloe/data_broker.hpp index 0ac73cfdf..e8729e6d0 100644 --- a/runtime/include/cloe/data_broker.hpp +++ b/runtime/include/cloe/data_broker.hpp @@ -688,7 +688,7 @@ struct LuaAutocompletionTag : MetaInformation::Tag { }; /** - * 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) @@ -1186,6 +1186,29 @@ std::size_t BasicContainer::subscriber_count() const { return signal_ != nullptr ? signal_->subscriber_count() : 0; } +/** + * TypedSignal decorates Signal with a specific datatype. + */ +template +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(); } + + template + void set_setter(TSetter setter) { + signal_->template set_setter(std::move(setter)); + } +}; + /** * Registry for type-erased signals. */ @@ -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 + void set_getter(std::string_view name, const Signal::typed_get_value_function_t& getter_fn) { + assert_static_type(); + using compatible_type = databroker::compatible_base_t; + + signal(name)->set_getter(getter_fn); + } /** * Return the setter-function of a signal. @@ -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 + void set_setter(std::string_view name, const Signal::typed_set_value_function_t& setter_fn) { + assert_static_type(); + using compatible_type = databroker::compatible_base_t; + + signal(name)->set_setter(setter_fn); + } }; +namespace databroker { + +/** + * Tags a signal + */ +template +struct SignalDescriptorBase { + using type = T; +}; +template +struct SignalDescriptorImpl : public SignalDescriptorBase { + using type = typename SignalDescriptorBase::type; + + template + static auto implement(DataBroker& db, TFirstName firstName, TNames... names) { + auto signal = TypedSignal(db.implement(firstName)); + ((db.alias(signal, names)), ...); + return signal; + } + template + static auto declare(DataBroker& db, TFirstName firstName, TNames... names) { + auto signal = TypedSignal(db.declare(firstName)); + ((db.alias(signal, names)), ...); + return signal; + } +}; + +template +struct SignalDescriptorImpl : public SignalDescriptorBase { + using type = typename SignalDescriptorBase::type; + + static constexpr const char* Name() { return FIRSTNAME; } + + /** + * Implements the signal + * + * \param db Instance of the DataBroker + * \return Container, the container of the signal + */ + static auto implement(DataBroker& db) { + auto container = db.implement(Name()); + auto signal = db.signal(Name()); + ((db.alias(signal, NAMES)), ...); + return container; + } + /** + * Declares the signal + * + * \param db Instance of the DataBroker + * \return TypeSignal, the signal + */ + static auto declare(DataBroker& db) { + auto signal = TypedSignal(db.declare(Name())); + ((db.alias(signal, NAMES)), ...); + return TypedSignal(std::move(signal)); + } + + /** + * Return the getter-function of a signal. + * + * \param db Instance of the DataBroker + * \return TypeSignal, the signal + */ + static auto signal(DataBroker& db) { + auto signal = db.signal(Name()); + return TypedSignal(std::move(signal)); + } + + /** + * Return the getter-function of a signal. + * + * \param db Instance of the DataBroker + * \return const Signal::typed_get_value_function_t&, getter-function of the signal + */ + static auto getter(DataBroker& db) { return db.getter(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 get_value_fn) { + db.set_getter(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&, setter-function of the signal + */ + static auto setter(DataBroker& db) { return db.setter(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 set_value_fn) { + db.set_setter(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(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(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 +struct SignalDescriptor : public SignalDescriptorImpl { + using type = typename SignalDescriptorImpl::type; +}; + +template +struct IsSignalDescriptor : std::false_type {}; +template +struct IsSignalDescriptor> : std::true_type {}; +template +constexpr bool is_signal_descriptor_v() { + return IsSignalDescriptor::value; +} + +} // namespace databroker + // Design-Goals: // -g1: Prepare for mass usage of Signals, Containers & Databroker // ==> Allow instantiation of all templates for relevant datatypes diff --git a/runtime/src/cloe/data_broker_test.cpp b/runtime/src/cloe/data_broker_test.cpp index cc0deed70..6a4359951 100644 --- a/runtime/src/cloe/data_broker_test.cpp +++ b/runtime/src/cloe/data_broker_test.cpp @@ -969,3 +969,28 @@ TEST(metainformations, metainformation_4) { //EXPECT_EQ(*metainformations.get(), metainformation); EXPECT_EQ(metainformations.get()->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; +char int1_name[] = "int_1"; +struct signal_int1 : ::cloe::databroker::SignalDescriptor {}; +char int2_name[] = "int_2"; +struct signal_int2 : ::cloe::databroker::SignalDescriptor {}; + +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); +}