diff --git a/include/rosa/config/config.h b/include/rosa/config/config.h index e340325..487ceb9 100644 --- a/include/rosa/config/config.h +++ b/include/rosa/config/config.h @@ -1,56 +1,55 @@ /******************************************************************************* * * File: config.h * * Contents: Configuration nformation on the build of the library. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CONFIG_CONFIG_H #define ROSA_CONFIG_CONFIG_H -#include - -// Various preprocessor macros containing config information. #include "rosa/config/rosa_config.h" +#include + // This OS-specific block defines one of the following: // - ROSA_LINUX // - ROSA_WINDOWS // It also defines ROSA_POSIX for POSIX-compatible systems. #if defined(__linux__) #define ROSA_LINUX #elif defined(WIN32) || defined(_WIN32) #define ROSA_WINDOWS #else #error Platform and/or compiler not supported #endif #if defined(ROSA_LINUX) #define ROSA_POSIX #endif // Defining filenames in a project-relative way based on absolute paths. #include "rosa/config/project_path.hpp" #define __FILENAME__ (__FILE__ + project_relative_path_index(__FILE__)) // Convenience macros. #define ROSA_VOID_STMT static_cast(0) #define ROSA_IGNORE_UNUSED(x) static_cast(x) #define ROSA_CRITICAL(error) \ do { \ std::cerr << "[FATAL] " << __func__ << "@" << __FILENAME__ << ":" \ << __LINE__ << ": critical error: '" << (error) << "'" \ << std::endl; \ ::abort(); \ } while (false) #define ROSA_RAISE_ERROR(msg) throw std::runtime_error(msg) #endif // ROSA_CONFIG_CONFIG_H diff --git a/include/rosa/config/version.h b/include/rosa/config/version.h index af16211..d3aa020 100644 --- a/include/rosa/config/version.h +++ b/include/rosa/config/version.h @@ -1,35 +1,34 @@ /******************************************************************************* * * File: version.h * * Contents: Version information about the build of the library. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CONFIG_VERSION_H #define ROSA_CONFIG_VERSION_H -#include - -// Various preprocessor macros containing the information. #include "rosa/config/rosa_config.h" +#include + namespace rosa { // Returns a string containing the name of the library followed by its version. std::string library_string(void); // Returns a string containing the version number of the library. std::string version(void); // Returns a multi-line string containing verbose information on the library. std::string verbose_version(void); } // End namespace rosa #endif // ROSA_CONFIG_VERSION_H diff --git a/include/rosa/core/Invoker.hpp b/include/rosa/core/Invoker.hpp index b344ebe..2b031b2 100644 --- a/include/rosa/core/Invoker.hpp +++ b/include/rosa/core/Invoker.hpp @@ -1,158 +1,160 @@ /******************************************************************************* * * File: Invoker.hpp * * Contents: Definition of Invoker interface and its implementation. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_INVOKER_HPP #define ROSA_CORE_INVOKER_HPP #include "rosa/core/MessageMatcher.hpp" + #include "rosa/support/log.h" + #include #include namespace rosa { // Wraps a function and provides a simple interface to invoke the stored // function by passing actual arguments as a Message. // NOTE: Invoker instances are supposed to be owned by Message handlers, and // not being used directly from user code. class Invoker { protected: // Protected ctor, only subclasses can instantiate. Invoker(void) noexcept; public: // Dtor. virtual ~Invoker(void); // Enumeration of possible results of an invocation. enum class Result { NoMatch, Invoked }; // Type alias for a smart-pointer for Invoker. using invoker_t = std::unique_ptr; // Type alias for Result. using result_t = Result; // Tells if Msg can be used to invoke F. virtual bool match(const Message &Msg) const noexcept = 0; // Invokes F with Msg if Msg can be used to invoke F. virtual result_t operator()(const Message &Msg) const noexcept = 0; // Instantiates an implementation of Invoker with the given function. // NOTE: As there is no empty Message, no Invoker wraps a function without an // argument. template static invoker_t wrap(std::function &&F) noexcept; // Convenience template alias for casting callable stuff to function objects // for wrapping. // FIXME: Should make it possible to avoid using an explicit conversion for // the arguments of wrap. template using F = std::function; }; // Nested namespace with Invoker implementation and helper templates, consider // it private. namespace { // Implementation of the Invoker interface, for functions with different // signature. // NOTE: As there is no empty Message, no Invoker wraps a function without an // argument. template class InvokerImpl; // Empty struct just to store a sequence of numbers in compile time as template // arguments. template struct Seq {}; // Sequence generator, the general case when counting down by extending the // sequence. template struct GenSeq : GenSeq {}; // Sequence generator, the terminal case when storing the generated sequence // into Seq. template struct GenSeq<0, S...> { using Type = Seq; }; // Specialization of InvokerImpl template for std::function. // NOTE: No std::function. template class InvokerImpl> final : public Invoker { // Type alias for the stored function. using function_t = std::function; // Type alias for correctly typed argument-tuples as obtained from Messages. using args_t = std::tuple; // Alias for MessageMatcher for the arguments of the stored function. using Matcher = MsgMatcher; // The wrapped function. const function_t F; // Helper function invoking F by unpacking Args with the help of actual // template arguments. // PRE: sizeof...(S) == std::tuple_size::value template inline void invokeFunction(Seq, const args_t &Args) const noexcept; public: // Ctor. InvokerImpl(function_t &&F) noexcept : F(F) { ASSERT(bool(F)); // Sanity check. } // Dtor. ~InvokerImpl(void) = default; // Tells if Msg can be used to invoke F. bool match(const Message &Msg) const noexcept override { return Matcher::doesStronglyMatch(Msg); }; // Invokes F with Msg if Msg can be used to invoke F. result_t operator()(const Message &Msg) const noexcept override { if (match(Msg)) { LOG_TRACE("Invoking with matching arguments"); invokeFunction(typename GenSeq::Type(), Matcher::extractedValues(Msg)); return result_t::Invoked; } else { LOG_TRACE("Tried to invoke with non-matching arguments"); return result_t::NoMatch; } } }; template template void InvokerImpl>::invokeFunction( Seq, const args_t &Args) const noexcept { ASSERT(sizeof...(S) == std::tuple_size::value); // Sanity check. F(std::get(Args)...); } } // End namespace template Invoker::invoker_t Invoker::wrap(std::function &&F) noexcept { return std::unique_ptr( new InvokerImpl>(std::move(F))); } } // End namespace rosa #endif // ROSA_CORE_INVOKER_HPP diff --git a/include/rosa/core/Message.hpp b/include/rosa/core/Message.hpp index d02586c..c874a45 100644 --- a/include/rosa/core/Message.hpp +++ b/include/rosa/core/Message.hpp @@ -1,306 +1,307 @@ /******************************************************************************* * * File: Message.hpp * * Contents: Declaration of Message base-class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_MESSAGE_HPP #define ROSA_CORE_MESSAGE_HPP #include "rosa/support/log.h" #include "rosa/support/type_token.hpp" + #include #include 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. // Messages are immutable data objects, obtaining their data upon creation and // providing only constant references for the stored values. // NOTE: Any reference obtained from a Message instance remains valid only as // long as the owning Message object is not destroyed. class Message { protected: // Ctor. // NOTE: No implementation for empty list. template Message(const Type &, const Ts &...) noexcept; // No copy and move. Message(const Message &) = delete; Message(Message &&) = delete; Message &operator=(const Message &) = delete; Message &operator=(Message &&) = delete; public: // Type alias for a smart pointer for Message. using message_t = std::unique_ptr; // Factory function to locally create a Message instance from constant // references. template static message_t create(const Type &T, const Types &... Ts) noexcept; // Factory function to locally create a Message instance from movable // references. template static message_t create(Type &&T, Types &&... Ts) noexcept; // A valid, non-empty token representing the types of the values stored in // the Message. const Token T; // The number of types encoded in T, that is the number of values stored in // Message. const size_t Size; // Virtual dtor. virtual ~Message(void); // Tells if the value in position Pos is of type Type. // NOTE: Token encodes atoms as AtomValue and not directly AtomConstants. // PRE: Pos < Size template bool isTypeAt(const size_t Pos) const noexcept; // Gives a constant reference of the value of type Type in position Pos. // PRE: Pos < Size && isTypeAt(Pos) template const Type &getValueAt(const size_t Pos) const noexcept; protected: // Provides an untyped pointer for the value in position Pos. // PRE: Pos < Size virtual const void *getPointerTo(const size_t Pos) const noexcept = 0; }; // Nested namespace with an implementation for Message, consider it private. namespace { // Template class for an implementation of Message, definition below. template class LocalMessage; // Initializes Arena with values from const references of Types. // NOTE: Arena needs to be a valid pointer to a memory area big enough for // values of Types. template inline void createMessageElements(void *const Arena, const Types &... Ts) noexcept; // NOTE: This terminal case is used for both constant references and movable // references. template inline void createMessageElement(void *const, const std::vector &Offsets) { ASSERT(Pos == Offsets.size()); } template inline void createMessageElement(void *const Arena, const std::vector &Offsets, const AtomConstant &, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createMessageElement(Arena, Offsets, Ts...); } template inline void createMessageElement(void *const Arena, const std::vector &Offsets, const Type &T, const Types &... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast(static_cast(Arena) + Offsets[Pos]))) Type(T); createMessageElement(Arena, Offsets, Ts...); } template inline void createMessageElements(void *const Arena, const Type &T, const Types &... Ts) noexcept { ASSERT(Arena != nullptr); createMessageElement<0>(Arena, LocalMessage::Offsets, T, Ts...); } // Initializes Arena with values from movable references of Types. // NOTE: Arena needs to be a valid pointer to a memory area big enough for // values of Types. template inline void createMessageElements(void *const Arena, Types &&... Ts) noexcept; template inline void createMessageElement(void *const Arena, const std::vector &Offsets, AtomConstant &&, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); *static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) = V; createMessageElement(Arena, Offsets, std::move(Ts)...); } template inline void createMessageElement(void *const Arena, const std::vector &Offsets, Type &&T, Types &&... Ts) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); new (static_cast(static_cast( static_cast(Arena) + Offsets[Pos]))) Type(std::move(T)); createMessageElement(Arena, Offsets, std::move(Ts)...); } template inline void createMessageElements(void *const Arena, Type &&T, Types &&... Ts) noexcept { ASSERT(Arena != nullptr); createMessageElement<0>(Arena, LocalMessage::Offsets, std::move(T), std::move(Ts)...); } // Destroyes values of Types stored in Arena. // NOTE: Arena needs to be a valid pointer to a memory area where values of // Types are stored. template inline void destroyMessageElements(void *const Arena) noexcept; template inline void destroyMessageElement(void *const, const std::vector &Offsets) noexcept { ASSERT(Pos == Offsets.size()); } template inline void destroyMessageElement(void *const Arena, const std::vector &Offsets) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) ->~Type(); destroyMessageElement(Arena, Offsets); } template inline void destroyMessageElements(void *const Arena) noexcept { destroyMessageElement<0, Type, Types...>( Arena, LocalMessage::Offsets); } // Implementation of the template LocalMessage. Provides facilities for storing // values as a Message. template class LocalMessage : public Message { public: // Type Token for the stored values. // NOTE: Only for compile-time checks, static member is not defined. static constexpr Token ST = TypeToken::type, typename std::decay::type...>::Value; // Offsets required to access values in Arena. static const std::vector Offsets; private: // A BLOB storing all the values one after the other. void *const Arena; // Generates offset for accessing values in Arena. static std::vector getOffsets(void) noexcept { Token T = ST; // Need a mutable copy. size_t I = 0; // Start indexing from position 0. std::vector O(lengthOfToken(T)); // Allocate vector of proper size. O[0] = 0; // First offset is always 0. while (!emptyToken(T)) { ASSERT(I < O.size()); // Calculate next offset based on the previous one. O[I + 1] = O[I] + sizeOfHeadOfToken(T); dropHeadOfToken(T), ++I; } ASSERT(I == O.size()); return O; } public: // Ctor for constant references. LocalMessage(const Type &T, const Types &... Ts) noexcept : Message(T, Ts...), Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(this->T == ST && Size == Offsets.size() && Arena != nullptr); // Sanity check. createMessageElements(Arena, T, Ts...); } // Ctor for movable referecens. LocalMessage(Type &&T, Types &&... Ts) noexcept : Message(T, Ts...), Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(this->T == ST && Size == Offsets.size() && Arena != nullptr); // Sanity check. createMessageElements(Arena, std::move(T), std::move(Ts)...); } // Dtor. ~LocalMessage(void) { destroyMessageElements(Arena); ::operator delete(Arena); } // Provides an untyped pointer for the constant value at Pos. const void *getPointerTo(const size_t Pos) const noexcept override { ASSERT(Pos < Offsets.size()); return static_cast(Arena) + Offsets[Pos]; } }; // Implementation of the static member field Offsets. template const std::vector LocalMessage::Offsets = LocalMessage::getOffsets(); } // End namespace template Message::Message(const Type &, const Ts &...) noexcept : T(TypeToken::type, typename std::decay::type...>::Value), Size(lengthOfToken(T)) { ASSERT(validToken(T) && lengthOfToken(T) == (1 + sizeof...(Ts))); // Sanity check. LOG_TRACE("Creating Message with Token(" + to_string(T) + ")"); } template Message::message_t Message::create(const Type &T, const Types &... Ts) noexcept { return message_t(new LocalMessage(T, Ts...)); } template Message::message_t Message::create(Type &&T, Types &&... Ts) noexcept { return message_t( new LocalMessage(std::move(T), std::move(Ts)...)); } template bool Message::isTypeAt(const size_t Pos) const noexcept { ASSERT(Pos < Size); Token T_ = T; // NOLINT dropNOfToken(T_, Pos); return isHeadOfTokenTheSameType(T_); } template const Type &Message::getValueAt(const size_t Pos) const noexcept { ASSERT(Pos < Size && isTypeAt(Pos)); return *static_cast(getPointerTo(Pos)); } } // End namespace rosa #endif // ROSA_CORE_MESSAGE_HPP diff --git a/include/rosa/core/MessageHandler.hpp b/include/rosa/core/MessageHandler.hpp index c2bb605..2d93c99 100644 --- a/include/rosa/core/MessageHandler.hpp +++ b/include/rosa/core/MessageHandler.hpp @@ -1,113 +1,115 @@ /******************************************************************************* * * File: MessageHandler.hpp * * Contents: Definition of MessageHandler interface. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_MESSAGEHANDLER_HPP #define ROSA_CORE_MESSAGEHANDLER_HPP #include "rosa/core/Invoker.hpp" + #include "rosa/support/log.h" + #include namespace rosa { // Handles Message instances. A MessageHandler stores Invokers and tries to // apply Messages to them in the order of definition. The first matching // Invoker is invoked with the Message, after which handling of Message is // completed. // // For example, consider the following snippet: // // MessageHandler { // Invoker::F([](uint8_t) { /* ... */ }), // Invoker::F([](uint8_t) { /* Never invoked */ }) // }; // // Applying a Message with TypeList invokes the first function, and // the second function would never be invoked because any matching Message had // already been handled by the first one. class MessageHandler { // Using invoker_t from Invoker. using invoker_t = Invoker::invoker_t; // Type alias for a vector storing Invokers. using invokers_t = std::vector; // Stores the Invokers of this MessageHandler. const invokers_t Invokers; // Creates a container with Invokers from the given functions. template static inline invokers_t createInvokers(Fun &&F, Funs &&... Fs) noexcept; // Wraps F into an Invoker and stores it into I at position Pos. // PRE: Pos < I.size() template static inline void wrapFun(invokers_t &I, const size_t Pos, Fun &&F, Funs &&... Fs) noexcept; // Terminal case for function wrapper. // PRE: Pos == I.size(); static inline void wrapFun(invokers_t &I, const size_t Pos) noexcept; public: // Ctor, stores the given functions into the new MessageHandler instance. template MessageHandler(Fun &&F, Funs &&... Fs) noexcept; virtual ~MessageHandler(void); // Tells if there is any Invoker that can handle Msg. bool canHandle(const Message &Msg) const noexcept; // Applies Msg to the first Invoker which can handle Msg, and tells if there // was any. // NOTE: This operator finds the first applicable Invoker and invokes it with // Msg, while the member function canHandle only checks if there is any // Invoker that can be invoked with Msg. bool operator()(const Message &Msg) const noexcept; }; template MessageHandler::MessageHandler(Fun &&F, Funs &&... Fs) noexcept : Invokers(createInvokers(std::move(F), std::move(Fs)...)) { LOG_TRACE("MessageHandler is created"); } template MessageHandler::invokers_t MessageHandler::createInvokers(Fun &&F, Funs &&... Fs) noexcept { // Create a container with the required size and get all the functions // wrapped. invokers_t I(1 + sizeof...(Funs)); wrapFun(I, 0, std::move(F), std::move(Fs)...); return I; } template void MessageHandler::wrapFun(invokers_t &I, const size_t Pos, Fun &&F, Funs &&... Fs) noexcept { ASSERT(Pos < I.size()); // Sanity check. // Wrap the current function and continue with the rest. I[Pos] = Invoker::wrap(std::move(F)); wrapFun(I, Pos + 1, std::move(Fs)...); } void MessageHandler::wrapFun(invokers_t &I, const size_t Pos) noexcept { ASSERT(Pos == I.size()); // Sanity check. // Nothing to do here. } } // End namespace rosa #endif // ROSA_CORE_MESSAGEHANDLER_HPP diff --git a/include/rosa/core/MessageMatcher.hpp b/include/rosa/core/MessageMatcher.hpp index c636f3a..b3d6b2a 100644 --- a/include/rosa/core/MessageMatcher.hpp +++ b/include/rosa/core/MessageMatcher.hpp @@ -1,167 +1,168 @@ /******************************************************************************* * * File: MessageMatcher.hpp * * Contents: Implementation of MessageMatcher. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_MESSAGEMATCHER_HPP #define ROSA_CORE_MESSAGEMATCHER_HPP #include "rosa/core/Message.hpp" + #include namespace rosa { // Template class with static functions type-checking a Message instance and // extracting stored values from Message instsances into std::tuple instances // with matching type arguments. template struct MessageMatcher; // Definition of MessageMatcher for non-empty lists of types, like Message // itself. template struct MessageMatcher> { // Type Token associated with the give TypeList. static constexpr Token T = TypeToken::Value; // Tells if stored values in Msg are matching types given as // TypeList, considering exact AtomConstants instead of AtomValues. static inline bool doesStronglyMatch(const Message &Msg) noexcept; // Gives a std::tuple with references to values stored in a type-matching Msg. // PRE: doesStronglyMatch(Msg) static inline std::tuple extractedValues(const Message &Msg) noexcept; }; // Convenience template alias turning a list of types into a TypeList for // MessageMatcher. template using MsgMatcher = MessageMatcher>; // Nested namespace with implementation for features of MessageMatcher, // consider it private. namespace { // Helper struct implementing type-checking and value extraction for // MessageMatcher. template struct MessageMatcherImpl; // Specialization handling the empty list of types. template <> struct MessageMatcherImpl { static inline bool doesStronglyMatchFrom(const Message &Msg, const size_t Pos) noexcept { // Matching EmptyTypeList only if reached the end of the stored types. return Pos == Msg.Size; } static inline std::tuple<> extractedValuesFrom(const Message &Msg, const 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 handling an AtomValue in the head. template struct MessageMatcherImpl, Ts...>> { static inline bool doesHeadStronglyMatchAt(const Message &Msg, const size_t Pos) noexcept { // Matching an AtomConstant in the head if there is a type stored at Pos, // the stored type is AtomValue, and the corresponding value matches the // AtomValue V. return Pos < Msg.Size && Msg.isTypeAt(Pos) && Msg.getValueAt(Pos) == V; } static inline bool doesStronglyMatchFrom(const Message &Msg, const 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>::doesStronglyMatchFrom(Msg, Pos + 1); } static inline std::tuple &, const Ts &...> extractedValuesFrom(const Message &Msg, const size_t Pos) noexcept { // Extracting for a non-empty list with a matching AtomConstant in the head // by getting the encoded AtomConstant and concatenating it with values // extracted for the rest of the list. ASSERT(doesHeadStronglyMatchAt(Msg, Pos)); return std::tuple_cat( std::tie(AtomConstant::Value), MessageMatcherImpl>::extractedValuesFrom(Msg, Pos + 1)); } }; // Specialization handling an regular builtin type (not an AtomConstant) in the // head. template struct MessageMatcherImpl> { static inline bool doesHeadStronglyMatchAt(const Message &Msg, const size_t Pos) noexcept { // Matching the head if there is a type stored at Pos, and the stored type // is T. return Pos < Msg.Size && Msg.isTypeAt(Pos); } static inline bool doesStronglyMatchFrom(const Message &Msg, const 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>::doesStronglyMatchFrom(Msg, Pos + 1); } static inline std::tuple extractedValuesFrom(const Message &Msg, const 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.getValueAt(Pos)), MessageMatcherImpl>::extractedValuesFrom(Msg, Pos + 1)); } }; } // End namespace template bool MessageMatcher>::doesStronglyMatch( const Message &Msg) noexcept { // NOTE: Fail quick on the size and T, then match against list with squashed // integers as Tokens are generated. return (1 + sizeof...(Ts)) == Msg.Size && T == Msg.T && MessageMatcherImpl>::Type>::doesStronglyMatchFrom(Msg, 0); } template std::tuple MessageMatcher>::extractedValues( const Message &Msg) noexcept { ASSERT(doesStronglyMatch(Msg)); // NOTE: Match against list with squashed integers as Tokens are generated. return MessageMatcherImpl>::Type>::extractedValuesFrom(Msg, 0); } } // End namespace rosa #endif // ROSA_CORE_MESSAGEMATCHER_HPP diff --git a/include/rosa/core/System.hpp b/include/rosa/core/System.hpp index 0025531..3ea3fab 100644 --- a/include/rosa/core/System.hpp +++ b/include/rosa/core/System.hpp @@ -1,133 +1,135 @@ /******************************************************************************* * * File: System.hpp * * Contents: Declaration of System base-class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_SYSTEM_HPP #define ROSA_CORE_SYSTEM_HPP #include "rosa/config/config.h" + #include "rosa/support/debug.hpp" #include "rosa/support/log.h" + #include #include #include namespace rosa { // Forward declarations. class Unit; // Base-class for actual agent-systems, the class provides facitlities to keep // track of Units of the system. // NOTE: This class and any subclasses are supposed to provide thread-safe // interfaces. class System { public: // Signature of creator functions for Units. template using UnitCreator = std::function; // Returns an object implementing the interface defined by the class. static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: // Protected ctor, only subclasses can instantiate. System(const std::string &Name) noexcept; // No copy and move. System(const System&) = delete; System(System&&) = delete; System &operator=(const System&) = delete; System &operator=(System&&) = delete; public: // Dtor. Only a cleared System can be destroyed. // PRE: SystemCleaned virtual ~System(void); protected: // Sets SystemCleaned flag. Can be called only once and the System must not // have any live Units. // PRE: !SystemCleaned && empty() // POST: SystemCleaned void markCleaned(void) noexcept; // Creates a Unit instance with the given UnitCreator, using the given Name // if any, then registers the new Unit instance by calling the virtual // member function registerUnit. // NOTE: This function handles the member field CountUnits. // STATIC PRE: std::is_base_of::value // PRE: !SystemCleaned template T &createUnit(UnitCreator C) noexcept; // Registers the given Unit instance to the System. // PRE: !isUnitRegistered(U) // POST: isUnitRegistered(U) virtual void registerUnit(Unit &U) noexcept = 0; // Unregisters and destroys the given Unit. // PRE: isUnitRegistered(U) // POST: !isUnitRegistered(U) && 'U is destroyed' virtual void destroyUnit(Unit &U) noexcept = 0; // Returns if the given Unit is registered in the System. virtual bool isUnitRegistered(const Unit &U) const noexcept = 0; public: // The textual name of the System. const std::string Name; private: // Number of Units constructed in the system. // NOTE: Should never be decremented! std::atomic CountUnits; // Indicates that the System has been cleaned and is ready for destruction. // The field is initialized as 'false' and can be set by the member function // markCleaned. // Subclasses must set the flag upon destroying the System instance, which // indicates to the destructor of the base-class that all the managed // resources has been properly released. std::atomic SystemCleaned; public: // Returns the number of Units constructed in the System so far, // including those being already destroyed. size_t numberOfConstructedUnits(void) const noexcept; // Returns the number of live Units, that have been constructed and not // destroyed yet. virtual size_t numberOfLiveUnits(void) const noexcept = 0; // Returns if the System has no live Units. virtual bool empty(void) const noexcept = 0; }; template T &System::createUnit(UnitCreator C) noexcept { STATIC_ASSERT((std::is_base_of::value), "not a Unit"); if (SystemCleaned) { ROSA_CRITICAL("Trying to create a Unit in an already cleaned System (" + Name + ")"); } const uint64_t Id = ++CountUnits; T *U = C(Id, *this); registerUnit(*U); LOG_TRACE("Unit created and registered (" + U->FullName + ")"); return *U; } } // End namespace rosa #endif // ROSA_CORE_SYSTEM_HPP diff --git a/include/rosa/core/Unit.h b/include/rosa/core/Unit.h index cecb663..1541398 100644 --- a/include/rosa/core/Unit.h +++ b/include/rosa/core/Unit.h @@ -1,85 +1,86 @@ /******************************************************************************* * * File: Unit.h * * Contents: Declaration of Unit base-class. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_CORE_UNIT_H #define ROSA_CORE_UNIT_H #include "rosa/support/atom.hpp" + #include #include namespace rosa { // Forward declarations. class System; // Base class for every entity in the system that has to be identified and // traced. // NOTE: Life-cycle of Unit instances is supposed to be managed by a System, do // not create and destroy a Unit directly. class Unit { public: // Identifies the kind of the Unit. // NOTE: Kind is dependent on the owning System. const AtomValue Kind; // System-assigned unique identifier of the Unit instance, // based on the static member field CountUnits. const size_t Id; // Textual identifier of the Unit instance. Defaults to a text referring to // the Id value of the Unit, unless otherwise defined via a constructor // argument. The Name of a Unit is not necessarily unique in the system. const std::string Name; protected: // The owning System. System &S; public: // Full qualified name of the Unit instance. const std::string FullName; public: // Ctor. // PRE: !Name.empty() Unit(const AtomValue Kind, const size_t Id, const std::string &Name, System &S) noexcept; // No copy and move. Unit(const Unit &) = delete; Unit(Unit &&) = delete; Unit &operator=(const Unit &) = delete; Unit &operator=(Unit &&) = delete; // Dtor. virtual ~Unit(void); // Dumping the object into a string for tracing purposes, // subclasses are supposed to override this function. virtual std::string dump(void) const noexcept; protected: // Returns a reference to the owning System. // NOTE: Subclasses may override the function to return a reference of a // special System subtype. virtual System &system() const noexcept; }; // Helper function dumping the given Unit instance to the given output stream. std::ostream &operator<<(std::ostream &OS, const Unit &U); } // End namespace rosa #endif // ROSA_CORE_UNIT_H diff --git a/include/rosa/support/debug.hpp b/include/rosa/support/debug.hpp index dcbed4a..48048fe 100644 --- a/include/rosa/support/debug.hpp +++ b/include/rosa/support/debug.hpp @@ -1,89 +1,91 @@ /******************************************************************************* * * File: debug.hpp * * Contents: Facility for debugging * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_SUPPORT_DEBUG_HPP #define ROSA_SUPPORT_DEBUG_HPP #include "rosa/config/config.h" + #include "rosa/support/type_helper.hpp" #include "rosa/support/terminal_colors.h" + #include #include namespace rosa { // Returns an output stream to use for debugging. std::ostream &dbgs(void); // Prints an array to the ostream. template std::ostream &operator<<(std::ostream &os, const std::array &arr) { os << '['; for (unsigned I = 0; I < size; ++I) { if (I) { os << ','; } os << (PRINTABLE(T)) arr[I]; } os << ']'; return os; } } // End namespace rosa #ifndef ROSA_ENABLE_ASSERTIONS #define ASSERT(stmt) ROSA_IGNORE_UNUSED(stmt) #elif defined(ROSA_WINDOWS) #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ ::abort(); \ } \ ROSA_VOID_STMT #else // defined(ROSA_LINUX) #include #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":" \ << __LINE__ << ": requirement failed: '" << #stmt << "'" \ << std::endl; \ void *array[20]; \ auto bt_size = ::backtrace(array, 20); \ ::backtrace_symbols_fd(array, bt_size, 2); \ ::abort(); \ } \ ROSA_VOID_STMT #endif // defined(ROSA_ENABLE_ASSERTIONS) #ifndef NDEBUG #define DEBUG(X) \ do { \ X; \ } while (false) #define DEBUGVAR(V) \ do { \ rosa::dbgs() << rosa::terminal::Color::Default << #V << " (" \ << __FILENAME__ << ":" << __LINE__ << "): " << (V) \ << std::endl; \ } while (false) #else // defined(NDEBUG) #define DEBUG(X) ROSA_IGNORE_UNUSED(X) #define DEBUGVAR(X) ROSA_IGNORE_UNUSED(X) #endif // defined(NDEBUG) // Static assertions are always emitted to the code. #define STATIC_ASSERT(COND, DIAG) static_assert((COND), DIAG) #endif // ROSA_SUPPORT_DEBUG_HPP diff --git a/include/rosa/support/log.h b/include/rosa/support/log.h index 60345a6..351d106 100644 --- a/include/rosa/support/log.h +++ b/include/rosa/support/log.h @@ -1,159 +1,161 @@ /******************************************************************************* * * File: log.h * * Contents: Facility for logging * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_SUPPORT_LOG_H #define ROSA_SUPPORT_LOG_H #include "rosa/config/config.h" + #include "rosa/support/terminal_colors.h" + #include #include // NOTE: One call for the various logging macros below is supposed to be used // for registering one log entry. That goes natural with the non-stream // implementations, which accept one string as argument. A more flexible way // for printing log entries, for example colorizing text, is to use macros // providing a log stream. It is important to note, however, that the stream // obtained from one macro evaluation fits for printing one log entry, without // nested/overlapping log entry emissions and the entry being closed with a // newline. Should this simple recommendation not being followed, the result // becomes hard to read due to missing line breaks and overlapping entries. // FIXME: Thread-safety is another issue, which need to be addressed for proper // logging. /* **************************************************************************** * Log Levels * * ****************************************************************************/ namespace rosa { // Type-safe definition of log levels, use this in code. // NOTE: Keep values in sync with the corresponding preprocessor definitions // below. enum class LogLevel { Error, Warning, Info, Debug, Trace, // Number of log levels: NumLogLevels }; // Converts a LogLevel to its string representation. // PRE: logLevel != LogLevel::NumLogLevels std::string logLevelToString(const LogLevel logLevel); // Prints colorized tag for the given LogLevel. // PRE: logLevel != LogLevel::NumLogLevels std::ostream &operator<<(std::ostream &os, const LogLevel logLevel); } // End namespace rosa // Valid log levels, only for preprocessor definitions below. // NOTE: Keep in sync with the values of enum class rosa::LogLevel. #define ROSA_LOG_LEVEL_ERROR 0 #define ROSA_LOG_LEVEL_WARNING 1 #define ROSA_LOG_LEVEL_INFO 2 #define ROSA_LOG_LEVEL_DEBUG 3 #define ROSA_LOG_LEVEL_TRACE 4 /* **************************************************************************** * Logger Implementation * * ****************************************************************************/ // Stream to print logs to. // FIXME: Make it configurable, e.g. printing into a file. #define ROSA_LOG_OSTREAM std::clog // Simple logging implementation printing a string message. #define ROSA_LOG_IMPL(level, output) \ do { \ ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" << __FILENAME__ \ << ":" << __LINE__ << ": " << (output) << std::endl; \ } while (false) // Simple logging implementation providing a stream to print to. #define ROSA_LOG_STREAM_IMPL(level) \ ([](void) -> std::ostream & { \ return ROSA_LOG_OSTREAM << (level) << " " << __func__ << "@" \ << __FILENAME__ << ":" << __LINE__ << ": "; \ }()) namespace rosa { // Dummy ostream printing to nowhere. extern std::ostream LogSink; } // End namespace rosa // A stream ignoring all its input. #define ROSA_LOG_STREAM_IGNORE rosa::LogSink /* **************************************************************************** * Logging Interface * * ****************************************************************************/ // Define logging macros if logging is enabled. #ifdef ROSA_LOG_LEVEL #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Error) #define LOG_ERROR(output) ROSA_LOG_IMPL(rosa::LogLevel::Error, output) #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Warning) #define LOG_WARNING(output) ROSA_LOG_IMPL(rosa::LogLevel::Warning, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Info) #define LOG_INFO(output) ROSA_LOG_IMPL(rosa::LogLevel::Info, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Debug) #define LOG_DEBUG(output) ROSA_LOG_IMPL(rosa::LogLevel::Debug, output) #endif #if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IMPL(rosa::LogLevel::Trace) #define LOG_TRACE(output) ROSA_LOG_IMPL(rosa::LogLevel::Trace, output) #endif #endif // defined ROSA_LOG_LEVEL // Define all disabled logging features as void. #ifndef LOG_ERROR #define LOG_ERROR_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_ERROR(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_WARNING #define LOG_WARNING_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_WARNING(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_INFO #define LOG_INFO_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_INFO(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_DEBUG #define LOG_DEBUG_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_DEBUG(output) ROSA_IGNORE_UNUSED(output) #endif #ifndef LOG_TRACE #define LOG_TRACE_STREAM ROSA_LOG_STREAM_IGNORE #define LOG_TRACE(output) ROSA_IGNORE_UNUSED(output) #endif #endif // ROSA_SUPPORT_LOG_H diff --git a/include/rosa/support/type_list.hpp b/include/rosa/support/type_list.hpp index 1afba61..d4b18d2 100644 --- a/include/rosa/support/type_list.hpp +++ b/include/rosa/support/type_list.hpp @@ -1,199 +1,200 @@ /******************************************************************************* * * File: type_list.hpp * * Contents: Facilities for types representing lists of types. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * * This implementation is partially based on the type_list implementation of * CAF. * TODO: Check license. * ******************************************************************************/ #ifndef ROSA_SUPPORT_TYPE_LIST_HPP #define ROSA_SUPPORT_TYPE_LIST_HPP #include "rosa/support/debug.hpp" #include "rosa/support/types.hpp" + #include namespace rosa { // A list of types. template struct TypeList { constexpr TypeList() { // nop } }; // Denotes the empty list. using EmptyTypeList = TypeList<>; // Gets element at index Pos of List. template struct TypeListAtImpl; template struct TypeListAtImpl { using Type = typename TypeListAtImpl::Type; }; template struct TypeListAtImpl<0, T, Ts...> { using Type = T; }; template struct TypeListAtImpl { using Type = none_t; }; template struct TypeListAt; template struct TypeListAt, Pos> { using Type = typename TypeListAtImpl::Type; }; // Finds the first element of type What beginning at index Pos. template struct TypeListIndexOfImpl; template struct TypeListIndexOfImpl { static constexpr int Value = -1; }; template struct TypeListIndexOfImpl { static constexpr int Value = Pos; }; template struct TypeListIndexOfImpl { static constexpr int Value = TypeListIndexOfImpl::Value; }; template struct TypeListIndexOf; template struct TypeListIndexOf, T> { static constexpr int Value = TypeListIndexOfImpl<0, T, Ts...>::Value; }; // Gets the first element of List. template struct TypeListHead; template <> struct TypeListHead { using Type = none_t; }; template struct TypeListHead> { using Type = T; }; // Gets the tail of List. template struct TypeListTail; template <> struct TypeListTail> { using Type = EmptyTypeList; }; template struct TypeListTail> { using Type = TypeList; }; // Extends a TypeList with a type, in the front or in the back depending on the // order of the template arguments. template struct TypeListPush; template struct TypeListPush, T> { using Type = TypeList; }; template struct TypeListPush> { using Type = TypeList; }; // Drops the first N element of List. template struct TypeListDrop; template struct TypeListDrop { using Type = EmptyTypeList; }; template struct TypeListDrop> { using Type = typename std::conditional< N == 0, TypeList, typename TypeListDrop>::Type>::type; }; // Gets the number of template parameters of List. template struct TypeListSize; template struct TypeListSize> { static constexpr size_t Value = sizeof...(Ts); }; template constexpr size_t TypeListSize>::Value; // Tests whether a list is empty. template struct TypeListEmpty { static constexpr bool Value = std::is_same::value; }; // Tells if List contains type T. template struct TypeListContains; template struct TypeListContains, T> { static constexpr bool Value = std::conditional, T>::Value == -1, std::false_type, std::true_type>::type::value; }; // Tells if ListA is a subset of ListB template struct TypeListSubsetOf; template struct TypeListSubsetOf { static constexpr bool Value = true; }; template struct TypeListSubsetOf { static constexpr bool Value = false; }; template struct TypeListSubsetOf, List> : TypeListSubsetOf, List, TypeListContains::Value> {}; // Finds the first element satisfying Pred template