diff --git a/include/rosa/config/config.h b/include/rosa/config/config.h index 53f273d..6090667 100644 --- a/include/rosa/config/config.h +++ b/include/rosa/config/config.h @@ -1,78 +1,86 @@ //===-- rosa/config/config.h ------------------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/config/config.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Configuration information on the build of the library. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CONFIG_CONFIG_H #define ROSA_CONFIG_CONFIG_H #include "rosa/config/rosa_config.h" #include // This OS-specific block defines one of the following: // - ROSA_LINUX // - ROSA_WINDOWS // It also defines ROSA_POSIX for POSIX-compatible systems. #if defined(__linux__) #define ROSA_LINUX #elif defined(WIN32) || defined(_WIN32) #define ROSA_WINDOWS #else #error Platform and/or compiler not supported #endif #if defined(ROSA_LINUX) #define ROSA_POSIX #endif // Defining filenames in a project-relative way based on absolute paths. #include "rosa/config/project_path.hpp" /// The project-relative path of the current source file. #define __FILENAME__ (__FILE__ + project_relative_path_index(__FILE__)) // Convenience macros. /// No-op. #define ROSA_VOID_STMT static_cast(0) -/// Ignors anything. +/// Ignors an expression. /// -/// \param x anything -#define ROSA_IGNORE_UNUSED(x) static_cast(x) +/// \param x expression +#define ROSA_IGNORE_UNUSED_EXPR(x) static_cast(x) + +/// Ignors a statement (not an expression). +/// +/// \param s statement +#define ROSA_IGNORE_UNUSED_STMT(s) \ + while (false) { \ + s; \ + } \ /// Prints an error message and aborts execution. /// /// \param error the error message to print #define ROSA_CRITICAL(error) \ do { \ std::cerr << "[FATAL] " << __func__ << "@" << __FILENAME__ << ":" \ << __LINE__ << ": critical error: '" << (error) << "'" \ << std::endl; \ ::abort(); \ } while (false) /// Raises a runtime error in the program. /// /// \param msg message describing the error /// /// \throws std::runtime_error #define ROSA_RAISE_ERROR(msg) throw std::runtime_error(msg) #endif // ROSA_CONFIG_CONFIG_H diff --git a/include/rosa/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp index 15e9a81..afa2693 100644 --- a/include/rosa/deluxe/DeluxeSensor.hpp +++ b/include/rosa/deluxe/DeluxeSensor.hpp @@ -1,674 +1,674 @@ //===-- rosa/deluxe/DeluxeSensor.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSensor.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::Agent for *sensor* role of the the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESENSOR_HPP #define ROSA_DELUXE_DELUXESENSOR_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" #include "rosa/deluxe/DeluxeExecutionPolicy.h" #include "rosa/deluxe/DeluxeTuple.hpp" /// Local helper macros to deal with built-in types. /// ///@{ /// Creates function name for member functions in \c rosa::deluxe::DeluxeSensor. /// /// \param N name suffix to use #define DSMASTERHANDLERNAME(N) handleMaster_##N /// Defines member functions for handling messages from *master* in /// \c rosa::deluxe::DeluxeSensor. /// /// \see \c DeluxeSensorMasterInputHandlers /// /// \note No pre- and post-conditions are validated directly by these functions, /// they rather rely on \c rosa::deluxe::DeluxeSensor::saveMasterInput to do /// that. /// /// \param T the type of input to handle /// \param N name suffix for the function identifier #define DSMASTERHANDLERDEFN(T, N) \ void DSMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \ T Value) noexcept { \ saveMasterInput(MasterId, Pos, Value); \ } /// Convenience macro for \c DSMASTERHANDLERDEFN with identical arguments. /// /// \see \c DSMASTERHANDLERDEFN /// /// This macro can be used instead of \c DSMASTERHANDLERDEFN if the actual value /// of \p T can be used as a part of a valid identifier. /// /// \param T the type of input to handle #define DSMASTERHANDLERDEF(T) DSMASTERHANDLERDEFN(T, T) /// Results in a \c THISMEMBER reference to a member function defined by /// \c DSMASTERHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeSensor to initialize super /// class \c rosa::Agent with member function defined by \c DSMASTERHANDLERDEFN. /// /// \see \c DSMASTERHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier #define DSMASTERHANDLERREF(N) THISMEMBER(DSMASTERHANDLERNAME(N)) ///@} namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *sensor* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// /// \invariant There is a compatible *execution policy* set; the actual value in /// \c rosa::deluxe::DeluxeSensor::MasterInputNextPos is valid with respect to /// the corresponding types. /// /// \see Definition of \c rosa::deluxe::DeluxeSensor::inv on the class invariant /// /// \note All member functions validate the class invariant as part of their /// precondition. Moreover, non-const functions validate the invariant before /// return as their postcondition. class DeluxeSensor : public Agent { /// Checks whether \p this object holds the class invariant. /// /// \see Invariant of the class \c rosa::deluxe::DeluxeSensor /// /// \return if \p this object holds the class invariant bool inv(void) const noexcept; /// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of /// \c this object. std::unique_ptr ExecutionPolicy; public: /// The type of values produced by \p this object. /// /// That is the types of values \p this object sends to its *master* in a /// \c rosa::deluxe::DeluxeTuple. /// /// \see \c rosa::deluxe::DeluxeSensor::master const Token OutputType; /// The type of values \p this object processes from its *master*. /// /// That is the types of values \p this object receives from its *master* in a /// \c rosa::deluxe::DeluxeTuple. /// /// \see \c rosa::deluxe::DeluxeSensor::master const Token MasterInputType; private: /// Indicates which element of the master-input is expected from the *master*. /// /// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value /// element by element in their order of definition. This member field tells /// the element at which position should be received next. /// /// \p this object is supposed to be triggered only when a complete /// master-input has been received, that is the field should hold the value /// `0`. /// /// \see \c rosa::deluxe::DeluxeSensor::handleTrigger /// \c rosa::deluxe::DeluxeSensor::saveMasterInput token_size_t MasterInputNextPos; /// Indicates whether the input value from the *master* has been changed since /// the last trigger received from the system. /// /// The flag is reset to \c false upon handling a trigger and then set to \c /// true by \c rosa::deluxe::DeluxeSensor::saveMasterInput when storig a new /// input value in \c rosa::deluxe::DeluxeSensor::MasterInputValue. bool MasterInputChanged; /// Stores the actual input value from *master*. /// /// \note The type of the stored value matches the types indicated by \c /// rosa::deluxe::DeluxeSensor::MasterInputType. const std::unique_ptr MasterInputValue; /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeSensor. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c DeluxeSensorTriggerHandlers using H = std::function; /// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of /// rosa::deluxe::DeluxeSensor /// /// \brief Trigger handler functions of \c rosa::deluxe::DeluxeSensor /// /// The actual data source functions and master-input processing function are /// captured in lambda expressions that are in turn wrapped in \c /// std::function objects. The lambda expression calls a processing function, /// either to handle master-input or obtain the next sensory value from data /// source. The next sensory value is sent it to *master* by calling \c /// rosa::deluxe::DeluxeSensor::sendToMaster. Also, the flag \c /// rosa::deluxe::DeluxeSensor::MasterInputChanged is reset when the current /// value is passed to the master-input processing function. The function \c /// rosa::deluxe::DeluxeSensor::handleTrigger needs only to call the proper /// function object. /// Processes master-input. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is called upon the sensor is trigged by the system. const H MFP; /// Produces the next sensory value during normal execution. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is used during normal execution. During simulation, the /// simulation environment sets \c rosa::deluxe::DeluxeSensor::SFP, which is /// used instead of \c rosa::deluxe::DeluxeSensor::FP. const H FP; /// Produces the next sensory value during simulation. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is empty by default. The simulation environment sets it to be /// used during simulation. H SFP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeSensor instance does not have any *master* at a /// given moment. Optional Master; /// Tells the unique identifier of the *master* of \p this object, if any /// registered. /// /// \return the unique identifier of the *master* /// /// \pre A *master* is registered for \p this object: \code /// Master /// \endcode id_t masterId(void) const noexcept; /// Wraps a master-input processing function into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeSensor::MFP and \c DeluxeSensorTriggerHandlers /// /// \tparam Ts types of elements of master-input processed by \p MF /// \tparam S0 indices for accessing master-input values /// /// \param MF function that processes master-input /// /// \note The second argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates /// that \p this object does not receive master-input, \p MF is never called /// if \p Ts is empty. /// /// \return trigger handler function based on \p MF /// /// \pre Statically, the indices match the elements: \code /// sizeof...(Ts) == sizeof...(S0) /// \endcode Dynamically, \p Ts... match \c /// rosa::deluxe::DeluxeSensor::MasterInputType: \code /// MasterInputType == DeluxeTuple::TT /// \endcode template H triggerHandlerFromProcessingFunction( std::function, bool>)> &&MF, Seq) noexcept; /// Wraps a data source function into a trigger handler. /// /// \see \c rosa::deluxe::DeluxeSensor::FP, \c /// rosa::deluxe::DeluxeSensor::SFP, and \c DeluxeSensorTriggerHandlers /// /// \tparam T type of data provided by \p F /// /// \param F function to generate value with /// \param inSimulation if F is a data source for Simulation /// /// \return trigger handler function based on \p F /// /// \pre Statically, the type agument \p T is an instance of \c /// rosa::deluxe::DeluxeTuple: \code /// IsDeluxeTuple::Value /// \endcode Dynamically, \p T matches \c /// rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == T::TT /// \endcode template H triggerHandlerFromDataSource(std::function &&F, bool inSimulation) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// /// \todo Enforce \p F and \p MF do not potentially throw exception. /// /// \tparam MT type of master-input handled by \p MF /// \tparam T type of data to operate on /// /// \note Instantiation fails if any of the type arguments \p MT and \p T is /// not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c /// rosa::deluxe::EmptyDeluxeTuple. /// /// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed /// object does not receive master-input. /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance /// \param MF function to process master-input values with /// \param F function to generate the next value with during normal operation /// /// \pre Statically, \p MT and \p T are instances of \c /// rosa::deluxe::DeluxeTuple and \p T contains at least one element:\code /// TypeListAllDeluxeTuple>::Value && T::Length > 0 /// \endcode /// Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::SensorKind: /// \code /// Kind == rosa::deluxe::atoms::SensorKind /// \endcode /// /// \see \c rosa::deluxe::DeluxeTuple template < typename MT, typename T, typename = std::enable_if_t< TypeListAllDeluxeTuple>::Value && (T::Length > 0)>> DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function)> &&MF, std::function &&F) noexcept; /// Destroys \p this object. ~DeluxeSensor(void) noexcept; /// Returns the current execution policy of \p this object. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note The returned reference is valid only as long as \c /// rosa::deluxe::DeluxeSensor::setExecutionPolicy() is not called and \p this /// object is not destroyed. /// /// \return \c rosa::deluxe::DeluxeSensor::ExecutionPolicy const DeluxeExecutionPolicy &executionPolicy(void) const noexcept; /// Sets the current execution policy of \p this object to \p EP. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note \p EP is set only if it can handle \p this object. /// /// \param EP the new execution policy for \p this object /// /// \return if \p EP was successfully set for \p this object. bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; /// The *master* of \p this object, if any. /// /// \see \c rosa::deluxe::DeluxeSensor::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// /// \note Any call to \c rosa::deluxe::DeluxeSensor::registerMaster should be /// paired with a corresponding call of \c /// rosa::deluxe::DeluxeAgent::registerSlave, which validates that /// input/output types of master and slave matches. /// /// \param _Master the *master* to register /// /// \pre \p Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; /// Clears the simulation trigger handler of \p this object. /// /// The function assigns \c rosa::deluxe::DeluxeSensor::SFP with \c nullptr. void clearSimulationDataSource(void) noexcept; /// Tells whether a simulation trigger handler is set for \p this object. /// /// The function returns whether \c rosa::deluxe::DeluxeSensor::SFP is not /// \c nullptr. /// /// \return if a simulation trigger handler is set for \p this object. bool simulationDataSourceIsSet(void) const noexcept; /// Registers a simulation data source for \p this object. /// /// A new simulation trigger handler wrapping \p SF is stored in /// \c rosa::deluxe::DeluxeSensor::SFP by overwriting any already registered /// simulation data source. /// /// \todo Enforce SF does not potentially throw exception. /// /// \tparam Ts types of elements of values provided by \p SF /// /// \param SF function to generate value with /// /// \pre \p Ts... match \c rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeToken::Value /// \endcode template void registerSimulationDataSource( std::function(void)> &&SF) noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeSensor::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// /// The elements from \p Value are sent one by one in separate messages to the /// *master*. /// /// \tparam Ts types of the elements in \p Value /// \tparam S0 indices for accessing elements of \p Value /// /// \param Value value to send /// /// \note The second argument provides indices statically as template /// arguments \p S0..., so its actual value is ignored. /// /// \pre Statically, the indices match the elements: \code /// sizeof...(Ts) == sizeof...(S0) /// \endcode Dynamically, \p Ts match \c /// rosa::deluxe::DeluxeSensor::OutputType: \code /// OutputType == TypeToken::Value /// \endcode template void sendToMaster(const DeluxeTuple &Value, Seq) noexcept; /// Handles master-input and generates the next sensory value upon trigger /// from the system. /// /// Executes \c rosa::deluxe::DeluxeSensor::MFP for processing master-input /// and data generating function \c rosa::deluxe::DeluxeSensor::FP or \c /// rosa::deluxe::DeluxeSensor::SFP if set. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. /// /// \pre Master-input is supposed to be completely received upon triggering: /// \code /// MasterInputNextPos == 0 /// \endcode void handleTrigger(atoms::Trigger) noexcept; /// Stores a new input value from the *master*. /// /// The function stores \p Value at position \p Pos in \c /// rosa::deluxe::DeluxeSensor::MasterInputValue and also sets the /// flag \c rosa::deluxe::DeluxeSensor::MasterInputChanged. The function also /// takes care of checking and updating \c /// rosa::deluxe::DeluxeSensor::MasterInputNextPos: increments its value and /// resets it to `0` when the last element is received. /// /// \note Utilized by member functions of group \c /// DeluxeSensorMasterInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of the *master* /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple /// \param Value the input value to store /// /// \pre The *master* with \p Id is registered, \p Pos is the expected /// position of master-input, and the input from the *master* at position \p /// Pos is expected to be of type \p T: \code /// Master && masterId() == Id && Pos == MasterInputNextPos && /// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value /// \endcode template void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept; /// \defgroup DeluxeSensorMasterInputHandlers Master-input handlers of /// rosa::deluxe::DeluxeSensor /// /// Definition of member functions handling messages from the *master* with /// different types of input /// /// A *slave* generally needs to be prepared to deal with values of any /// built-in type to handle messages from its *master*. Each type requires a /// separate message handler, which are implemented by these functions. The /// functions instantiate \c rosa::deluxe::DeluxeSensor::saveMasterInput with /// the proper template argument and pass the content of the message on for /// processing. /// /// \note The member functions in this group are defined by \c /// DSMASTERHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ DSMASTERHANDLERDEF(AtomValue) DSMASTERHANDLERDEF(int16_t) DSMASTERHANDLERDEF(int32_t) DSMASTERHANDLERDEF(int64_t) DSMASTERHANDLERDEF(int8_t) DSMASTERHANDLERDEFN(long double, long_double) DSMASTERHANDLERDEFN(std::string, std__string) DSMASTERHANDLERDEF(uint16_t) DSMASTERHANDLERDEF(uint32_t) DSMASTERHANDLERDEF(uint64_t) DSMASTERHANDLERDEF(uint8_t) DSMASTERHANDLERDEF(unit_t) DSMASTERHANDLERDEF(bool) DSMASTERHANDLERDEF(double) DSMASTERHANDLERDEF(float) /// @} }; template DeluxeSensor::H DeluxeSensor::triggerHandlerFromProcessingFunction( std::function, bool>)> &&MF, Seq) noexcept { using MT = DeluxeTuple; STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(MasterInputType == MT::TT); // NOTE: Clang 6 warns about unused lambda captures; we suppress that // warning (those variables need to be captured). #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-lambda-capture" #endif // defined __clang__ return [ this, MF ](void) noexcept { // Do not do anything for master-input type \c // rosa::deluxe::EmptyDeluxeTuple. if constexpr (!std::is_same::value) { LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " handles master-input." << std::endl; // The assert must hold if \p this object was successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); const auto MasterInputArg = std::make_pair( // Get all elements of the tuple in a fold expression. DeluxeTuple(*static_cast( MasterInputValue->pointerTo(static_cast(S0)))...), MasterInputChanged); MasterInputChanged = false; MF(MasterInputArg); } }; #ifdef __clang__ #pragma clang diagnostic pop #endif // defined __clang__ } template DeluxeSensor::H DeluxeSensor::triggerHandlerFromDataSource(std::function &&F, bool inSimulation) noexcept { STATIC_ASSERT(IsDeluxeTuple::Value, "not tuple type argument"); ASSERT(OutputType == T::TT); return [ this, F, inSimulation ](void) noexcept { // Get value and send it to master only if \p ExecutionPolicy allows it. if (ExecutionPolicy->shouldProcess({})) { LOG_TRACE_STREAM << "DeluxeSensor " << Name << " obtains next value." << std::endl; sendToMaster(F(), seq_t()); } else { LOG_TRACE_STREAM << "DeluxeSensor " << Name << " skips next value." << std::endl; if (inSimulation) { // But read input value in Simulation anyway as input values are // provided for the highest execution frequency for simulation (void)F(); } } }; } template DeluxeSensor::DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, std::function)> &&MF, std::function &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), DSMASTERHANDLERREF(AtomValue), DSMASTERHANDLERREF(int16_t), DSMASTERHANDLERREF(int32_t), DSMASTERHANDLERREF(int64_t), DSMASTERHANDLERREF(int8_t), DSMASTERHANDLERREF(long_double), DSMASTERHANDLERREF(std__string), DSMASTERHANDLERREF(uint16_t), DSMASTERHANDLERREF(uint32_t), DSMASTERHANDLERREF(uint64_t), DSMASTERHANDLERREF(uint8_t), DSMASTERHANDLERREF(unit_t), DSMASTERHANDLERREF(bool), DSMASTERHANDLERREF(double), DSMASTERHANDLERREF(float)), ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT), - MasterInputType(MT::TT), MasterInputChanged(false), + MasterInputType(MT::TT), MasterInputNextPos(0), MasterInputChanged(false), MasterInputValue(new typename TokenizedStorageForTypeList< typename UnwrapDeluxeTuple::Type>::Type()), MFP(triggerHandlerFromProcessingFunction(std::move(MF), seq_t())), FP(triggerHandlerFromDataSource(std::move(F), false)), SFP(nullptr) { ASSERT(Kind == atoms::SensorKind); LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " is created." << std::endl; ASSERT(inv()); } template void DeluxeSensor::registerSimulationDataSource( std::function(void)> &&SF) noexcept { ASSERT(OutputType == TypeToken::Value); SFP = triggerHandlerFromDataSource(std::move(SF), true); ASSERT(inv()); } template void DeluxeSensor::sendToMaster(const DeluxeTuple &Value, Seq) noexcept { STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); ASSERT(OutputType == TypeToken::Value); // The assert must hold if \p this object was successfuuly constructed. ASSERT((true && ... && (static_cast(static_cast(S0)) == S0))); // Create a static constant array for these indices to be available as lvalue // references when creating messages below. \c S0... when used directly in a // fold expression is a temporary value, which would result in \c // rosa::Message instances being created with rvalue references. Further, all // other values would to copied into a temporary variable for making them /// available as rvalue references (they are constant lvalue references here). static constexpr std::array Indices{{S0...}}; LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id << ") sends to master(" << static_cast(Master && *Master) << "): " << Value << std::endl; // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { // Handle each element of the tuple in a fold expression. (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0], std::get(Value))), ...); } ASSERT(inv()); } template void DeluxeSensor::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept { ASSERT(Master && masterId() == Id && Pos == MasterInputNextPos && typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id << ") saves value from master: (" << static_cast(Pos) << ") " << Value << std::endl; // Save value. *static_cast(MasterInputValue->pointerTo(Pos)) = Value; // Update position of next value. if (++MasterInputNextPos == lengthOfToken(MasterInputType)) { MasterInputNextPos = 0; } // Set flag. MasterInputChanged = true; } } // End namespace deluxe } // End namespace rosa #undef DSMASTERHANDLEREF #undef DSMASTERHANDLEDEF #undef DSMASTERHANDLEDEFN #undef DSMASTERHANDLENAME #endif // ROSA_DELUXE_DELUXESENSOR_HPP diff --git a/include/rosa/support/debug.hpp b/include/rosa/support/debug.hpp index c22dcea..c8230a9 100644 --- a/include/rosa/support/debug.hpp +++ b/include/rosa/support/debug.hpp @@ -1,133 +1,133 @@ //===-- rosa/support/debug.hpp ----------------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/support/debug.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facility for debugging /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_DEBUG_HPP #define ROSA_SUPPORT_DEBUG_HPP #include "rosa/config/config.h" #include "rosa/support/terminal_colors.h" #include "rosa/support/type_helper.hpp" #include #include namespace rosa { /// Returns an output stream to use for debugging. std::ostream &dbgs(void); /// Prints a \c std::array to a \c std::ostream. /// /// \tparam T type of values stored in \p A /// \tparam Size number of elements in \p A /// /// \param [in,out] OS \c std::ostream to print to /// \param A \c std::array to print to \p OS /// /// \return \p OS after printing \p A template std::ostream &operator<<(std::ostream &OS, const std::array &A) { OS << '['; for (unsigned I = 0; I < Size; ++I) { if (I) { OS << ','; } OS << PRINTABLE(A[I]); } OS << ']'; return OS; } } // End namespace rosa /// \def ASSERT(stmt) /// \brief Enforces an assertion. /// /// \note Takes effect only when \c ROSA_ENABLE_ASSERTIONS is defined. /// /// Checks if \p stmt evaluates to true. If not, prints an error message about /// violating the assertion and aborts execution. /// /// \param stmt statement to evaluate, needs to be convertable to \c bool #ifndef ROSA_ENABLE_ASSERTIONS -#define ASSERT(stmt) ROSA_IGNORE_UNUSED(stmt) +#define ASSERT(stmt) ROSA_IGNORE_UNUSED_EXPR(stmt) #elif defined(ROSA_WINDOWS) #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ ::abort(); \ } \ ROSA_VOID_STMT #else // defined(ROSA_LINUX) #include #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ void *array[20]; \ auto bt_size = ::backtrace(array, 20); \ ::backtrace_symbols_fd(array, bt_size, 2); \ ::abort(); \ } \ ROSA_VOID_STMT #endif // defined(ROSA_ENABLE_ASSERTIONS) /// \def DEBUG(X) /// \brief Executes the given piece of code only if \c NDEBUG is not defined. /// /// \param X the code to execute /// \def DEBUGVAR(V) /// \brief Dumps the given variable to the default debug output. /// /// \param V the variable to dump /// /// \sa \c rosa::dbgs() #ifndef NDEBUG #define DEBUG(X) \ do { \ X; \ } while (false) #define DEBUGVAR(V) \ do { \ rosa::dbgs() << rosa::terminal::Color::Default << #V << " (" \ << __FILENAME__ << ":" << __LINE__ << "): " << (V) \ << std::endl; \ } while (false) #else // defined(NDEBUG) -#define DEBUG(X) ROSA_IGNORE_UNUSED(X) -#define DEBUGVAR(X) ROSA_IGNORE_UNUSED(X) +#define DEBUG(X) ROSA_IGNORE_UNUSED_STMT(X) +#define DEBUGVAR(X) ROSA_IGNORE_UNUSED_EXPR(X) #endif // defined(NDEBUG) /// Enforces static assertion. /// /// \param COND the condition to evaluate, must be statically evaluable /// \param DIAG error message if \p COND does not evaluate to \c true #define STATIC_ASSERT(COND, DIAG) static_assert((COND), DIAG) #endif // ROSA_SUPPORT_DEBUG_HPP diff --git a/include/rosa/support/log.h b/include/rosa/support/log.h index 3f3049d..c5b60ce 100644 --- a/include/rosa/support/log.h +++ b/include/rosa/support/log.h @@ -1,265 +1,265 @@ //===-- rosa/support/log.h --------------------------------------*- C++ -*-===// // // The RoSA Framework // // Distributed under the terms and conditions of the Boost Software License 1.0. // See accompanying file LICENSE. // // If you did not receive a copy of the license file, see // http://www.boost.org/LICENSE_1_0.txt. // //===----------------------------------------------------------------------===// /// /// \file rosa/support/log.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facility for logging. /// /// \note One call for the various logging macros is supposed to be used /// for registering one log entry. That goes natural with the non-stream /// implementations, which accept one string as argument. A more flexible way /// for printing log entries, for example for colorizing text, is to use macros /// providing a log stream. It is important to note, however, that the stream /// obtained from one macro evaluation fits for printing one log entry, without /// nested/overlapping log entry emissions and the entry being closed with a /// newline. Should this simple recommendation not being followed, the result /// becomes hard to read due to missing line breaks and overlapping entries. /// /// \todo Thread-safety is another issue, which need to be addressed for proper /// logging. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_LOG_H #define ROSA_SUPPORT_LOG_H #include "rosa/config/config.h" #include "rosa/support/terminal_colors.h" #include #include /* **************************************************************************** * Log Levels * * ****************************************************************************/ namespace rosa { /// Type-safe definition of log levels, use this in code. /// \note Keep values in sync with the corresponding preprocessor definitions. enum class LogLevel { Error, ///< Log errors only Warning, ///< Like \c rosa::LogLevel::Error and also log warnings Info, ///< Like \c rosa::LogLevel::Warning and also log general infos Debug, ///< Like \c rosa::LogLevel::Info and also log debug infos Trace, ///< Like \c rosa::LogLevel::Debug and also log trace infos NumLogLevels ///< Number of log levels }; /// Converts a \c rosa::LogLevel to its textual representation. /// /// \param logLevel \c rosa::LogLevel to convert /// /// \return \c std::string representing \p logLevel /// /// \pre `logLevel` is valid:\code /// logLevel != LogLevel::NumLogLevels /// \endcode std::string logLevelToString(const LogLevel logLevel); /// Prints colorized tag for the given \c rosa::LogLevel. /// /// \param [in,out] OS \c std::ostream to print to /// \param logLevel \c rosa::LogLevel to print tag for /// /// \return \p OS after printing a tag for \p logLevel /// /// \pre \p logLevel is valid:\code /// logLevel != LogLevel::NumLogLevels /// \endcode std::ostream &operator<<(std::ostream &OS, const LogLevel logLevel); } // End namespace rosa /// \name Valid log level constants /// \note Only for preprocessor definitions in this file. /// \note Keep the defintions in sync with the values of \c rosa::LogLevel. ///@{ #define ROSA_LOG_LEVEL_ERROR \ 0 ///< Value corresponding to \c rosa::LogLevel::Error #define ROSA_LOG_LEVEL_WARNING \ 1 ///< Value corresponding to \c rosa::LogLevel::Warning #define ROSA_LOG_LEVEL_INFO \ 2 ///< Value corresponding to \c rosa::LogLevel::Info #define ROSA_LOG_LEVEL_DEBUG \ 3 ///< Value corresponding to \c rosa::LogLevel::Debug #define ROSA_LOG_LEVEL_TRACE \ 4 ///< Value corresponding to \c rosa::LogLevel::Trace ///@} /* **************************************************************************** * Logger Implementation * * ****************************************************************************/ /// Stream to print logs to /// /// \todo Make it configurable, e.g. printing into a file. #define ROSA_LOG_OSTREAM std::clog /// Prints a log message to \c ROSA_LOG_OSTREAM. /// /// \param level \c rosa::LogLevel of the log entry /// \param output message to print #define ROSA_LOG_IMPL(level, output) \ do { \ ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" << __FILENAME__ \ << ":" << __LINE__ << ": " << (output) << '\n'; \ } while (false) /// Returns a stream to print a log message to. /// /// \param level \c rosa::LogLevel of the log entry that is about to be printed #define ROSA_LOG_STREAM_IMPL(level) \ ([](void) -> std::ostream & { \ return ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" \ << __FILENAME__ << ":" << __LINE__ << ": "; \ }()) namespace rosa { /// Dummy \c std::ostream printing to nowhere. extern std::ostream LogSink; } // End namespace rosa /// An output stream ignoring all its input. #define ROSA_LOG_STREAM_IGNORE rosa::LogSink /* **************************************************************************** * Logging Interface * * ****************************************************************************/ /// \name Logging interface /// /// Preprocesser macros for convenience. ///@{ /// \def LOG_ERROR_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Error. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Error. /// \def LOG_ERROR(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Error. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Error. /// \def LOG_WARNING_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Warning. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Warning. /// \def LOG_WARNING(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Warning. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Warning. /// \def LOG_INFO_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Info. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Info. /// \def LOG_INFO(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Info. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Info. /// \def LOG_DEBUG_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Debug. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Debug. /// \def LOG_DEBUG(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Debug. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Debug. /// \def LOG_TRACE_STREAM /// \brief Returns a stream to print a log entry of level /// \c rosa::LogLevel::Trace. /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Trace. /// \def LOG_TRACE(output) /// \brief Prints a log entry of level \c rosa::LogLevel::Trace. /// \param output the message to print /// \note Takes effect only if RoSA is built with a log level not smaller than /// \c rosa::LogLevel::Trace. ///@} // Define logging macros if logging is enabled. #ifdef ROSA_LOG_LEVEL #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Error) #define LOG_ERROR(output) ROSA_LOG_IMPL(rosa::LogLevel::Error, output) #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Warning) #define LOG_WARNING(output) ROSA_LOG_IMPL(rosa::LogLevel::Warning, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Info) #define LOG_INFO(output) ROSA_LOG_IMPL(rosa::LogLevel::Info, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Debug) #define LOG_DEBUG(output) ROSA_LOG_IMPL(rosa::LogLevel::Debug, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Trace) #define LOG_TRACE(output) ROSA_LOG_IMPL(rosa::LogLevel::Trace, output) #endif #endif // defined ROSA_LOG_LEVEL // Define all disabled logging features as void. #ifndef LOG_ERROR #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_ERROR(output) ROSA_IGNORE_UNUSED(output) +#define LOG_ERROR(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_WARNING(output) ROSA_IGNORE_UNUSED(output) +#define LOG_WARNING(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_INFO(output) ROSA_IGNORE_UNUSED(output) +#define LOG_INFO(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_DEBUG(output) ROSA_IGNORE_UNUSED(output) +#define LOG_DEBUG(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #ifndef LOG_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IGNORE -#define LOG_TRACE(output) ROSA_IGNORE_UNUSED(output) +#define LOG_TRACE(output) ROSA_IGNORE_UNUSED_EXPR(output) #endif #endif // ROSA_SUPPORT_LOG_H