Page MenuHomePhorge

No OneTemporary

Size
108 KB
Referenced Files
None
Subscribers
None
diff --git a/examples/type-facilities/type-facilities.cpp b/examples/type-facilities/type-facilities.cpp
index d714a73..b4a3ade 100644
--- a/examples/type-facilities/type-facilities.cpp
+++ b/examples/type-facilities/type-facilities.cpp
@@ -1,88 +1,88 @@
//===-- examples/type-facilities/type-facilities.cpp ------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file examples/type-facilities/type-facilities.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief An example showcasing various type-related support facilities.
///
//===----------------------------------------------------------------------===//
#include "rosa/config/version.h"
#include "rosa/support/log.h"
#include "rosa/support/terminal_colors.h"
#include "rosa/support/type_token.hpp"
using namespace rosa;
using namespace rosa::terminal;
int main(void) {
LOG_INFO_STREAM << library_string() << " -- " << Color::Red
<< "type facilities" << Color::Default << '\n';
auto &Log = LOG_TRACE_STREAM;
Log << "\nNumberOfBuiltinTypes: " << NumberOfBuiltinTypes
<< "\nTokenBits: " << token::TokenBits
<< "\nRepresentationBits: " << token::RepresentationBits
<< "\nMaxTokenizableListSize: " << token::MaxTokenizableListSize
<< "\n\n";
Log << "Type number information on 'uint8_t':";
constexpr TypeNumber TN = TypeNumberOf<uint8_t>::Value;
Log << "\n type number: " << PRINTABLE_TN(TN)
<< "\n size: " << TypeForNumber<TN>::Size
<< "\n name: " << TypeForNumber<TN>::Name << "\n\n";
Log << "Type number information on 'std::string':";
constexpr TypeNumber TNS = TypeNumberOf<std::string>::Value;
Log << "\n type number: " << PRINTABLE_TN(TNS)
<< "\n size: " << TypeForNumber<TNS>::Size
<< "\n name: " << TypeForNumber<TNS>::Name << "\n\n";
Log << "Type number information of AtomConstants:";
using Atom1 = AtomConstant<atom("atom1")>;
using Atom2 = AtomConstant<atom("atom2")>;
Log << "\n std::is_same<Atom1, Atom2>::value: "
<< std::is_same<Atom1, Atom2>::value << "\n TypeNumberOf<Atom1>::Value: "
<< PRINTABLE_TN(TypeNumberOf<Atom1>::Value)
<< "\n TypeNumberOf<Atom2>::Value: "
<< PRINTABLE_TN(TypeNumberOf<Atom2>::Value)
<< "\n name: " << TypeForNumber<TypeNumberOf<Atom1>::Value>::Name
<< "\n\n";
Log << "Type token information on 'TypeList<uint8_t, uint16_t, "
"std::string>':";
// \c rosa::Token is generated statically.
constexpr Token T = TypeToken<uint8_t, uint16_t, std::string>::Value;
STATIC_ASSERT(
(T == TypeListToken<TypeList<uint8_t, uint16_t, std::string>>::Value),
"alias template definition is wrong");
Token T_ = T; // We need a non-const value for dropping head later.
// Iterate over encoded entries in \c T_.
while (!emptyToken(T_)) {
Log << "\n token: " << PRINTABLE_TOKEN(T_)
<< "\n valid: " << validToken(T_) << "\n empty: " << emptyToken(T_)
- << "\n length: " << lengthOfToken(T_)
+ << "\n length: " << static_cast<size_t>(lengthOfToken(T_))
<< "\n full size: " << sizeOfValuesOfToken(T_)
<< "\n head type number: " << PRINTABLE_TN(headOfToken(T_))
<< "\n size of head: " << sizeOfHeadOfToken(T_)
<< "\n name of head: " << nameOfHeadOfToken(T_)
<< "\n is head uint8_t: " << isHeadOfTokenTheSameType<uint8_t>(T_)
<< "\n is head uint16_t: " << isHeadOfTokenTheSameType<uint16_t>(T_)
<< "\n is head std::string: "
<< isHeadOfTokenTheSameType<std::string>(T_) << "\nDropping head...";
dropHeadOfToken(T_);
}
// Here when Token became empty.
Log << "\n token: " << PRINTABLE_TOKEN(T_) << "\n empty: " << emptyToken(T_)
<< '\n';
return 0;
}
diff --git a/include/rosa/core/Message.hpp b/include/rosa/core/Message.hpp
index ce9e6ec..06b6c60 100644
--- a/include/rosa/core/Message.hpp
+++ b/include/rosa/core/Message.hpp
@@ -1,258 +1,259 @@
//===-- rosa/core/Message.hpp -----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/core/Message.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Declaration of \c rosa::Message base-class.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_CORE_MESSAGE_HPP
#define ROSA_CORE_MESSAGE_HPP
#include "rosa/support/log.h"
#include "rosa/support/tokenized_storages.hpp"
#include "rosa/core/forward_declarations.h"
namespace rosa {
/// *Message* interface.
///
/// The interface provides means to check the type of the stored values, but
/// actual data is to be managed by derived implementations.
///
/// A \c rosa::Message instance is an immutable data object that obtains its
/// data upon creation and provides only constant references for the stored
/// values.
///
/// \note Any reference obtained from a \c rosa::Message instance remains valid
/// only as long as the owning \c rosa::Message object is not destroyed.
///
/// \todo Some member functions of \c rosa::Message duplicate member functions
/// of \c rosa::TokenizedStorage, which cannot be easily factored out into a
/// common base class due to eventual diamond inheritance issues in derived
/// classes. Could this duplication be avoided?
class Message {
protected:
/// Creates a new instance.
///
/// \note No implementation for empty list.
///
/// \tparam Type type of the mandatory first argument
/// \tparam Types types of any further arguments
///
/// \note the actual arguments are ignored by the constructor it is only
/// their type that matters. The actual values are supposed to be handled by
/// any implementation derived from \c rosa::Message.
///
/// \pre \p Type and \p Types are all built-in types and the number of stored
/// values does not exceed \c rosa::token::MaxTokenizableListSize.
template <typename Type, typename... Types>
Message(const Type &, const Types &...) noexcept;
/// No copying and moving of \c rosa::Message instances.
///@{
Message(const Message &) = delete;
Message(Message &&) = delete;
Message &operator=(const Message &) = delete;
Message &operator=(Message &&) = delete;
///@}
public:
/// Creates a \c rosa::message_t object from constant lvalue references.
///
/// \tparam Type type of the mandatory first argument
/// \tparam Types types of any further arguments
///
/// \param T the first value to include in the \c rosa::Message
/// \param Ts optional further values to include in the \c rosa::Message
///
/// \return new \c rosa::message_t object created from the given arguments
template <typename Type, typename... Types>
static message_t create(const Type &T, const Types &... Ts) noexcept;
/// Creates a \c rosa::message_t object from rvalue references.
///
/// \tparam Type type of the mandatory first argument
/// \tparam Types types of any further arguments
///
/// \param T the first value to include in the \c rosa::Message
/// \param Ts optional further values to include in the \c rosa::Message
///
/// \return new \c rosa::message_t object created from the given arguments
template <typename Type, typename... Types>
static message_t create(Type &&T, Types &&... Ts) noexcept;
/// Represents the types of the values stored in \p this object.
///
/// A valid, non-empty \c rosa::Token representing the types of the values
/// stored in \p this object.
const Token T;
/// The number of values stored in \p this object.
///
/// That is the number of types encoded in \c rosa::Message::T.
- const size_t Size;
+ const token_size_t Size;
/// Destroys \p this object.
virtual ~Message(void);
/// Tells if the value stored at a given index is of a given type.
///
/// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as
/// the \c rosa::AtomValue wrapped into it.
///
/// \tparam Type type to match against
///
/// \param Pos index the type of the value at is to be matched against \p Type
///
/// \return if the value at index \p Pos of type \p Type
///
/// \pre \p Pos is a valid index:\code
/// Pos < Size
/// \endcode
- template <typename Type> bool isTypeAt(const size_t Pos) const noexcept;
+ template <typename Type> bool isTypeAt(const token_size_t Pos) const noexcept;
/// Gives a constant reference of a value of a given type stored at a given
/// index.
///
/// \tparam Type type to give a reference of
///
/// \param Pos index to set the reference for
///
/// \return constant reference of \p Type for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index and the value at index \p Pos is of type
/// \p Type:
/// \code
/// Pos < Size && isTypeAt<Type>(Pos)
/// \endcode
- template <typename Type> const Type &valueAt(const size_t Pos) const noexcept;
+ template <typename Type>
+ const Type &valueAt(const token_size_t Pos) const noexcept;
protected:
/// Provides an untyped pointer for the value at a given index.
///
/// \param Pos index to take a pointer for
///
/// \return untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Size
/// \endcode
- virtual const void *pointerTo(const size_t Pos) const noexcept = 0;
+ virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0;
};
/// Nested namespace with implementation for \c rosa::Message, consider it
/// private.
namespace {
/// Template class for an implementation of \c rosa::Message.
///
/// \tparam Types types whose values are to be stored
template <typename... Types> class LocalMessage;
/// Implementation of the template \c rosa::LocalMessage providing facilities
/// for storing values as a \c rosa::Message object.
///
/// \tparam Type type of the first mandatory value of the \c rosa::Message
/// \tparam Types of any further values
template <typename Type, typename... Types>
class LocalMessage<Type, Types...> final
: public Message,
private TokenizedStorage<Type, Types...> {
public:
/// Creates an instance from constant lvalue references.
///
/// \param T the mandatory first value to store in the \c rosa::Message object
/// \param Ts optional further values to store in the \c rosa::Message object
LocalMessage(const Type &T, const Types &... Ts) noexcept
: Message(T, Ts...),
TokenizedStorage<Type, Types...>(T, Ts...) {
ASSERT(this->T == this->ST && Size == this->size()); // Sanity check.
}
/// Creates an instance from rvalue references.
///
/// \param T the mandatory first value to store in the \c rosa::Message object
/// \param Ts optional further values to store in the \c rosa::Message object
LocalMessage(Type &&T, Types &&... Ts) noexcept
: Message(T, Ts...),
TokenizedStorage<Type, Types...>(std::move(T), std::move(Ts)...) {
ASSERT(this->T == this->ST && Size == this->size()); // Sanity check.
}
/// Provides an untyped pointer for the constant value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return untyped pointer for the constant value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Size
/// \endcode
- const void *pointerTo(const size_t Pos) const noexcept override {
+ const void *pointerTo(const token_size_t Pos) const noexcept override {
ASSERT(Pos < Size);
return TokenizedStorage<Type, Types...>::pointerTo(Pos);
}
/// Aborts the program!
///
/// Since \c rosa::Message instances are supposed to be immutable, the
/// non-const inherited function is overridden so that it aborts execution.
- void *pointerTo(const size_t) noexcept override {
+ void *pointerTo(const token_size_t) noexcept override {
ROSA_CRITICAL("Unallowed operation of rosa::LocalMessage");
}
};
} // End namespace
template <typename Type, typename... Types>
Message::Message(const Type &, const Types &...) noexcept
: T(TypeToken<typename std::decay<Type>::type,
typename std::decay<Types>::type...>::Value),
Size(lengthOfToken(T)) {
ASSERT(validToken(T) &&
lengthOfToken(T) == (1 + sizeof...(Types))); // Sanity check.
LOG_TRACE("Creating Message with Token(" + to_string(T) + ")");
}
/// \note The implementation instantiates a private local template class
/// \c LocalMessage.
template <typename Type, typename... Types>
message_t Message::create(const Type &T, const Types &... Ts) noexcept {
return message_t(new LocalMessage<Type, Types...>(T, Ts...));
}
/// \note The implementation instantiates a private local template class
/// \c LocalMessage.
template <typename Type, typename... Types>
message_t Message::create(Type &&T, Types &&... Ts) noexcept {
return message_t(
new LocalMessage<Type, Types...>(std::move(T), std::move(Ts)...));
}
template <typename Type>
-bool Message::isTypeAt(const size_t Pos) const noexcept {
+bool Message::isTypeAt(const token_size_t Pos) const noexcept {
ASSERT(Pos < Size);
Token TT = T;
dropNOfToken(TT, Pos);
return isHeadOfTokenTheSameType<Type>(TT);
}
template <typename Type>
-const Type &Message::valueAt(const size_t Pos) const noexcept {
+const Type &Message::valueAt(const token_size_t Pos) const noexcept {
ASSERT(Pos < Size && isTypeAt<Type>(Pos));
return *static_cast<const Type *>(pointerTo(Pos));
}
} // End namespace rosa
#endif // ROSA_CORE_MESSAGE_HPP
diff --git a/include/rosa/core/MessageMatcher.hpp b/include/rosa/core/MessageMatcher.hpp
index 71bd69f..0092cb9 100644
--- a/include/rosa/core/MessageMatcher.hpp
+++ b/include/rosa/core/MessageMatcher.hpp
@@ -1,201 +1,201 @@
//===-- rosa/core/MessageMatcher.hpp ----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/core/MessageMatcher.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Facilities for checking and matching types of values stored in
/// \c rosa::Message instances.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_CORE_MESSAGEMATCHER_HPP
#define ROSA_CORE_MESSAGEMATCHER_HPP
#include "rosa/core/Message.hpp"
#include <tuple>
namespace rosa {
/// Provides features to type-check a \c rosa::Message instance and extract
/// stored values from it into an \c std::tuple instance with matching type
/// arguments.
///
/// \tparam List \c rosa::TypeList to check the stored values against
template <typename List> struct MessageMatcher;
/// Definition of \c rosa::MessageMatcher for non-empty lists of types, like
/// \c rosa::Message itself.
///
/// \tparam Type first mandatory type
/// \tparam Types any further types
template <typename Type, typename... Types>
struct MessageMatcher<TypeList<Type, Types...>> {
/// \c rosa::Token associated to the given \c rosa::TypeList.
static constexpr Token T = TypeToken<Type, Types...>::Value;
/// Tells if the values stored in a \c rosa::Message instance are matching
/// types given as \c rosa::TypeList<T, Types...>, considering
/// \c rosa::AtomConstant instead of \c rosa::AtomValue.
///
/// \param Msg \c rosa::Message to match
///
/// \return whether the types of values stored in \p Msg matches
/// \c rosa::TypeList<Type, Types...>
static inline bool doesStronglyMatch(const Message &Msg) noexcept;
/// Gives a \c std::tuple with references to the values stored in a
/// type-matching instance of \c rosa::Message.
///
/// \param Msg \c rosa::Message to extract values from
///
/// \return \c std::tuple with references to the values stored in \p Msg
///
/// \pre Types of the values stored in \p Msg matches
/// \c rosa::TypeList<Type, Types...>:\code
/// doesStronglyMatch(Msg)
/// \endcode
static inline std::tuple<const Type &, const Types &...>
extractedValues(const Message &Msg) noexcept;
};
/// Turns a list of types into a \c rosa::TypeList for \c rosa::MessageMatcher.
template <typename Type, typename... Types>
using MsgMatcher = MessageMatcher<TypeList<Type, Types...>>;
/// Nested namespace with implementation for features of
/// \c rosa::MessageMatcher, consider it private.
namespace {
/// \defgroup MessageMatcherImpl Implementation for rosa::MessageMatcher
///
/// An implementation of type-checking and value extraction for
/// \c rosa::MessageMatcher.
///
///@{
/// Template declaration of \c MessageMatcherImpl.
///
/// \tparam List \c rosa::TypeList to match against
template <typename List> struct MessageMatcherImpl;
/// Specialization for \c rosa::EmptyTypeList.
template <> struct MessageMatcherImpl<EmptyTypeList> {
static bool doesStronglyMatchFrom(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// Matching EmptyTypeList only if reached the end of the stored types.
return Pos == Msg.Size;
}
static std::tuple<> extractedValuesFrom(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// It is valid to extract an empty list only if we reached the end of
// stored values.
ASSERT(doesStronglyMatchFrom(Msg, Pos));
return std::tie();
}
};
/// Specialization for \c rosa::AtomValue in the head.
template <AtomValue V, typename... Ts>
struct MessageMatcherImpl<TypeList<AtomConstant<V>, Ts...>> {
static bool doesHeadStronglyMatchAt(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// Matching a \c rosa::AtomConstant in the head if there is a type stored at
// \p Pos, the stored type is \c rosa::AtomValue, and the corresponding
// value matches the \c rosa::AtomValue \p V.
return Pos < Msg.Size && Msg.isTypeAt<AtomValue>(Pos) &&
Msg.valueAt<AtomValue>(Pos) == V;
}
static bool doesStronglyMatchFrom(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// Matching a non-empty list if the head is matching and the rest of the
// list is matching.
return doesHeadStronglyMatchAt(Msg, Pos) &&
MessageMatcherImpl<TypeList<Ts...>>::doesStronglyMatchFrom(Msg,
Pos + 1);
}
static std::tuple<const AtomConstant<V> &, const Ts &...>
- extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept {
+ extractedValuesFrom(const Message &Msg, const token_size_t Pos) noexcept {
// Extracting for a non-empty list with a matching \c rosa::AtomConstant in
// the head by getting the encoded \c rosa::AtomConstant and concatenating
// it with values extracted for the rest of the list.
ASSERT(doesHeadStronglyMatchAt(Msg, Pos));
return std::tuple_cat(
std::tie(AtomConstant<V>::Value),
MessageMatcherImpl<TypeList<Ts...>>::extractedValuesFrom(Msg, Pos + 1));
}
};
/// Definition for the general case when a regular built-in type (not a
/// \c rosa::AtomConstant) is in the head.
template <typename T, typename... Ts>
struct MessageMatcherImpl<TypeList<T, Ts...>> {
static bool doesHeadStronglyMatchAt(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// Matching the head if there is a type stored at \p Pos, and the stored
// type is \p T.
return Pos < Msg.Size && Msg.isTypeAt<T>(Pos);
}
static bool doesStronglyMatchFrom(const Message &Msg,
- const size_t Pos) noexcept {
+ const token_size_t Pos) noexcept {
// Matching a non-empty list if the head is matching and the rest of the
// list is matching.
return doesHeadStronglyMatchAt(Msg, Pos) &&
MessageMatcherImpl<TypeList<Ts...>>::doesStronglyMatchFrom(Msg,
Pos + 1);
}
static std::tuple<const T &, const Ts &...>
- extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept {
+ extractedValuesFrom(const Message &Msg, const token_size_t Pos) noexcept {
// Extracting for a non-empty list with a matching head by getting the
// value for the head and concatenating it with values extracted for the
// rest of the list.
ASSERT(doesHeadStronglyMatchAt(Msg, Pos));
return std::tuple_cat(
std::tie(Msg.valueAt<T>(Pos)),
MessageMatcherImpl<TypeList<Ts...>>::extractedValuesFrom(Msg, Pos + 1));
}
};
///@}
} // End namespace
template <typename Type, typename... Types>
bool MessageMatcher<TypeList<Type, Types...>>::doesStronglyMatch(
const Message &Msg) noexcept {
// \note Fail quick on \c rosa::MessageMatcher::T, then match against list
// with squashed integers the way \c rosa::Token is generated.
return T == Msg.T &&
MessageMatcherImpl<typename SquashedTypeList<
TypeList<Type, Types...>>::Type>::doesStronglyMatchFrom(Msg, 0);
}
template <typename Type, typename... Types>
std::tuple<const Type &, const Types &...>
MessageMatcher<TypeList<Type, Types...>>::extractedValues(
const Message &Msg) noexcept {
ASSERT(doesStronglyMatch(Msg));
// \note Match against a list with squashed integers as \c rosa::Token is
// generated.
return MessageMatcherImpl<typename SquashedTypeList<
TypeList<Type, Types...>>::Type>::extractedValuesFrom(Msg, 0);
}
} // End namespace rosa
#endif // ROSA_CORE_MESSAGEMATCHER_HPP
diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp
index c73afdc..5534660 100755
--- a/include/rosa/deluxe/DeluxeAgent.hpp
+++ b/include/rosa/deluxe/DeluxeAgent.hpp
@@ -1,1119 +1,1124 @@
//===-- rosa/deluxe/DeluxeAgent.hpp -----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/deluxe/DeluxeAgent.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Specialization of \c rosa::Agent for *agent* role of the *deluxe
/// interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_DELUXE_DELUXEAGENT_HPP
#define ROSA_DELUXE_DELUXEAGENT_HPP
#include "rosa/core/Agent.hpp"
#include "rosa/deluxe/DeluxeAtoms.hpp"
#include <map>
/// Local helper macros to deal with built-in types.
///
///@{
/// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent.
///
/// \param N name suffix to use
#define DASLAVEHANDLERNAME(N) handleSlave_##N
/// Creates function name for member functions in \c rosa::deluxe::DeluxeAgent.
///
/// \param N name suffix to use
#define DAMASTERHANDLERNAME(N) handleMaster_##N
/// Defines member functions for handling messages from *slaves* in
/// \c rosa::deluxe::DeluxeAgent.
///
/// \see \c DeluxeAgentInputHandlers
///
/// \note No pre- and post-conditions are validated directly by these functions,
/// they rather rely on \c rosa::deluxe::DeluxeAgent::saveInput to do that.
///
/// \param T the type of input to handle
/// \param N name suffix for the function identifier
#define DASLAVEHANDLERDEFN(T, N) \
void DASLAVEHANDLERNAME(N)(atoms::Slave, id_t SlaveId, T Value) noexcept { \
saveInput(SlaveId, Value); \
}
/// Defines member functions for handling messages from *master* in
/// \c rosa::deluxe::DeluxeAgent.
///
/// \see \c DeluxeAgentMasterInputHandlers
///
/// \note No pre- and post-conditions are validated directly by these functions,
/// they rather rely on \c rosa::deluxe::DeluxeAgent::saveMasterInput to do
/// that.
///
/// \param T the type of input to handle
/// \param N name suffix for the function identifier
#define DAMASTERHANDLERDEFN(T, N) \
void DAMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, \
T Value) noexcept { \
saveMasterInput(MasterId, Value); \
}
/// Convenience macro for \c DASLAVEHANDLERDEFN with identical arguments.
///
/// \see \c DASLAVEHANDLERDEFN
///
/// This macro can be used instead of \c DASLAVEHANDLERDEFN 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 DASLAVEHANDLERDEF(T) DASLAVEHANDLERDEFN(T, T)
/// Convenience macro for \c DAMASTERHANDLERDEFN with identical arguments.
///
/// \see \c DAMASTERHANDLERDEFN
///
/// This macro can be used instead of \c DAMASTERHANDLERDEFN 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 DAMASTERHANDLERDEF(T) DAMASTERHANDLERDEFN(T, T)
/// Results in a \c THISMEMBER reference to a member function defined by
/// \c DASLAVEHANDLERDEFN.
///
/// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super
/// class \c rosa::Agent with member function defined by \c DASLAVEHANDLERDEFN.
///
/// \see \c DASLAVEHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
#define DASLAVEHANDLERREF(N) THISMEMBER(DASLAVEHANDLERNAME(N))
/// Results in a \c THISMEMBER reference to a member function defined by
/// \c DAMASTERHANDLERDEFN.
///
/// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super
/// class \c rosa::Agent with member function defined by \c DAMASTERHANDLERDEFN.
///
/// \see \c DAMASTERHANDLERDEFN, \c THISMEMBER
///
/// \param N name suffix for the function identifier
#define DAMASTERHANDLERREF(N) THISMEMBER(DAMASTERHANDLERNAME(N))
///@}
namespace rosa {
namespace deluxe {
/// Specialization of \c rosa::Agent for *agent* role of the *deluxe interface*.
///
/// \see \c rosa::deluxe::DeluxeContext
///
/// \invariant All input-related container objects have a size matching
/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding
/// entry for each input. \c rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs
/// matches \c rosa::deluxe::DeluxeAgent::NumberOfInputs. All
/// master-output-related container objects have a size matching \c
/// rosa::deluxe::DeluxeAgent::NumberOfMasterOutputs. Types of input and
/// master-output values are consistent throughout all the input-related and
/// master-output-related containers, respectively. No *slave* is registered at
/// more than one input position. *Slave* registrations and corresponding
/// reverse lookup information are consistent.
///
/// \see Definition of \c rosa::deluxe::DeluxeAgent::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 DeluxeAgent : public Agent {
/// Checks whether \p this object holds the class invariant.
///
/// \see Invariant of the class \c rosa::deluxe::DeluxeAgent
///
/// \return if \p this object holds the class invariant
bool inv(void) const noexcept;
public:
/// The type of values produced by \p this object.
///
/// That is the type of values \p this object sends to its *master*.
///
/// \see \c rosa::deluxe::DeluxeAgent::master
const TypeNumber OutputType;
/// Number of inputs processed by \p this object.
const size_t NumberOfInputs;
/// The type of values \p this object processes from its *master*.
///
/// \see \c rosa::deluxe::DeluxeAgent::master
const TypeNumber MasterInputType;
/// Number of outputs produces by \p this object for its *slaves*.
///
/// \note This values is equal to \c
/// rosa::deluxe::DeluxeAgent::NumberOfInputs.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave.
const size_t NumberOfMasterOutputs;
private:
/// Types of input values produced by *slaves* of \p this object.
///
/// \note The \c rosa::TypeNumber values stored here match the corresponding
/// values in \c rosa::deluxe::DeluxeAgent::InputValues.
///
/// \note The position of a type in the \c std::vector indicates which
/// argument of \p this object's processing function it belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
const std::vector<TypeNumber> InputTypes;
/// Indicates whether any particular input value has been changed since the
/// last trigger received from the system.
///
/// All the flags are reset to \c false upon handling a trigger and then set
/// to \c true by \c rosa::deluxe::DeluxeAgent::saveInput when storing a new
/// input value in \c rosa::deluxe::DeluxeAgent::InputValues.
///
/// \note The position of a flag in the \c std::vector indicates which
/// argument of \p this object's processing function it belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
std::vector<bool> InputChanged;
/// Stores the actual input values.
///
/// \note The types of stored values match the corresponding
/// \c rosa::TypeNumber values in \c rosa::deluxe::DeluxeAgent::InputTypes.
///
/// \note The position of a value in the \c rosa::AbstractTokenizedStorage
/// indicates which argument of \p this object's processing function it is.
/// See also \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
const std::unique_ptr<AbstractTokenizedStorage> InputValues;
/// 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::DeluxeAgent::saveMasterInput when storig a new
/// input value in \c rosa::deluxe::DeluxeAgent::MasterInputValue.
bool MasterInputChanged;
/// Stores the actual input value from *master*.
///
/// \note The type of the stored value matches the type indicated by \c
/// rosa::deluxe::DeluxeAgent::MasterInputType.
const std::unique_ptr<AbstractTokenizedStorage> MasterInputValue;
/// Types of output values produced by \p this object for its *slaves*.
///
/// That is the type of values \p this object sends to its *slaves*.
///
/// \note The position of a type in the \c std::vector indicates which
/// *slave* of \p this object the type belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
const std::vector<TypeNumber> MasterOutputTypes;
/// Alias for function objects used as trigger handler for
/// \c rosa::deluxe::DeluxeAgent.
///
/// \note The function used for \c H is to be \c noexcept.
///
/// \see \c rosa::deluxe::DeluxeAgent::FP
using H = std::function<void(void)>;
/// Handles trigger from the system.
///
/// The actual functions processing *slave* and *master* inputs and generating
/// optional output to *master* and *slaves* are captured in a lambda
/// expression that is in turn wrapped in a \c std::function object. The
/// lambda expression calls the master-input processing function with the
/// actual master-input data and sends its result -- if any -- to *slaves* by
/// calling \c rosa::deluxe::DeluxeAgent::handleMasterOutputs; then calls the
/// input processing function with the actual input data and sends its result
/// -- if any -- to *master* by calling \c
/// rosa::deluxe::DeluxeAgent::sendToMaster and *slaves* by calling \c
/// rosa::deluxe::DeluxeAgent::handleMasterOutputs. Also, all the flags stored
/// in \c rosa::deluxe::DeluxeAgent::InputChanged and \c
/// rosa::deluxe::DeluxeAgent::MasterInputChanged are reset when the current
/// values are processed. The function \c
/// rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the
/// function object.
///
/// \see \c
/// rosa::deluxe::DeluxeAgent::triggerHandlerFromProcessingFunctions
const H FP;
/// The *master* to send values to.
///
/// \note *Masters* are set dynamically, hence it is possible that a
/// \c rosa::deluxe::DeluxeAgent instance does not have any *master* at a
/// given moment.
Optional<AgentHandle> Master;
/// The *slaves* sending input to \p this object.
///
/// \note The position of a *slave* in the \c std::vector indicates which
/// argument of \p this object's processing function it belongs to. See also
/// \c rosa::deluxe::DeluxeAgent::DeluxeAgent.
///
/// \note *Slaves* are set dynamically, hence it is possible that a
/// \c rosa::deluxe::DeluxeAgent instance does have input positions without
/// any *slave* associated to them.
///
/// \note Reverse lookup information is maintained in
/// \c rosa::deluxe::DeluxeAgent::SlaveIds, which is to be kept in sync with
/// the *slaves* stored here.
std::vector<Optional<AgentHandle>> Slaves;
/// Associates \c rosa::id_t values to corresponding indices of registered
/// *slaves*.
///
/// \see \c rosa::deluxe::DeluxeAgent::Slaves
std::map<id_t, size_t> SlaveIds;
/// 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;
/// Tells whether types stored in \c rosa::TypeList \p As match the input
/// types of \p this object.
///
/// \tparam As \c rosa::TypeList containing types to match against values in
/// \c rosa::deluxe::DeluxeAgent::InputTypes
///
/// \note Instatiation of the template fails if \p As is not \c
/// rosa::TypeList.
///
/// \return if types in \p As match \c rosa::TypeNumber values stored in
/// \c rosa::deluxe::DeluxeAgent::InputTypes
template <typename As> bool inputTypesMatch(void) const noexcept;
/// Tells whether types stored in \c rosa::TypeList \p Ts match the
/// master-output types of \p this object.
///
/// \tparam Ts \c rosa::TypeList containing types to match against values in
/// \c rosa::deluxe::DeluxeAgent::MasterOutputTypes
///
/// \note Instatiation of the template fails if \p As is not \c
/// rosa::TypeList.
///
/// \return if types in \p Ts match \c rosa::TypeNumber values stored in
/// \c rosa::deluxe::DeluxeAgent::MasterOutputTypes
template <typename Ts> bool masterOutputTypesMatch(void) const noexcept;
/// Gives an \c std::tuple containing the current input values and their
/// change flags so that they can be used for the processing function.
///
/// \tparam As types of the input values
/// \tparam S0 indices for accessing input values and their change flags
///
/// \note The only argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return current input values and their change flags prepared for invoking
/// the processing function with them
///
/// \pre The type arguments \p As... match the input types of \p this object
/// and the provided indices \p S0... constitute a proper sequence for
/// accessing input values and their change flags: \code
/// inputTypesMatch<TypeList<As...>>() && sizeof...(As) == sizeof...(S0)
/// \endcode
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...> prepareCurrentInputs(Seq<S0...>) const
noexcept;
/// Invokes a processing function matching the input, output, and
/// master-output input types of \p this object with actual arguments provided
/// in a \c std::tuple.
///
/// \note \p Args providing the actual arguments for \p F is to be created by
/// \c rosa::deluxe::DeluxeAgent::prepareCurrentInputs.
///
/// \tparam T output type of the processing function
/// \tparam Ts types of master-output values of the processing function
/// \tparam As types of inputs for the processing function
/// \tparam S0 indices starting with `0` for extracting actual arguments from
/// \p Args
///
/// \param F the processing function to invoke
/// \param Args the actual arguments to invoke \p F with
///
/// \note The last argument provides indices statically as template arguments
/// \p S0..., so its actual value is ignored.
///
/// \return the result of \p F for actual arguments \p Args
///
/// \pre The provided sequence of indices \p S0... constitutes a proper
/// sequence for extracting all actual arguments for
/// \p F from \p Args: \code
/// sizeof...(As) == sizeof...(S0)
/// \endcode
template <typename T, typename... Ts, typename... As, size_t... S0>
static std::tuple<Optional<T>, Optional<Ts>...>
invokeWithTuple(std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args,
Seq<S0...>) noexcept;
/// Handles the master-output value at position \p Pos of \p Output.
///
/// \p Output is a \c std::tuple resulted by a processing function and
/// contains master-output values starting at position \p Offset. The
/// function takes the master-output value for *slave* position \p Pos and
/// sends its actual value, if any, to the corresponding *slave*.
///
/// \note A master-output of type \c rosa::unit_t indicates no actual output
/// and hence no message is generated for a position whose corresponding
/// master-output type is \c rosa::unit_t.
///
/// \note The function provides position-based implementation for \c
/// rosa::deluxe::DeluxeAgent::handleMasterOutputs.
///
/// \tparam Offset index of the first master-output value in \p Output
/// \tparam Pos the position of the master-output to handle
/// \tparam Ts output types stored in \p Output
///
/// \param Output \c std::tuple resulted by the processing function
///
/// \pre \p Output matches the master-output types \p this object was created
/// with and \p Pos is a valid master-output index: \code
/// masterOutputTypesMatch<typename TypeListDrop<Offset,
/// TypeList<Ts...>>::Type>() &&
/// Pos < NumberOfMasterOutputs
/// \endcode
template <size_t Offset, size_t Pos, typename... Ts>
void handleMasterOutputAtPos(
const std::tuple<Optional<Ts>...> &Output) noexcept;
/// Handles master-output values from \p Output.
///
/// \p Output is a \c std::tuple resulted by a processing function and
/// contains master-output values starting at position \p Offset. The function
/// takes master-output values and sends each actual value to the
/// corresponding *slave*.
///
/// \tparam Offset index of the first master-output value in \p Output
/// \tparam Ts output types stored in \p Output
/// \tparam S0 indices starting with `0` for extracting master-output values
/// from \p Output
///
/// \param Output \c std::tuple resulted by a processing function
///
/// \pre \p Output matches the master-output types \p this object was created
/// with and the provided sequence of indices \p S0... constitues a proper
/// sequence for extraing all master-output values from \p Output: \code
/// masterOutputTypesMatch<typename TypeListDrop<Offset,
/// TypeList<Ts...>>::Type>() &&
/// sizeof...(S0) == NumberOfMasterOutputs
/// \endcode
template <size_t Offset, typename... Ts, size_t... S0>
void
handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) noexcept;
/// Wraps processing functions into a trigger handler.
///
/// \see \c rosa::deluxe::DeluxeAgent::FP
///
/// \note The function cannot be const qualified because the lambda
/// expression defined in it needs to capture \p this object by a non-const
/// reference
///
/// \tparam MT type of master-input
/// \tparam T type of output
/// \tparam Ts types of master-output values
/// \tparam As types of input values
///
/// \param MF function processing master-input and generating output
/// \param F function processing inputs and generating output
///
/// \note A master-input type of \c rosa::unit_t indicates that \p this object
/// does not receive master-input, \p MF is never called if \p MT is \c
/// rosa::unit_t.
///
/// \return trigger handler function based on \p F and \p MF
///
/// \pre Template arguments \p MT, \p T, \p Ts..., and \p As... match the
/// corresponding types \p this object was created with: \code
/// MasterInputType == TypeNumberOf<MT>::Value &&
/// OutputType == TypeNumberOf<T>::Value &&
/// inputTypesMatch<TypeList<As...>>() &&
/// masterOutputTypesMatch<TypeList<Ts...>>()
/// \endcode
template <typename MT, typename T, typename... Ts, typename... As>
H triggerHandlerFromProcessingFunctions(
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
public:
/// Creates a new instance.
///
/// The constructor instantiates the base-class with functions to handle
/// messages as defined for the *deluxe interface*.
///
/// The function \p F generates a \c std::tuple of values: the first value is
/// the output for the *master* and the rest is for the *slaves*. All output
/// generated by the function is optional as an agent may decide not to output
/// anything at some situation.
///
/// \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 output of \p F
/// \tparam Ts type of master-output values of \p F and \p MF
/// \tparam As types of input values of \p F
///
/// \note Instantiation fails if any of the type arguments \p MT, \p T, \p
/// Ts..., and \p As... is not a built-in type.
///
/// \note If \p MT is \c rosa::unit_t, the constructed object does not receive
/// master-input. Similarly, if any of \p Ts... is \c rosa::unit_t, the
/// constructed object does not generated master-output for the corresponding
/// *slave* position.
///
/// \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 and generate
/// master-output with
/// \param F function to process input values and generate output and
/// master-output with
///
/// \pre Statically, all of the type arguments \p MT, \p T, \p Ts..., and \p
/// As... is a built-in type and the number of input and master-output types
/// are equal: \code
/// TypeListSubsetOf<TypeList<MT, T, Ts..., As...>,
/// BuiltinTypes>::Value && sizeof...(Ts) == sizeof...(As)
///\endcode
/// Dynamically, the instance is created as of kind \c
/// rosa::deluxe::atoms::AgentKind: \code
/// Kind == rosa::deluxe::atoms::AgentKind
/// \endcode
template <typename MT, typename T, typename... Ts, typename... As,
typename =
std::enable_if_t<TypeListSubsetOf<TypeList<MT, T, Ts..., As...>,
BuiltinTypes>::Value &&
sizeof...(Ts) == sizeof...(As)>>
DeluxeAgent(
const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept;
/// Destroys \p this object.
~DeluxeAgent(void) noexcept;
/// The *master* of \p this object, if any is registered.
///
/// \see \c rosa::deluxe::DeluxeAgent::registerMaster
///
/// \return the *master* registered for \p this object
Optional<AgentHandle> 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::DeluxeAgent::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<AgentHandle> _Master) noexcept;
/// Tells the type of values consumed from the *slave* at a position.
///
/// That is the type of values \p this object expect to be sent to it by its
/// *slave* registered at position \p Pos.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave
///
/// \param Pos position of *slave*
///
/// \return \c rosa::TypeNumber representing the type of values consumed from
/// the *slave* at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfInputs
/// \endcode
TypeNumber inputType(const size_t Pos) const noexcept;
/// Tells the type of values produced for the *slave* at a position.
///
/// That is the type of values \p this object potentially sends to its
/// *slave* registered at position \p Pos.
///
/// \see \c rosa::deluxe::DeluxeAgent::slave
///
/// \param Pos position of *slave*
///
/// \return \c rosa::TypeNumber representing the type of values produced for
/// the *slave* at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfMasterOutputs
/// \endcode
TypeNumber masterOutputType(const size_t Pos) const noexcept;
/// The *slave* of \p this object registered at a position, if any.
///
/// \see \c rosa::deluxe::DeluxeAgent::registerSlave
///
/// \param Pos position of *slave*
///
/// \return the *slave* registered for \p this object at position \p Pos
///
/// \pre \p Pos is a valid index of input: \code
/// Pos < NumberOfInputs
/// \endcode
Optional<AgentHandle> slave(const size_t Pos) const noexcept;
/// Registers a *slave* for \p this object at a position.
///
/// The new *slave* is registered by overwriting the reference to any already
/// registered *slave* at position \p Pos. One can clear the registered
/// reference by passing an *empty* \c rosa::Optional object as actual
/// argument. If \p Slave is already registered for another position, the
/// other position gets cleared.
///
/// \note The role of the referred *slave* is validated by checking its
/// *kind*.
///
/// \note The type of values produced by the referred *slave* is validated by
/// matching its `OutputType` against the corresponding value in
/// \c rosa::deluxe::DeluxeAgent::InputTypes.
///
/// \note The type of master-input values processed by the referred *slave* is
/// validated by matching its `MasterInputType` against the corresponding
/// value in \c rosa::deluxe::DeluxeAgent::MasterOutputTypes.
///
/// \param Pos position to register \p Slave at
/// \param Slave the *slave* to register
///
/// \pre \p Pos is a valid index of input, \p Slave is empty or of kind
/// \c rosa::deluxe::atoms::AgentKind or \c rosa::deluxe::atoms::SensorKind,
/// and \p Slave -- if not empty -- produces values of types matching the
/// expected input type at position \p Pos and processes values of types
/// matching the produced master-output type at position \p Pos:
/// \code
/// Pos < NumberOfInputs &&
/// (!Slave ||
/// (unwrapAgent(*Slave.)Kind == rosa::deluxe::atoms::SensorKind &&
/// static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (MasterOutputTypes[Pos] == TypeNumberOf<unit_t>::Value ||
/// static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).MasterInputType
/// == MasterOutputTypes[Pos])) ||
/// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind &&
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).OutputType ==
/// InputTypes[Pos] &&
/// (MasterOutputTypes[Pos] == TypeNumberOf<unit_t>::Value ||
/// static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).MasterInputType ==
/// MasterOutputTypes[Pos])))
/// \endcode
void registerSlave(const size_t Pos,
const Optional<AgentHandle> Slave) noexcept;
/// Tells the position of a registered *slave*.
///
/// \param Slave \c rosa::AgentHandle for the *slave* to check
///
/// \return position of \p Slave if it is registered and found,
/// \c rosa::deluxe::DeluxeAgent::NumberOfInputs otherwise.
size_t positionOfSlave(AgentHandle Slave) const noexcept;
private:
/// Sends a value to the *master* of \p this object.
///
/// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Master if it
/// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function
/// does nothing otherwise.
///
/// \tparam T type of the value to send
///
/// \param Value value to send
///
/// \pre \p T matches \c rosa::deluxe::DeluxeiAgent::OutputType: \code
/// OutputType == TypeNumberOf<T>::Value
/// \endcode
template <typename T> void sendToMaster(const T &Value) noexcept;
/// Sends a value to a *slave* of \p this object at position \p Pos.
///
/// \p Value is getting sent to \c rosa::deluxe::DeluxeAgent::Slaves[Pos] if
/// it contains a valid handle. The function does nothing otherwise.
///
/// \tparam T type of the value to send
///
/// \param Pos the position of the *slave* to send \p Value to
/// \param Value value to send
///
/// \pre \p Pos is a valid *slave* position and \p T matches \c
/// rosa::deluxe::DeluxeiAgent::MasterOutputTypes[Pos]: \code
/// Pos < NumberOfMasterOutputs &&
/// MasterOutputTypes[Pos] == TypeNumberOf<T>::Value
/// \endcode
template <typename T>
void sendToSlave(const size_t Pos, const T &Value) noexcept;
/// Generates the next output by processing current input values upon trigger
/// from the system.
///
/// Executes \c rosa::deluxe::DeluxeAgent::FP.
///
/// \note The only argument is a \c rosa::AtomConstant, hence its actual
/// value is ignored.
void handleTrigger(atoms::Trigger) noexcept;
/// Stores a new input value from a *slave*.
///
/// The function stores \p Value in \c rosa::deluxe::DeluxeAgent::InputValues
/// at the position associated to \p Id in
/// \c rosa::deluxe::DeluxeAgent::SlaveIds and also sets the corresponding
/// flag in \c rosa::deluxe::DeluxeAgent::InputChanged.
///
/// \note Utilized by member functions of group \c DeluxeAgentInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of *slave*
/// \param Value the input value to store
///
/// \pre The *slave* with \p Id is registered and the input from it is
/// expected to be of type \p T: \code
/// SlaveIds.find(Id) != SlaveIds.end() &&
/// InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf<T>::Value
/// \endcode
template <typename T> void saveInput(id_t Id, T Value) noexcept;
/// Stores a new input value from the *master*.
///
/// The function stores \p Value in \c
/// rosa::deluxe::DeluxeAgent::MasterInputValue and also sets the
/// corresponding flag \c rosa::deluxe::DeluxeAgent::MasterInputChanged.
///
/// \note Utilized by member functions of group \c
/// DeluxeAgentMasterInputHandlers.
///
/// \tparam T type of input to store
///
/// \param Id unique identifier of the *master*
/// \param Value the input value to store
///
/// \pre The *master* with \p Id is registered and the input from the *master*
/// is expected to be of type \p T: \code
/// Master && masterId() == Id && MasterInputType == TypeNumberOf<T>::Value
/// \endcode
template <typename T> void saveMasterInput(id_t Id, T Value) noexcept;
/// \defgroup DeluxeAgentInputHandlers Input handlers of
/// rosa::deluxe::DeluxeAgent
///
/// Definition of member functions handling messages from *slaves* with
/// different types of input
///
/// A *master* generally needs to be prepared to deal with values of any
/// built-in type to handle messages from its *slaves*. Each type requires a
/// separate message handler, which are implemented by these functions. The
/// functions instantiate \c rosa::deluxe::DeluxeAgent::saveInput 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
/// DASLAVEHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes.
///
///@{
DASLAVEHANDLERDEF(AtomValue)
DASLAVEHANDLERDEF(int16_t)
DASLAVEHANDLERDEF(int32_t)
DASLAVEHANDLERDEF(int64_t)
DASLAVEHANDLERDEF(int8_t)
DASLAVEHANDLERDEFN(long double, long_double)
DASLAVEHANDLERDEFN(std::string, std__string)
DASLAVEHANDLERDEF(uint16_t)
DASLAVEHANDLERDEF(uint32_t)
DASLAVEHANDLERDEF(uint64_t)
DASLAVEHANDLERDEF(uint8_t)
DASLAVEHANDLERDEF(unit_t)
DASLAVEHANDLERDEF(bool)
DASLAVEHANDLERDEF(double)
DASLAVEHANDLERDEF(float)
/// @}
/// \defgroup DeluxeAgentMasterInputHandlers Master-input handlers of
/// rosa::deluxe::DeluxeAgent
///
/// 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, except for \c rosa::unit_t, 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::DeluxeAgent::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
/// DAMASTERHANDLERDEF.
///
/// \note Keep these definitions in sync with \c rosa::BuiltinTypes; but do no
/// include \c rosa::unit_t.
///
///@{
DAMASTERHANDLERDEF(AtomValue)
DAMASTERHANDLERDEF(int16_t)
DAMASTERHANDLERDEF(int32_t)
DAMASTERHANDLERDEF(int64_t)
DAMASTERHANDLERDEF(int8_t)
DAMASTERHANDLERDEFN(long double, long_double)
DAMASTERHANDLERDEFN(std::string, std__string)
DAMASTERHANDLERDEF(uint16_t)
DAMASTERHANDLERDEF(uint32_t)
DAMASTERHANDLERDEF(uint64_t)
DAMASTERHANDLERDEF(uint8_t)
DAMASTERHANDLERDEF(bool)
DAMASTERHANDLERDEF(double)
DAMASTERHANDLERDEF(float)
/// @}
};
/// Anonymous namespace with implementation for
/// \c rosa::deluxe::DeluxeAgent::inputTypesMatch and \c
/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch, consider it private.
namespace {
/// Template \c struct whose specializations provide a recursive implementation
/// for \c TypesMatchList.
///
/// \tparam As types to match
template <typename... As> struct TypesMatchImpl;
/// Template specialization for the general case, when at least one type is to
/// be matched.
///
/// \tparam A first type to match
/// \tparam As further types to match
template <typename A, typename... As> struct TypesMatchImpl<A, As...> {
/// Tells whether types \p A, \p As... match \c rosa::TypeNumber values
/// stored in \p Types starting at position \p Pos.
///
/// The function has got a recursive implementation: it matches the first
/// type \p A against \c rosa::TypeNumber at position \p Pos of \p Types, then
/// further types \p As.. are matched recursively starting at position
/// \c (Pos + 1).
///
/// \param Types container of \c rosa::TypeNumber values to match types
/// against
/// \param Pos position in \p Types to start matching at
///
/// \return if types \p A, \p As... match \c rosa::TypeNumber values stored
/// in \p Types starting at position \p Pos
static bool f(const std::vector<TypeNumber> &Types, size_t Pos) noexcept {
return Pos < Types.size() && TypeNumberOf<A>::Value == Types[Pos] &&
TypesMatchImpl<As...>::f(Types, Pos + 1);
}
};
/// Template specialization for the terminal case, when no type remains to
/// check.
template <> struct TypesMatchImpl<> {
/// Tells whether \p Pos is the number of values stored in \p Types.
///
/// In this terminal case, there is no more types to match because all the
/// types are supposed to be already matched successfully. The whole list of
/// types already matched is a complete match if it covers all values in
/// \p Types. That is true if \p Pos points exactly to the end of \p Types.
///
/// \param Types container of \c rosa::TypeNumber values to match types
/// against
/// \param Pos position in \p Types to start matching at
///
/// \return if \p Pos is the number of values stored in \p Types
static bool f(const std::vector<TypeNumber> &Types, size_t Pos) noexcept {
return Pos == Types.size();
}
};
/// Template \c struct that provides an implementation for \c
/// rosa::deluxe::DeluxeAgent::inputTypesMatch and \c
/// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch.
///
/// \note Match a list of types \p As... against a \c std::vector of
/// \c rosa::TypeNumber values, \c Types, like \code
/// bool match = TypesMatchList<TypeList<As...>>::f(Types);
/// \endcode
///
/// \tparam As \c rosa::TypeList that contains types to match
template <typename As> struct TypesMatchList;
/// Template specialization implementing the feature.
///
/// \tparam As \c rosa::TypeList that contains types to match
template <typename... As> struct TypesMatchList<TypeList<As...>> {
/// Tells whether types \p As... match \c rosa::TypeNumber values
/// stored in \p Types.
///
/// The function unwraps the types from \c rosa::TypeList and utilizes \c
/// TypesMatchImpl to do the check.
///
/// \param Types container of \c rosa::TypeNumber values to match types
/// against
///
/// \return if types \p As... match \c rosa::TypeNumber values stored
/// in \p Types
static bool f(const std::vector<TypeNumber> &Types) noexcept {
return TypesMatchImpl<As...>::f(Types, 0);
}
};
} // End namespace
template <typename As>
bool DeluxeAgent::inputTypesMatch(void) const noexcept {
return TypesMatchList<As>::f(InputTypes);
}
template <typename Ts>
bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept {
return TypesMatchList<Ts>::f(MasterOutputTypes);
}
template <typename... As, size_t... S0>
std::tuple<std::pair<As, bool>...>
DeluxeAgent::prepareCurrentInputs(Seq<S0...>) const noexcept {
// Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch
// inside \c ASSERT because of the comma in its template argument list.
auto MFP = &DeluxeAgent::inputTypesMatch<TypeList<As...>>;
ASSERT(inv() && (this->*MFP)() && sizeof...(As) == sizeof...(S0));
+ // The below should hold because of the above, just leave it for sanity check.
+ ASSERT((true && ... && static_cast<size_t>(static_cast<token_size_t>(S0))));
return std::make_tuple(
- std::make_pair(*static_cast<const As *>(InputValues->pointerTo(S0)),
+ std::make_pair(*static_cast<const As *>(
+ InputValues->pointerTo(static_cast<token_size_t>(S0))),
InputChanged[S0])...);
}
template <typename T, typename... Ts, typename... As, size_t... S0>
std::tuple<Optional<T>, Optional<Ts>...> DeluxeAgent::invokeWithTuple(
std::function<
std::tuple<Optional<T>, Optional<Ts>...>(std::pair<As, bool>...)>
F,
const std::tuple<std::pair<As, bool>...> Args, Seq<S0...>) noexcept {
ASSERT(sizeof...(As) == sizeof...(S0));
return F(std::get<S0>(Args)...);
}
template <size_t Offset, size_t Pos, typename... Ts>
void DeluxeAgent::handleMasterOutputAtPos(
const std::tuple<Optional<Ts>...> &Output) noexcept {
// Need to indirectly reference \c
// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch inside \c ASSERT because
// of the comma in its template argument list.
auto MOTMFP = &DeluxeAgent::masterOutputTypesMatch<
typename TypeListDrop<Offset, TypeList<Ts...>>::Type>;
ASSERT(inv() && (this->*MOTMFP)() && Pos < NumberOfMasterOutputs);
// Do not do anything for master-output type \c rosa::unit_t.
if (!std::is_same<typename TypeListAt<TypeList<Ts...>, Offset + Pos>::Type,
unit_t>::value) {
const auto MasterOutput = std::get<Offset + Pos>(Output);
if (MasterOutput) {
sendToSlave(Pos, *MasterOutput);
}
}
}
template <size_t Offset, typename... Ts, size_t... S0>
void DeluxeAgent::handleMasterOutputs(const std::tuple<Optional<Ts>...> &Output,
Seq<S0...>) noexcept {
// Need to indirectly reference \c
// rosa::deluxe::DeluxeAgent::masterOutputTypesMatch inside \c ASSERT because
// of the comma in its template argument list.
auto MOTMFP = &DeluxeAgent::masterOutputTypesMatch<
typename TypeListDrop<Offset, TypeList<Ts...>>::Type>;
ASSERT(inv() && (this->*MOTMFP)() && sizeof...(S0) == NumberOfMasterOutputs);
// Handle each master-output position in a fold expression.
(handleMasterOutputAtPos<Offset, S0>(Output), ...);
}
template <typename MT, typename T, typename... Ts, typename... As>
DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions(
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept {
// Need to indirectly reference \c rosa::deluxe::DeluxeAgent::inputTypesMatch
// and \c rosa::deluxe::DeluxeAgent::masterOutputTypesMatch inside \c ASSERT
// because of the comma in their respective template argument lists.
auto ITMFP = &DeluxeAgent::inputTypesMatch<TypeList<As...>>;
auto MOTMFP = &DeluxeAgent::masterOutputTypesMatch<TypeList<Ts...>>;
ASSERT(MasterInputType == TypeNumberOf<MT>::Value &&
OutputType == TypeNumberOf<T>::Value && (this->*ITMFP)() &&
(this->*MOTMFP)());
return [ this, MF, F ]() noexcept {
// \note These indices work for both inputs and master-outputs.
using Indices = typename GenSeq<sizeof...(As)>::Type;
// Handle master-input.
// Do not do anything for master-input type \c rosa::unit_t.
if (MasterInputType != TypeNumberOf<unit_t>::Value) {
LOG_TRACE_STREAM << "DeluxeAgent " << Name << " handles master-input."
<< std::endl;
const auto MasterInputArg = std::make_pair(
*static_cast<const MT *>(MasterInputValue->pointerTo(0)),
MasterInputChanged);
MasterInputChanged = false;
const std::tuple<Optional<Ts>...> MasterOutput = MF(MasterInputArg);
handleMasterOutputs<0>(MasterOutput, Indices());
}
// Handle inputs.
LOG_TRACE_STREAM << "DeluxeAgent " << Name << " handles input."
<< std::endl;
const auto InputArgs = prepareCurrentInputs<As...>(Indices());
std::fill(InputChanged.begin(), InputChanged.end(), false);
const std::tuple<Optional<T>, Optional<Ts>...> Output =
invokeWithTuple(F, InputArgs, Indices());
const auto OutputToMaster = std::get<0>(Output);
if (OutputToMaster) {
sendToMaster(*OutputToMaster);
}
handleMasterOutputs<1>(Output, Indices());
};
}
template <typename MT, typename T, typename... Ts, typename... As, typename>
DeluxeAgent::DeluxeAgent(
const AtomValue Kind, const id_t Id, const std::string &Name,
MessagingSystem &S,
std::function<std::tuple<Optional<Ts>...>(std::pair<MT, bool>)> &&MF,
std::function<std::tuple<Optional<T>, Optional<Ts>...>(
std::pair<As, bool>...)> &&F) noexcept
: Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger),
DASLAVEHANDLERREF(AtomValue), DASLAVEHANDLERREF(int16_t),
DASLAVEHANDLERREF(int32_t), DASLAVEHANDLERREF(int64_t),
DASLAVEHANDLERREF(int8_t), DASLAVEHANDLERREF(long_double),
DASLAVEHANDLERREF(std__string), DASLAVEHANDLERREF(uint16_t),
DASLAVEHANDLERREF(uint32_t), DASLAVEHANDLERREF(uint64_t),
DASLAVEHANDLERREF(uint8_t), DASLAVEHANDLERREF(unit_t),
DASLAVEHANDLERREF(bool), DASLAVEHANDLERREF(double),
DASLAVEHANDLERREF(float), DAMASTERHANDLERREF(AtomValue),
DAMASTERHANDLERREF(int16_t), DAMASTERHANDLERREF(int32_t),
DAMASTERHANDLERREF(int64_t), DAMASTERHANDLERREF(int8_t),
DAMASTERHANDLERREF(long_double), DAMASTERHANDLERREF(std__string),
DAMASTERHANDLERREF(uint16_t), DAMASTERHANDLERREF(uint32_t),
DAMASTERHANDLERREF(uint64_t), DAMASTERHANDLERREF(uint8_t),
DAMASTERHANDLERREF(bool), DAMASTERHANDLERREF(double),
DAMASTERHANDLERREF(float)),
OutputType(TypeNumberOf<T>::Value), NumberOfInputs(sizeof...(As)),
MasterInputType(TypeNumberOf<MT>::Value),
NumberOfMasterOutputs(NumberOfInputs),
InputTypes({TypeNumberOf<As>::Value...}),
InputChanged(NumberOfInputs, false),
InputValues(new TokenizedStorage<As...>()), MasterInputChanged(false),
MasterInputValue(new TokenizedStorage<MT>()),
MasterOutputTypes({TypeNumberOf<Ts>::Value...}),
FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F))),
Slaves(NumberOfInputs) {
ASSERT(Kind == atoms::AgentKind);
LOG_TRACE("DeluxeAgent is created.");
ASSERT(inv());
}
template <typename T>
void DeluxeAgent::sendToMaster(const T &Value) noexcept {
ASSERT(inv() && OutputType == TypeNumberOf<T>::Value);
// There is a handle and the referred *master* is in a valid state.
if (Master && *Master) {
Master->sendMessage(Message::create(atoms::Slave::Value, Id, Value));
}
}
template <typename T>
void DeluxeAgent::sendToSlave(const size_t Pos, const T &Value) noexcept {
ASSERT(inv() && Pos < NumberOfMasterOutputs &&
MasterOutputTypes[Pos] == TypeNumberOf<T>::Value);
// There is a handle and the referred *slave* is in a valid state.
auto Slave = Slaves[Pos];
if (Slave && *Slave) {
Slave->sendMessage(Message::create(atoms::Master::Value, Id, Value));
}
}
template <typename T> void DeluxeAgent::saveInput(id_t Id, T Value) noexcept {
ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() &&
InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf<T>::Value);
size_t Pos = SlaveIds.at(Id);
- *static_cast<T *>(InputValues->pointerTo(Pos)) = Value;
+ ASSERT(static_cast<size_t>(static_cast<token_size_t>(Pos)) == Pos);
+ *static_cast<T *>(InputValues->pointerTo(static_cast<token_size_t>(Pos))) =
+ Value;
InputChanged[Pos] = true;
ASSERT(inv());
}
template <typename T>
void DeluxeAgent::saveMasterInput(id_t Id, T Value) noexcept {
ASSERT(inv() && Master && masterId() == Id &&
MasterInputType == TypeNumberOf<T>::Value);
*static_cast<T *>(MasterInputValue->pointerTo(0)) = Value;
MasterInputChanged = true;
ASSERT(inv());
}
} // End namespace deluxe
} // End namespace rosa
#undef DASLAVEHANDLEREF
#undef DAMASTERHANDLEREF
#undef DASLAVEHANDLEDEF
#undef DAMASTERHANDLEDEF
#undef DASLAVEHANDLEDEFN
#undef DAMASTERHANDLEDEFN
#undef DASLAVEHANDLENAME
#undef DAMASTERHANDLENAME
#endif // ROSA_DELUXE_DELUXEAGENT_HPP
diff --git a/include/rosa/support/tokenized_storages.hpp b/include/rosa/support/tokenized_storages.hpp
index 36d10fb..63cf40f 100755
--- a/include/rosa/support/tokenized_storages.hpp
+++ b/include/rosa/support/tokenized_storages.hpp
@@ -1,500 +1,501 @@
//===-- rosa/support/tokenized_storages.hpp ---------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/tokenized_storages.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Definition of storage helper template for storing values in a
/// type-safe way based on type tokens.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TOKENIZED_STORAGES_HPP
#define ROSA_SUPPORT_TOKENIZED_STORAGES_HPP
#include "rosa/support/type_token.hpp"
#include <memory>
#include <vector>
namespace rosa {
/// Defines a simple interface for storing and accessing values of different
/// types.
///
/// While the interface provides features to access values and know their
/// types, it is the users responsibility to use particular values according to
/// their actual types. No facilities for type-safe access of values is
/// provided by the class.
///
/// \see \c rosa::TokenizedStorage for a type-safe specialization of the
/// interface.
class AbstractTokenizedStorage {
protected:
/// Protected constructor restricts instantiation for derived classes.
AbstractTokenizedStorage(void) noexcept = default;
public:
/// No copying and moving of \c rosa::AbstractTokenizedStorage instances.
///@{
AbstractTokenizedStorage(const AbstractTokenizedStorage&) = delete;
AbstractTokenizedStorage &operator=(const AbstractTokenizedStorage&) = delete;
AbstractTokenizedStorage(AbstractTokenizedStorage&& Other) = delete;
AbstractTokenizedStorage &operator=(AbstractTokenizedStorage&&) = delete;
///@}
/// Destroys \p this object.
virtual ~AbstractTokenizedStorage(void) noexcept = default;
/// Tells how many values are stored in \p this object.
///
/// \return number of values stored in \p this object
virtual size_t size(void) const noexcept = 0;
/// Tells the type of the value stored at a position.
///
/// \param Pos the index of the value whose type is to returned
///
/// \return \c rosa::TypeNumber for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
- virtual TypeNumber typeAt(const size_t Pos) const noexcept = 0;
+ virtual TypeNumber typeAt(const token_size_t Pos) const noexcept = 0;
/// Provides an untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
- virtual void *pointerTo(const size_t Pos) noexcept = 0;
+ virtual void *pointerTo(const token_size_t Pos) noexcept = 0;
/// Provides a constant untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return constant untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Offsets.size()
/// \endcode
- virtual const void *pointerTo(const size_t Pos) const noexcept = 0;
+ virtual const void *pointerTo(const token_size_t Pos) const noexcept = 0;
};
/// Template class storing values and providing dynamic type-safe access to
/// them in a lightweight way based on type tokens.
///
/// \see rosa/support/type_token.hpp
///
/// \tparam Types types whose values are to be stored
template <typename... Types> class TokenizedStorage;
/// Nested namespace with implementation for \c rosa::TokenizedStorage, consider
/// it private.
namespace {
/// Initializes a pre-allocated memory area with values from constant lvalue
/// references.
///
/// \tparam Types types whose values are to be stored
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts the values to store in \p Arena
///
/// \note \p Arena needs to be a valid pointer to a memory area big enough for
/// values of \p Types.
template <typename... Types>
inline void createArenaElements(void *const Arena,
const Types &... Ts) noexcept;
/// \defgroup createLvalueArenaElement Implementation of creating lvalue arena elements
///
/// Stores values from constant lvalue references into a pre-allocated memory
/// area.
///
/// \note To be used by the implementation of \c createArenaElements.
///
/// \todo Document these functions.
///@{
/// \note This terminal case is used for both constant lvalue references and
/// value references.
template <size_t Pos>
inline void createArenaElement(void *const,
const std::vector<size_t> &Offsets) {
ASSERT(Pos == Offsets.size());
}
template <size_t Pos, typename Type, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets,
const Type &T, const Types &... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
new (static_cast<Type *>(static_cast<void *>(static_cast<uint8_t *>(Arena) +
Offsets[Pos]))) Type(T);
createArenaElement<Pos + 1>(Arena, Offsets, Ts...);
}
template <size_t Pos, AtomValue V, typename... Types>
inline void
createArenaElement(void *const Arena, const std::vector<size_t> &Offsets,
const AtomConstant<V> &, const Types &... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
*static_cast<AtomValue *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos])) = V;
createArenaElement<Pos + 1>(Arena, Offsets, Ts...);
}
///@}
/// Implementation of the template.
///
/// \tparam Types types of values to store
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts values to store in \p Arena
///
/// \pre \p Arena is not \p nullptr.
template <typename... Types>
inline void createArenaElements(void *const Arena,
const Types &... Ts) noexcept {
ASSERT(Arena != nullptr);
createArenaElement<0>(Arena, TokenizedStorage<Types...>::Offsets, Ts...);
}
/// Initializes a pre-allocated memory area with values from rvalue references.
///
/// \tparam Types types whose values are to be stored
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts the values to store in \p Arena
///
/// \note \p Arena needs to be a valid pointer to a memory area big enough for
/// values of \p Types.
template <typename... Types>
inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept;
/// \defgroup createRvalueArenaElement Implementation of creating rvalue arena elements
///
/// Stores values from rvalue references into a pre-allocated memory area.
///
/// \note To be used by the implementation of \c createArenaElements.
///
/// \todo Document these functions.
///@{
template <size_t Pos, typename Type, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets, Type &&T,
Types &&... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
new (static_cast<Type *>(static_cast<void *>(
static_cast<uint8_t *>(Arena) + Offsets[Pos]))) Type(std::move(T));
createArenaElement<Pos + 1>(Arena, Offsets, std::move(Ts)...);
}
template <size_t Pos, AtomValue V, typename... Types>
inline void createArenaElement(void *const Arena,
const std::vector<size_t> &Offsets,
AtomConstant<V> &&, Types &&... Ts) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
*static_cast<AtomValue *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos])) = V;
createArenaElement<Pos + 1>(Arena, Offsets, std::move(Ts)...);
}
///@}
/// Implementation of the template.
///
/// \tparam Types types of values to store
///
/// \param Arena pre-allocated memory area to store values to
/// \param Ts values to store in \p Arena
///
/// \pre \p Arena is not \c nullptr.
template <typename... Types>
inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept {
ASSERT(Arena != nullptr);
createArenaElement<0>(Arena, TokenizedStorage<Types...>::Offsets,
std::move(Ts)...);
}
/// Destroys values allocated by \c createArenaElements.
///
/// \tparam Types types whose values are stored in \p Arena
///
/// \param Arena the memory area to destroy values from
///
/// \note \p Arena needs to be a valid pointer to a memory area where values of
/// \p Types are stored.
template <typename... Types>
inline void destroyArenaElements(void *const Arena) noexcept;
/// \defgroup destroyArenaElement Implementation of destroying arena elements
///
/// Destroys values from a memory area.
///
/// \note To be used by the implementation of \c destroyArenaElements.
///
/// \todo Document these functions.
///@{
template <size_t Pos>
inline void destroyArenaElement(void *const,
const std::vector<size_t> &Offsets) noexcept {
ASSERT(Pos == Offsets.size());
}
template <size_t Pos, typename Type, typename... Types>
inline void destroyArenaElement(void *const Arena,
const std::vector<size_t> &Offsets) noexcept {
ASSERT(Arena != nullptr && Pos < Offsets.size());
static_cast<Type *>(
static_cast<void *>(static_cast<uint8_t *>(Arena) + Offsets[Pos]))
->~Type();
destroyArenaElement<Pos + 1, Types...>(Arena, Offsets);
}
///@}
/// Implementation of the template.
///
/// \tparam Types types of values to destroy
///
/// \param Arena the memory area to destroy values from
///
/// \pre \p Arena is not \c nullptr.
template <typename... Types>
inline void destroyArenaElements(void *const Arena) noexcept {
ASSERT(Arena != nullptr);
destroyArenaElement<0, Types...>(Arena, TokenizedStorage<Types...>::Offsets);
}
} // End namespace
/// Implementation of the template \c rosa::TokenizedStorage as a
/// specialization of \c rosa::AbstractTokenizedStorage.
///
/// The class provides facilities for storing values and providing type-safe
/// access to them.
///
/// \tparam Types types of values to store
template <typename... Types>
class TokenizedStorage : public AbstractTokenizedStorage {
public:
/// \c rosa::Token for the stored values.
static constexpr Token ST =
TypeToken<typename std::decay<Types>::type...>::Value;
/// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena.
static const std::vector<size_t> Offsets;
private:
/// A BLOB storing all the values one after the other.
void *const Arena;
/// Generates byte offsets for accessing values stored in
/// \c rosa::TokenizedStorage::Arena.
///
/// \return \c std::vector containing byte offsets for accessing values stored
/// in \c rosa::TokenizedStorage::Arena
static std::vector<size_t> offsets(void) noexcept {
Token T = ST; // Need a mutable copy.
- const size_t N = lengthOfToken(T); // Number of types encoded in \c T.
+ const token_size_t N = lengthOfToken(T); // Number of types encoded in \c T.
std::vector<size_t> O(N); // Allocate vector of proper size.
// Do nothing for 0 elements.
if (N > 0) {
- size_t I = 0; // Start indexing from position \c 0.
+ token_size_t I = 0; // Start indexing from position \c 0.
O[0] = 0; // First offset is always \c 0.
while (I < N - 1) {
ASSERT(I + 1 < O.size() && lengthOfToken(T) == N - I);
// Calculate next offset based on the previous one.
// \note The offset of the last value is stored at `O[N - 1]`, which is
// set when `I == N - 2`. Hence the limit of the loop.
O[I + 1] = O[I] + sizeOfHeadOfToken(T);
dropHeadOfToken(T), ++I;
}
ASSERT(I + 1 == O.size() && lengthOfToken(T) == 1);
}
return O;
}
public:
/// Creates an instance with default values.
///
/// \note This constructor requires that all actual template arguments \p
/// Types... are default constructible.
TokenizedStorage(void) noexcept
: Arena(::operator new(sizeOfValuesOfToken(ST))) {
ASSERT(Arena != nullptr); // Sanity check.
createArenaElements(Arena, Types()...);
}
/// Creates an instance from constant lvalue references.
///
/// \param Ts values to store
TokenizedStorage(const Types &... Ts) noexcept
: Arena(::operator new(sizeOfValuesOfToken(ST))) {
ASSERT(Arena != nullptr); // Sanity check.
createArenaElements(Arena, Ts...);
}
/// Creates an instance from rvalue references.
///
/// \param Ts values to store
TokenizedStorage(Types &&... Ts) noexcept
: Arena(::operator new(sizeOfValuesOfToken(ST))) {
ASSERT(Arena != nullptr); // Sanity check.
createArenaElements(Arena, std::move(Ts)...);
}
/// No copying and moving of \c rosa::TokenizedStorage instances.
///
/// \note This restriction may be relaxed as moving should be easy to
/// implement, only requires the possiblity to validate Arena pointer.
///@{
TokenizedStorage(const TokenizedStorage&) = delete;
TokenizedStorage &operator=(const TokenizedStorage&) = delete;
TokenizedStorage(TokenizedStorage&& Other) = delete;
TokenizedStorage &operator=(TokenizedStorage&&) = delete;
///@}
// Destroys \p this object.
~TokenizedStorage(void) {
destroyArenaElements<Types...>(Arena);
::operator delete(Arena);
}
/// Tells how many values are stored in \p this object.
///
/// \return number of values stored in \p this object
size_t size(void) const noexcept override {
return Offsets.size();
}
/// Tells the type of the value stored at a position.
///
/// \param Pos the index of the value whose type is to returned
///
/// \return \c rosa::TypeNumber for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
- TypeNumber typeAt(const size_t Pos) const noexcept override {
+ TypeNumber typeAt(const token_size_t Pos) const noexcept override {
ASSERT(Pos < size());
Token TT = ST;
dropNOfToken(TT, Pos);
return headOfToken(TT);
}
/// Provides an untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < size()
/// \endcode
- void *pointerTo(const size_t Pos) noexcept override {
+ void *pointerTo(const token_size_t Pos) noexcept override {
ASSERT(Pos < size());
return static_cast<uint8_t *>(Arena) + Offsets[Pos];
}
/// Provides a constant untyped pointer for the value stored at a position.
///
/// \param Pos the index of the value to return an untyped pointer for
///
/// \return constant untyped pointer for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index:\code
/// Pos < Offsets.size()
/// \endcode
- const void *pointerTo(const size_t Pos) const noexcept override {
+ const void *pointerTo(const token_size_t Pos) const noexcept override {
ASSERT(Pos < size());
return static_cast<const uint8_t *>(Arena) + Offsets[Pos];
}
/// Tells if the value stored at a given index is of a given type.
///
/// \note Any \c rosa::AtomConstant is encoded in \c rosa::Token as
/// the \c rosa::AtomValue wrapped into it.
///
/// \tparam T type to match against
///
/// \param Pos index the type of the value at is to be matched against \p Type
///
/// \return if the value at index \p Pos of type \p T
///
/// \pre \p Pos is a valid index:\code
/// Pos < Offsets.size()
/// \endcode
template <typename T> bool isTypeAt(const size_t Pos) const noexcept {
ASSERT(Pos < size());
Token TT = ST;
dropNOfToken(TT, Pos);
return isHeadOfTokenTheSameType<T>(TT);
}
/// Gives a reference of a value of a given type stored at a given index.
///
/// \note The constant variant of the function relies on this implementation,
/// the function may not modify \p this object!
///
/// \tparam T type to give a reference of
///
/// \param Pos index to set the reference for
///
/// \return reference of \p T for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index and the value at index \p Pos is of type
/// \p T:
/// \code
/// Pos < Size && isTypeAt<T>(Pos)
/// \endcode
- template <typename T> T &valueAt(const size_t Pos) noexcept {
+ template <typename T> T &valueAt(const token_size_t Pos) noexcept {
ASSERT(Pos < size() && isTypeAt<T>(Pos));
return *static_cast<T *>(pointerTo(Pos));
}
/// Gives a constant reference of a value of a given type stored at a given
/// index.
///
/// \tparam T type to give a reference of
///
/// \param Pos index to set the reference for
///
/// \return constant reference of \p T for the value stored at index \p Pos
///
/// \pre \p Pos is a valid index and the value at index \p Pos is of type
/// \p T:
/// \code
/// Pos < Size && isTypeAt<T>(Pos)
/// \endcode
- template <typename T> const T &valueAt(const size_t Pos) const noexcept {
+ template <typename T>
+ const T &valueAt(const token_size_t Pos) const noexcept {
// \note Just use the non-const implementation as that does not modify
// \p this object.
return const_cast<TokenizedStorage *>(this)->valueAt<T>(Pos);
}
};
// Implementation of the static member field \c rosa::TokenizedStorage::Offsets.
template <typename... Types>
const std::vector<size_t>
TokenizedStorage<Types...>::Offsets = TokenizedStorage<Types...>::offsets();
} // End namespace rosa
#endif // ROSA_SUPPORT_TOKENIZED_STORAGES_HPP
diff --git a/include/rosa/support/type_token.hpp b/include/rosa/support/type_token.hpp
index ed753bf..ee08886 100644
--- a/include/rosa/support/type_token.hpp
+++ b/include/rosa/support/type_token.hpp
@@ -1,279 +1,288 @@
//===-- rosa/support/type_token.hpp -----------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/type_token.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Facilities for encoding TypeLists as unsigned integer values.
///
/// \note **On the compatibility between different versions of the type token
/// implementation:**
/// Different software versions produce compatible type tokens as long as
/// *backward compatibility* of \c rosa::BuiltinTypes is maintained (denoted by
/// \c rosa::TypeNumberVersion) and the type token implementation uses the same
/// type as \c rosa::token_t (boiling down to the same \c rosa::token::TokenBits
/// value) and the same \c rosa::token::RepresentationBits value. Thus,
/// interacting software need to cross-validate the aforementioned values to
/// check compatibility. Interoperation between compatible sofware is limited
/// to backward compatiblity, that is builtin types defined in both software
/// versions are handled correctly but a newer system may produce a type token
/// which is invalid in an old one. Therefore, tokens obtained from a compatible
/// remote system need to be validated.
///
//===----------------------------------------------------------------------===//
#ifndef ROSA_SUPPORT_TYPE_TOKEN_HPP
#define ROSA_SUPPORT_TYPE_TOKEN_HPP
#include "rosa/support/type_numbers.hpp"
namespace rosa {
/// Integer type to store type tokens.
///
/// \note The trade-off between the binary overhead of type encoding and the
/// maximal size of encodable lists can be tuned by using unsigned integer types
/// of different widths as \c rosa::token_t.
using token_t = uint64_t;
/// Sanity check in case someone would change \c rosa::token_t.
STATIC_ASSERT(std::is_unsigned<token_t>::value,
"token_t is not an unsigned integer");
/// Turn \c rosa::token_t into a strongly typed enumeration.
///
/// Values of \c rosa::token_t casted to \c rosa::Token can be used in a
/// type-safe way.
enum class Token : token_t {};
/// A type to cast tokens into in order to output them to streams as
/// numbers and not ASCII-codes.
///
/// \note Use it for safety, necessary for printing \c uint8_t values.
using printable_token_t = printable_t<token_t>;
/// Casts a \c rosa::Token into \c rosa::printable_token_t.
///
/// \param T \c rosa::Token to cast
#define PRINTABLE_TOKEN(T) static_cast<printable_token_t>(T)
/// Converts a \c rosa::Token into \c std::string.
///
/// \param T \c rosa::Token to convert
///
/// \return \c std::string representing \p T
inline std::string to_string(const Token T) {
return std::to_string(static_cast<token_t>(T));
}
/// Encloses constants related to the implementation of \c rosa::Token.
namespace token {
/// The number of bits in one \c rosa::Token.
constexpr size_t TokenBits = sizeof(Token) * 8;
/// The number of bits a builtin type can be uniquely encoded into, that is any
/// valid \c rosa::TypeNumber can fit into.
///
/// \note There is one extra bit position added for encoding so that providing a
/// better chance to maintain backward comaptibility when \c rosa::BuiltinTypes
/// is extended.
constexpr size_t RepresentationBits = log2(NumberOfBuiltinTypes) + 1;
/// Maximal size of uniquely tokenizable \c rosa::TypeList.
constexpr size_t MaxTokenizableListSize = TokenBits / RepresentationBits;
} // End namespace token
+/// Integer type to store length of a \c rosa::Token.
+///
+/// \note The narrowest unsigned integer type that is wide enough to
+/// represent \c rosa::token::MaxTokenizableListSize different values.
+using token_size_t = typename TypeListFind<
+ typename TypeListDrop<log2(token::MaxTokenizableListSize) / 8 + 1,
+ IntegerTypesBySize>::Type,
+ IsNotNoneType>::Type::Second;
+
/// \defgroup TypeListTokenImpl Implementation of rosa::TypeListToken
///
/// \brief Generates a \c rosa::Token for a squashed \c rosa::TypeList.
///
/// \note Only to be used by the implementation of \c rosa::TypeListToken.
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to generate \c rosa::Token for
template <typename List> struct TypeListTokenImpl;
/// Specialization for \c rosa::EmptyTypeList.
template <> struct TypeListTokenImpl<EmptyTypeList> {
static constexpr Token Value = static_cast<Token>(0);
};
/// Specialization for a non-empty \c rosa::TypeList.
template <typename T, typename... Ts>
struct TypeListTokenImpl<TypeList<T, Ts...>> {
static constexpr TypeNumber TN = TypeNumberOf<T>::Value;
// Check if the generated type number is valid.
STATIC_ASSERT(validTypeNumber(TN), "non-builtin type");
static constexpr Token Value = static_cast<Token>(
(static_cast<token_t>(TypeListTokenImpl<TypeList<Ts...>>::Value)
<< token::RepresentationBits) |
static_cast<type_nr_t>(TN));
};
///@}
/// \name TypeListToken
///
/// \brief Generates a \c rosa::Token for a \c rosa::TypeList.
///
/// \c rosa::Token for a \c rosa::TypeList \c List can be obtained as \code
/// TypeListToken<List>::Value
/// \endcode
///
/// \note The \c rosa::TypeList cannot have more than
/// \c rosa::token::MaxTokenizableListSize elements and must be a subset of
/// \c rosa::BuiltinTypes with respect to squashed integers.
///
/// \note A generated \c rosa::Token uniquely represents a list of types, except
/// for \c rosa::AtomConstant types. Observe that any \c rosa::AtomConstant is
/// encoded as the type \c rosa::AtomValue. The type information on all separate
/// \c rosa::AtomConstant types are lost and replaced by the \c rosa::AtomValue
/// type whose actual value needs to be used in order to obtain the original
/// \c rosa::AtomConstant type and so the full type information on the encoded
/// \c rosa::TypeList.
///
///@{
/// Declaration of the template.
///
/// \tparam List \c rosa::TypeList to generate \c rosa::Token for
template <typename List> struct TypeListToken;
/// Implementation using \c rosa::TypeListTokenImpl.
template <typename... Ts> struct TypeListToken<TypeList<Ts...>> {
/// \note \c rosa::TypeNumber is computed against \c rosa::squased_t, so let's
/// do the same here.
using List = typename SquashedTypeList<TypeList<Ts...>>::Type;
/// Check the length of the list here.
/// \note Type validation is done one-by-one in \c rosa::TypeListTokenImpl.
STATIC_ASSERT((TypeListSize<List>::Value <= token::MaxTokenizableListSize),
"too long list of types");
/// The \c rosa::Token for \p List.
static constexpr Token Value = TypeListTokenImpl<List>::Value;
};
///@}
/// Convenience template to generate \c rosa::Token for a list of types.
template <typename... Ts> using TypeToken = TypeListToken<TypeList<Ts...>>;
/// Tells if a given \c rosa::Token is valid.
///
/// A \c rosa::Token is considered valid if it can be decoded by the current
/// software.
///
/// \note Validation gives a correct result only when \p T was generated by a
/// compatible software.
///
/// \sa Note for type_token.hpp on compatibility.
///
/// \param T \c rosa::Token to validate
///
/// \return if \p T is valid
bool validToken(const Token T);
/// Tells if a \c rosa::Token does encode an empty list of types.
///
/// \param T \c rosa::Token to check
///
/// \return if \p T encodes an empty list of types
bool emptyToken(const Token T);
/// Tells how many types are encoded in a \c rosa::Token.
///
/// \param T \c rosa::Token to check
///
/// \return how many types are encoded in \p T
-size_t lengthOfToken(const Token T);
+token_size_t lengthOfToken(const Token T);
/// Tells the full memory size of the types encoded in a \c rosa::Token.
///
/// The full memory size of the types encoded in a \c rosa::Token is the sum of
/// the separate memory sizes of all the types encoded in the \c rosa::Token.
///
/// \param T \c rosa::Token to check
///
/// \return full memory size of the types encoded in \p T
///
/// \pre \p T is valid:\code
/// validToken(T)
/// \endcode
size_t sizeOfValuesOfToken(const Token T);
/// Extracts the \c rosa::TypeNumber of the first encoded type of a
/// \c rosa::Token.
///
/// \note The returned type number is not validated.
///
/// \param T \c rosa::Token to take the first \c rosa::TypeNumber from
///
/// \return the first \c rosa::TypeNumber encoded in \p T
inline TypeNumber headOfToken(const Token T) {
return static_cast<TypeNumber>(static_cast<token_t>(T) &
((1 << token::RepresentationBits) - 1));
}
/// Tells the memory size of the first type encoded in a \c rosa::Token.
///
/// \param T \c rosa::Token to check the first encoded type of
///
/// \return memory size of the first type encoded in \p T
///
/// \pre \p T is not empty and valid:\code
/// !empty(T) && validToken(T)
/// \endcode
size_t sizeOfHeadOfToken(const Token T);
/// Gives the textual representation of the first type encoded in a
/// \c rosa::Token.
///
/// \param T \c rosa::Token to take the name of its first encoded type
///
/// \return textual representation of the first type encoded in \p T
///
/// \pre \p T is not empty and valid:\code
/// !empty(T) && validToken(T)
/// \endcode
const char *nameOfHeadOfToken(const Token T);
/// Updates a \c rosa::Token by dropping its first encoded type.
///
/// \param [in,out] T \c rosa::Token to drop the first encoded type of
void dropHeadOfToken(Token &T);
/// Updates a \c rosa::Token by dropping a number of its first encoded types
///
/// \param [in,out] T \c rosa::Token to drop the first \p N encoded types of
/// \param N the number of types to drop from \p T
void dropNOfToken(Token &T, const size_t N);
/// Tells if the first encoded type of a \c rosa::Token is a given type.
///
/// \tparam Type type to match the first encoded type of \p T against
///
/// \param T \c rosa::Token whose first encoded type is to be matched against
/// \p Type
///
/// \return if the first encoded type of \p T is \p Type
///
/// \pre \p T is not empty and valid:\code
/// !empty(T) && validToken(T)
/// \endcode
template <typename Type> bool isHeadOfTokenTheSameType(const Token T) {
ASSERT(!emptyToken(T) && validToken(T));
return TypeNumberOf<Type>::Value == headOfToken(T);
}
} // End namespace rosa
#endif // ROSA_SUPPORT_TYPE_TOKEN_HPP
diff --git a/lib/deluxe/DeluxeAgent.cpp b/lib/deluxe/DeluxeAgent.cpp
index b32ac99..3a100fc 100755
--- a/lib/deluxe/DeluxeAgent.cpp
+++ b/lib/deluxe/DeluxeAgent.cpp
@@ -1,235 +1,237 @@
//===-- deluxe/DeluxeAgent.cpp ----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file deluxe/DeluxeAgent.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Implementation of rosa/deluxe/DeluxeAgent.hpp.
///
//===----------------------------------------------------------------------===//
#include "rosa/deluxe/DeluxeAgent.hpp"
#include "rosa/deluxe/DeluxeSensor.hpp"
#include <algorithm>
namespace rosa {
namespace deluxe {
bool DeluxeAgent::inv(void) const noexcept {
// Check number of inputs and master-outputs.
if (NumberOfInputs != NumberOfMasterOutputs) {
return false;
}
// Check container sizes.
if (!(InputTypes.size() == NumberOfInputs &&
InputChanged.size() == NumberOfInputs &&
InputValues->size() == NumberOfInputs &&
MasterOutputTypes.size() == NumberOfInputs // == NumberOfMasterOutputs
&& Slaves.size() == NumberOfInputs)) {
return false;
}
// Check *slave* types and validate *slave* registrations and reverse lookup
// information.
std::map<id_t, size_t> RefIds; // Build up a reference of SlaveIds in this.
for (size_t I = 0; I < NumberOfInputs; ++I) {
// First, validate input types at position \c I.
const TypeNumber T = InputTypes[I];
- if (InputValues->typeAt(I) != T) {
+ // The assert must hold if \p this object was successfuuly constructed.
+ ASSERT(static_cast<size_t>(static_cast<token_size_t>(I)) == I);
+ if (InputValues->typeAt(static_cast<token_size_t>(I)) != T) {
return false;
}
// Check the registered *slave* at position \c I.
const auto &Slave = Slaves[I];
// If \c Slave is empty, nothing to check.
if (!Slave)
continue;
// Prepare master-output related info for the *slave*.
const TypeNumber MT = MasterOutputTypes[I];
const bool hasMT = MT != TypeNumberOf<unit_t>::Value;
// \c Slave is not empty here.
// Check the `OutputType` and `MasterInputType` of the registered *slave*.
const auto &A = unwrapAgent(*Slave);
if (!((A.Kind == atoms::SensorKind &&
static_cast<const DeluxeSensor &>(A).OutputType == T &&
(!hasMT ||
static_cast<const DeluxeSensor &>(A).MasterInputType == MT)) ||
(A.Kind == atoms::AgentKind &&
static_cast<const DeluxeAgent &>(A).OutputType == T &&
(!hasMT ||
static_cast<const DeluxeAgent &>(A).MasterInputType == MT)))) {
return false;
}
// Validate that the *slave* is not registered more than once.
if (std::any_of(
Slaves.begin() + I + 1, Slaves.end(),
[&Slave](const Optional<AgentHandle> &O) { return O && *Slave == *O; })) {
return false;
}
// Build the content of \c RefIds.
RefIds.emplace(A.Id, I);
}
// Validate *slave* reverse lookup information against our reference.
if (RefIds != SlaveIds) {
return false;
}
// All checks were successful, the invariant is held.
return true;
}
DeluxeAgent::~DeluxeAgent(void) noexcept {
ASSERT(inv());
LOG_TRACE("Destroying DeluxeAgent...");
// Make sure \p this object is not a registered *slave*.
if (Master) {
ASSERT(unwrapAgent(*Master).Kind == atoms::AgentKind); // Sanity check.
DeluxeAgent &M = static_cast<DeluxeAgent&>(unwrapAgent(*Master));
ASSERT(M.positionOfSlave(self()) != M.NumberOfInputs); // Sanity check.
M.registerSlave(M.positionOfSlave(self()), {});
Master = {};
}
// Also, make sure \p this object is no acting *master*.
for (size_t Pos = 0; Pos < NumberOfInputs; ++Pos) {
registerSlave(Pos, {});
}
// Now there is no connection with other entities, safe to destroy.
}
id_t DeluxeAgent::masterId(void) const noexcept {
ASSERT(inv() && Master);
return unwrapAgent(*Master).Id;
}
Optional<AgentHandle> DeluxeAgent::master(void) const noexcept {
ASSERT(inv());
return Master;
}
void DeluxeAgent::registerMaster(const Optional<AgentHandle> _Master) noexcept {
ASSERT(inv() && (!_Master || unwrapAgent(*_Master).Kind == atoms::AgentKind));
Master = _Master;
ASSERT(inv());
}
TypeNumber DeluxeAgent::inputType(const size_t Pos) const noexcept {
ASSERT(inv() && Pos < NumberOfInputs);
return InputTypes[Pos];
}
TypeNumber DeluxeAgent::masterOutputType(const size_t Pos) const noexcept {
ASSERT(inv() && Pos < NumberOfMasterOutputs);
return MasterOutputTypes[Pos];
}
Optional<AgentHandle> DeluxeAgent::slave(const size_t Pos) const noexcept {
ASSERT(inv() && Pos < NumberOfInputs);
return Slaves[Pos];
}
void DeluxeAgent::registerSlave(const size_t Pos,
const Optional<AgentHandle> Slave) noexcept {
ASSERT(inv() && Pos < NumberOfInputs &&
(!Slave ||
(unwrapAgent(*Slave).Kind == atoms::SensorKind &&
static_cast<const DeluxeSensor &>(unwrapAgent(*Slave)).OutputType ==
InputTypes[Pos] &&
(MasterOutputTypes[Pos] == TypeNumberOf<unit_t>::Value ||
static_cast<const DeluxeSensor &>(unwrapAgent(*Slave))
.MasterInputType == MasterOutputTypes[Pos])) ||
(unwrapAgent(*Slave).Kind == atoms::AgentKind &&
static_cast<const DeluxeAgent &>(unwrapAgent(*Slave)).OutputType ==
InputTypes[Pos] &&
(MasterOutputTypes[Pos] == TypeNumberOf<unit_t>::Value ||
static_cast<const DeluxeAgent &>(unwrapAgent(*Slave))
.MasterInputType == MasterOutputTypes[Pos]))));
// If registering an actual *slave*, not just clearing the slot, make sure
// the same *slave* is not registered to another slot.
if (Slave) {
auto It = SlaveIds.find(unwrapAgent(*Slave).Id);
if (It != SlaveIds.end()) {
Slaves[It->second] = {};//Optional<AgentHandle>();
SlaveIds.erase(It);
}
}
// Obtain the place whose content is to be replaced with \p Slave
auto &OldSlave = Slaves[Pos];
// If there is already a *slave* registered at \p Pos, clear reverse lookup
// information for it, and make sure it no longer has \p this object as
// *master*.
if (OldSlave) {
auto &A = unwrapAgent(*OldSlave);
ASSERT(SlaveIds.find(A.Id) != SlaveIds.end()); // Sanity check.
SlaveIds.erase(A.Id);
if (A.Kind == atoms::AgentKind) {
static_cast<DeluxeAgent &>(A).registerMaster({});
} else {
ASSERT(A.Kind == atoms::SensorKind); // Sanity check.
static_cast<DeluxeSensor &>(A).registerMaster({});
}
}
// Register \p Slave at \p Pos.
OldSlave = Slave;
// If registering an actual *slave*, not just clearing the slot, register
// reverse lookup information for the new *slave*.
if (Slave) {
SlaveIds.emplace(unwrapAgent(*Slave).Id, Pos);
}
ASSERT(inv());
}
size_t DeluxeAgent::positionOfSlave(const AgentHandle Slave) const noexcept {
ASSERT(inv());
bool Found = false;
size_t Pos = 0;
while (!Found && Pos < NumberOfInputs) {
auto &ExistingSlave = Slaves[Pos];
if (ExistingSlave && *ExistingSlave == Slave) {
Found = true;
} else {
++Pos;
}
}
ASSERT(Found || Pos == NumberOfInputs); // Sanity check.
return Pos;
}
void DeluxeAgent::handleTrigger(atoms::Trigger) noexcept {
ASSERT(inv());
FP();
ASSERT(inv());
}
} // End namespace deluxe
} // End namespace rosa
diff --git a/lib/support/type_token.cpp b/lib/support/type_token.cpp
index 30c37c7..508b471 100644
--- a/lib/support/type_token.cpp
+++ b/lib/support/type_token.cpp
@@ -1,131 +1,131 @@
//===-- support/type_token.cpp ----------------------------------*- C++ -*-===//
//
// The RoSA Framework
//
//===----------------------------------------------------------------------===//
///
/// \file support/type_token.cpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
-/// \date 2017
+/// \date 2017-2019
///
/// \brief Implementation for rosa/support/type_token.hpp.
///
/// \todo Automatically generate proper cases for the switches with a big number
/// of handcrafted similar cases in them.
///
//===----------------------------------------------------------------------===//
#include "rosa/support/type_token.hpp"
namespace rosa {
using namespace token;
bool validToken(const Token T) {
Token TT = T;
bool Valid = true;
while (Valid && !emptyToken(TT)) {
Valid &= validTypeNumber(headOfToken(TT));
dropHeadOfToken(TT);
}
return Valid;
}
bool emptyToken(const Token T) { return static_cast<token_t>(T) == 0; }
-size_t lengthOfToken(const Token T) {
+token_size_t lengthOfToken(const Token T) {
Token TT = T;
- size_t N = 0;
+ token_size_t N = 0;
while (!emptyToken(TT)) {
++N;
dropHeadOfToken(TT);
}
return N;
}
size_t sizeOfValuesOfToken(const Token T) {
ASSERT(validToken(T));
Token TT = T;
size_t S = 0;
while (!emptyToken(TT)) {
S += sizeOfHeadOfToken(TT);
dropHeadOfToken(TT);
}
return S;
}
#define SIZECASE(N) \
{ \
case N: \
return TypeForNumber<static_cast<TypeNumber>(N)>::Size; \
}
size_t sizeOfHeadOfToken(const Token T) {
ASSERT(!emptyToken(T) && validToken(T));
switch (static_cast<type_nr_t>(headOfToken(T))) {
default: {
// Should never come here when \p T is valid and the case-list below covers
// \c rosa::BuiltinTypes.
ROSA_CRITICAL("unknown type number");
}
SIZECASE(1);
SIZECASE(2);
SIZECASE(3);
SIZECASE(4);
SIZECASE(5);
SIZECASE(6);
SIZECASE(7);
SIZECASE(8);
SIZECASE(9);
SIZECASE(10);
SIZECASE(11);
SIZECASE(12);
SIZECASE(13);
SIZECASE(14);
SIZECASE(15);
}
}
#define NAMECASE(N) \
{ \
case N: \
return TypeForNumber<static_cast<TypeNumber>(N)>::Name; \
}
const char *nameOfHeadOfToken(const Token T) {
ASSERT(!emptyToken(T) && validToken(T));
switch (static_cast<type_nr_t>(headOfToken(T))) {
default: {
// Should never come here when \p T is valid and the case-list below covers
// \c rosa::BuiltinTypes.
ROSA_CRITICAL("unknown type number");
}
NAMECASE(1);
NAMECASE(2);
NAMECASE(3);
NAMECASE(4);
NAMECASE(5);
NAMECASE(6);
NAMECASE(7);
NAMECASE(8);
NAMECASE(9);
NAMECASE(10);
NAMECASE(11);
NAMECASE(12);
NAMECASE(13);
NAMECASE(14);
NAMECASE(15);
}
}
void dropHeadOfToken(Token &T) {
T = static_cast<Token>(static_cast<token_t>(T) >> RepresentationBits);
}
void dropNOfToken(Token &T, const size_t N) {
T = static_cast<Token>(static_cast<token_t>(T) >> (N * RepresentationBits));
}
} // End namespace rosa

File Metadata

Mime Type
text/x-diff
Expires
Fri, Jul 4, 2:59 AM (6 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
157433
Default Alt Text
(108 KB)

Event Timeline