diff --git a/examples/messaging/messaging.cpp b/examples/messaging/messaging.cpp index 0ee50f1..693bf76 100644 --- a/examples/messaging/messaging.cpp +++ b/examples/messaging/messaging.cpp @@ -1,134 +1,139 @@ /******************************************************************************* * * File: messaging.cpp * * Contents: An example showcasing features related to Messages. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #include "rosa/config/version.h" #include "rosa/core/Invoker.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" #include using namespace rosa; using namespace rosa::terminal; // A simple implementation of the Message interface with two hardcoded types. class MyMessage : public Message { const uint8_t U8; // First value const uint16_t U16; // Second value public: MyMessage(const uint8_t U8, const uint16_t U16) : Message(U8, U16), U8(U8), U16(U16) {} const void *getPointerTo(const size_t Pos) const noexcept override { switch (Pos) { default: ROSA_CRITICAL("wrong position"); case 0: return &U8; case 1: return &U16; } } }; class MyAtomMessage : public Message { const AtomValue A; // First value public: template MyAtomMessage(AtomConstant C) : Message(C), A(V) {} const void *getPointerTo(const size_t Pos) const noexcept override { switch (Pos) { default: ROSA_CRITICAL("wrong position"); case 0: return &A; } } }; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging" << Color::Default << std::endl; auto &Log = LOG_TRACE_STREAM << std::endl; MyMessage Msg(1, 2); Log << "Checking on 'MyMessage(1, 2)' that is of 'Message with " "TypeList<>':" << std::endl << " Size: " << Msg.size() << std::endl << " Pos 0 is uint8_t: " << Msg.isTypeAt(0) << std::endl << " Pos 1 is uint16_t: " << Msg.isTypeAt(1) << std::endl << " Pos 2 is uint32_t: " << Msg.isTypeAt(1) << std::endl << " Value at pos 0: " << PRINTABLE(uint8_t)(Msg.getValueAt(0)) << std::endl << " Value at pos 1: " << Msg.getValueAt(1) << std::endl << std::endl; using MyMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << std::endl << " matching: " << MyMatcher::doesStronglyMatch(Msg) << std::endl; auto Vs = MyMatcher::extractedValues(Msg); Log << " value: '(" << PRINTABLE(uint8_t)(std::get<0>(Vs)) << ", " << std::get<1>(Vs) << ")'" << std::endl << std::endl; using MyWrongMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << std::endl << " matching: " << MyWrongMatcher::doesStronglyMatch(Msg) << std::endl << std::endl; using MyAtom = AtomConstant; const MyAtom &A = MyAtom::Value; using MyNAtom = AtomConstant; MyAtomMessage AMsg(A); Log << "Checking on 'MyAtomMessage(AtomConstant::Value)' " "that is of 'Message with TypeList>':" << std::endl << " Size: " << AMsg.size() << std::endl << " Pos 0 is 'AtomValue': " << AMsg.isTypeAt(0) << std::endl << " Pos 0 is 'AtomConstant': " << AMsg.isTypeAt(0) << std::endl << std::endl; using MyAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << std::endl << " matching: " << MyAtomMatcher::doesStronglyMatch(AMsg) << std::endl << " value: '(" << to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) << ")'" << std::endl << std::endl; using MyWrongAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << std::endl << " matching: " << MyWrongAtomMatcher::doesStronglyMatch(AMsg) << std::endl << std::endl; // FIXME: Is this a compiler bug, - // cannot use turnIntoInvoker([&](MyAtom)... ??? - auto Inv = turnIntoInvoker; - auto I = Inv([&Log](MyAtom) noexcept->void { + // cannot use Invoker::wrap([&](MyAtom)... ??? + auto WrapFix = Invoker::wrap; + auto IP = WrapFix([&Log](MyAtom) noexcept->void { Log << "** Lambda-function called via Invoker." << std::endl; }); + auto &I = *IP; // Get a reference from the pointer. Log << "Invoking a function of signature 'void(MyAtom) noexcept':" << std::endl - << " with 'Message with TypeList'..." << std::endl; + << " with 'Message with TypeList'" << std::endl + << " does Message match Invoker: " << I.match(Msg) << std::endl + << " invoking..." << std::endl; I(Msg); - Log << " with 'Message with TypeList'..." << std::endl; + Log << " with 'Message with TypeList'..." << std::endl + << " does Message match Invoker: " << I.match(AMsg) << std::endl + << " invoking..." << std::endl; I(AMsg); return 0; } diff --git a/include/rosa/core/Invoker.hpp b/include/rosa/core/Invoker.hpp index 6998f0d..b909915 100644 --- a/include/rosa/core/Invoker.hpp +++ b/include/rosa/core/Invoker.hpp @@ -1,136 +1,162 @@ /******************************************************************************* * * File: Invoker.hpp * - * Contents: Implementation of Invoker. + * 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. -// NOTE: As there is no empty Message, no Invoker wraps a function without an -// argument. -template class Invoker; +class Invoker { +protected: + // Protected ctor, only subclasses can instantiate. + Invoker(void) noexcept; -// Convenience function for instantiating Invoker and utilizing template -// argument deduction. -// FIXME: C++17 supports class template deduction in some contexts. May that be -// usable to directly instantiate Invoker? -// FIXME: Alternatively, use the Callable concept of C++17? -template -Invoker> -turnIntoInvoker(std::function &&F) noexcept { - return Invoker>(std::move(F)); -} +public: + // Dtor. + virtual ~Invoker(void); + + // Enumeration of possible results of an invocation. + enum class Result { NoMatch, Invoked }; + + // 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; -// Nested namespace with helper templates, consider it private. + // 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 std::unique_ptr + wrap(std::function &&F) noexcept; +}; + +// 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; }; -}; // End namespace - -// Specialization of Invoker template for std::function. +// Specialization of InvokerImpl template for std::function. // NOTE: No std::function. template -class Invoker> final { +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. - Invoker(function_t &&F) noexcept; + InvokerImpl(function_t &&F) noexcept; // Dtor. - ~Invoker(void); + ~InvokerImpl(void) = default; - // Enumeration of possible results of an invokation. - enum class Result { NoMatch, Invoked }; + // Tells if Msg can be used to invoke F. + bool match(const Message &Msg) const noexcept override; - // Type alias for Result. - using result_t = Result; - - // Invoking F with Msg if the values stored in Msg are of proper types as - // arguments for F. - result_t operator()(const Message &Msg) const noexcept; + // Invokes F with Msg if Msg can be used to invoke F. + result_t operator()(const Message &Msg) const noexcept override; }; template -Invoker>::Invoker( +InvokerImpl>::InvokerImpl( function_t &&F) noexcept : F(F) { ASSERT(bool(F)); // Sanity check. - LOG_TRACE("Creating Invoker for arguments with Token(" + - to_string(TypeToken::Value) + ")"); -} - -template -Invoker>::~Invoker(void) { - LOG_TRACE("Destroying Invoker"); } template template -void Invoker>::invokeFunction( +void InvokerImpl>::invokeFunction( Seq, const args_t &Args) const noexcept { ASSERT(sizeof...(S) == std::tuple_size::value); // Sanity check. F(std::get(Args)...); } template -typename Invoker>::result_t -Invoker>:: +bool InvokerImpl>::match( + const Message &Msg) const noexcept { + return Matcher::doesStronglyMatch(Msg); +}; + +template +typename Invoker::result_t InvokerImpl>:: operator()(const Message &Msg) const noexcept { - using Matcher = MsgMatcher; - if (Matcher::doesStronglyMatch(Msg)) { + 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; } } +} // End namespace + +template +std::unique_ptr +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/lib/core/CMakeLists.txt b/lib/core/CMakeLists.txt index 5d7c6a9..5e40be0 100644 --- a/lib/core/CMakeLists.txt +++ b/lib/core/CMakeLists.txt @@ -1,9 +1,10 @@ add_library(ROSACore Unit.cpp System.cpp SystemImpl.cpp Message.cpp + Invoker.cpp ) ROSA_add_library_dependencies(ROSACore ROSASupport) diff --git a/lib/core/Invoker.cpp b/lib/core/Invoker.cpp new file mode 100644 index 0000000..13aed72 --- /dev/null +++ b/lib/core/Invoker.cpp @@ -0,0 +1,22 @@ +/******************************************************************************* + * + * File: Invoker.cpp + * + * Contents: Implementation of non-template part of Invoker. + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#include "rosa/core/Invoker.hpp" + +namespace rosa { + +Invoker::Invoker(void) noexcept { LOG_TRACE("Creating Invoker"); } + +Invoker::~Invoker(void) { LOG_TRACE("Destroying Invoker"); } + +} // End namespace rosa +