diff --git a/apps/sa-ews1/sa-ews1.cpp b/apps/sa-ews1/sa-ews1.cpp index 67a1ac6..836e3d1 100644 --- a/apps/sa-ews1/sa-ews1.cpp +++ b/apps/sa-ews1/sa-ews1.cpp @@ -1,316 +1,318 @@ //===-- apps/sa-ews1/sa-ews1.cpp --------------------------------*- C++ -*-===// // // The RoSA Framework -- Application SA-EWS1 // //===----------------------------------------------------------------------===// /// /// \file apps/sa-ews1/sa-ews1.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief The application SA-EWS1 implements the case study from the paper: /// M. Götzinger, N. Taherinejad, A. M. Rahmani, P. Liljeberg, A. Jantsch, and /// H. Tenhunen: Enhancing the Early Warning Score System Using Data Confidence /// DOI: 10.1007/978-3-319-58877-3_12 //===----------------------------------------------------------------------===// #include "rosa/agent/Abstraction.hpp" #include "rosa/agent/Confidence.hpp" #include "rosa/config/version.h" #include "rosa/deluxe/DeluxeContext.hpp" #include "rosa/support/csv/CSVReader.hpp" #include "rosa/support/csv/CSVWriter.hpp" #include using namespace rosa; using namespace rosa::agent; using namespace rosa::deluxe; using namespace rosa::terminal; const std::string AppName = "SA-EWS1"; /// Paths for the CSV files for simulation. /// ///@{ const std::string HRCSVPath = "HR.csv"; const std::string BRCSVPath = "BR.csv"; const std::string SpO2CSVPath = "SpO2.csv"; const std::string BPSysCSVPath = "BPSys.csv"; const std::string BodyTempCSVPath = "BodyTemp.csv"; const std::string ScoreCSVPath = "Score.csv"; ///@} /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 16; /// Warning levels for abstraction. enum WarningScore { No = 0, Low = 1, High = 2, Emergency = 3 }; /// Helper function creating a deluxe agent for pre-processing sensory values. /// /// Received values are first validated for confidence. Values which the /// validator does not mark confident are ignored. Confident values are /// abstracted into a \c WarningScore value, which is the result of the /// processing function. /// /// \note The result, \c WarningScore, is returned as \c uint32_t because /// enumeration types are not integrated into built-in types. Hence, a master /// to these agents receives its input as \c uint32_t values, and may cast them /// to \c WarningScore explicitly. /// /// \tparam T type of values to receive from the sensor /// /// \param C the deluxe context to create the agent in /// \param Name name of the new agent /// \param CC confidence validator to use /// \param A abstraction to use /// /// \return handle for the new agent template AgentHandle createLowLevelAgent(std::unique_ptr &C, const std::string &Name, const Confidence &CC, const Abstraction &A) { - using handler = DeluxeAgent::D; + using handler = std::function(std::pair)>; using result = Optional; return C->createAgent( Name, handler([&, Name](std::pair I) -> result { LOG_INFO_STREAM << "\n******\n" << Name << " " << (I.second ? "" : "") << " value: " << I.first << "\n******\n"; return (I.second && CC(I.first)) ? result(A(I.first)) : result(); })); } int main(void) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << AppName << "app" << Color::Default << '\n' << Color::Yellow << "CSV files are read from and written to the current working directory." << Color::Default << '\n'; std::unique_ptr C = DeluxeContext::create(AppName); // // Create deluxe sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but // with the default value of the second argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. AgentHandle HRSensor = C->createSensor("HR Sensor"); AgentHandle BRSensor = C->createSensor("BR Sensor"); AgentHandle SpO2Sensor = C->createSensor("SpO2 Sensor"); AgentHandle BPSysSensor = C->createSensor("BPSys Sensor"); AgentHandle BodyTempSensor = C->createSensor("BodyTemp Sensor"); // // Create functionalities. // LOG_INFO("Creating Functionalities for Agents."); // // Define confidence validators. // // Lower bounds are inclusive and upper bounds are exclusive. Confidence HRConfidence(0, 501); Confidence BRConfidence(0, 301); Confidence SpO2Confidence(0, 101); Confidence BPSysConfidence(0,501); Confidence BodyTempConfidence(-60, nextRepresentableFloatingPoint(50.0f)); // // Define abstractions. // RangeAbstraction HRAbstraction( {{{0, 40}, Emergency}, {{40, 51}, High}, {{51, 60}, Low}, {{60, 100}, No}, {{100, 110}, Low}, {{110, 129}, High}, {{129, 200}, Emergency}}, Emergency); RangeAbstraction BRAbstraction({{{0, 9}, High}, {{9, 14}, No}, {{14, 20}, Low}, {{20, 29}, High}, {{29, 50}, Emergency}}, Emergency); RangeAbstraction SpO2Abstraction({{{1, 85}, Emergency}, {{85, 90}, High}, {{90, 95}, Low}, {{95, 100}, No}}, Emergency); RangeAbstraction BPSysAbstraction( {{{0, 70}, Emergency}, {{70, 81}, High}, {{81, 101}, Low}, {{101, 149}, No}, {{149, 169}, Low}, {{169, 179}, High}, {{179, 200}, Emergency}}, Emergency); RangeAbstraction BodyTempAbstraction( {{{0.f, 28.f}, Emergency}, {{28.f, 32.f}, High}, {{32.f, 35.f}, Low}, {{35.f, 38.f}, No}, {{38.f, 39.5f}, High}, {{39.5f, 100.f}, Emergency}}, Emergency); // // Create low-level deluxe agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); AgentHandle HRAgent = createLowLevelAgent(C, "HR Agent", HRConfidence, HRAbstraction); AgentHandle BRAgent = createLowLevelAgent(C, "BR Agent", BRConfidence, BRAbstraction); AgentHandle SpO2Agent = createLowLevelAgent(C, "SpO2 Agent", SpO2Confidence, SpO2Abstraction); AgentHandle BPSysAgent = createLowLevelAgent(C, "BPSys Agent", BPSysConfidence, BPSysAbstraction); AgentHandle BodyTempAgent = createLowLevelAgent( C, "BodyTemp Agent", BodyTempConfidence, BodyTempAbstraction); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); C->connectSensor(HRAgent, 0, HRSensor, "HR Sensor Channel"); C->connectSensor(BRAgent, 0, BRSensor, "BR Sensor Channel"); C->connectSensor(SpO2Agent, 0, SpO2Sensor, "SpO2 Sensor Channel"); C->connectSensor(BPSysAgent, 0, BPSysSensor, "BPSys Sensor Channel"); C->connectSensor(BodyTempAgent, 0, BodyTempSensor, "BodyTemp Sensor Channel"); // // Create a high-level deluxe agent. // LOG_INFO("Create high-level agent."); // The new agent logs its input values and results in the the sum of them. AgentHandle BodyAgent = C->createAgent( "Body Agent", - DeluxeAgent::D( + std::function( + std::pair, std::pair, + std::pair, std::pair, + std::pair)>( [](std::pair HR, std::pair BR, std::pair SpO2, std::pair BPSys, std::pair BodyTemp) -> Optional { LOG_INFO_STREAM << "\n*******\nBody Agent trigged with values:\n" << (HR.second ? "" : "") << " HR warning score: " << HR.first << "\n" << (BR.second ? "" : "") << " BR warning score: " << BR.first << "\n" << (SpO2.second ? "" : "") << " SpO2 warning score: " << SpO2.first << "\n" << (BPSys.second ? "" : "") << " BPSys warning score: " << BPSys.first << "\n" << (BodyTemp.second ? "" : "") << " BodyTemp warning score: " << BodyTemp.first << "\n******\n"; return {HR.first + BR.first + SpO2.first + BPSys.first + BodyTemp.first}; })); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); C->connectAgents(BodyAgent, 0, HRAgent, "HR Agent Channel"); C->connectAgents(BodyAgent, 1, BRAgent, "BR Agent Channel"); C->connectAgents(BodyAgent, 2, SpO2Agent, "SpO2 Agent Channel"); C->connectAgents(BodyAgent, 3, BPSysAgent, "BPSys Agent Channel"); C->connectAgents(BodyAgent, 4, BodyTempAgent, "BodyTemp Agent Channel"); // // For simulation output, create a logger agent writing the output of the // high-level agent into a CSV file. // LOG_INFO("Create a logger agent."); // Create CSV writer. std::ofstream ScoreCSV(ScoreCSVPath); csv::CSVWriter ScoreWriter(ScoreCSV); // The agent writes each new input value into a CSV file and produces nothing. AgentHandle LoggerAgent = C->createAgent( "Logger Agent", - DeluxeAgent::D( + std::function(std::pair)>( [&ScoreWriter](std::pair Score) -> Optional { if (Score.second) { // The state of \p ScoreWriter is not checked, expecting good. ScoreWriter << Score.first; } return {}; })); // // Connect the high-level agent to the logger agent. // LOG_INFO("Connect the high-level agent to the logger agent."); C->connectAgents(LoggerAgent, 0, BodyAgent, "Body Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // // Open CSV files and register them for their corresponding sensors. // // Type aliases for iterators. using CSVInt = csv::CSVFlatIterator; using CSVFloat = csv::CSVFlatIterator; std::ifstream HRCSV(HRCSVPath); C->registerSensorValues(HRSensor, CSVInt(HRCSV), CSVInt()); std::ifstream BRCSV(BRCSVPath); C->registerSensorValues(BRSensor, CSVInt(BRCSV), CSVInt()); std::ifstream SpO2CSV(SpO2CSVPath); C->registerSensorValues(SpO2Sensor, CSVInt(SpO2CSV), CSVInt()); std::ifstream BPSysCSV(BPSysCSVPath); C->registerSensorValues(BPSysSensor, CSVInt(BPSysCSV), CSVInt()); std::ifstream BodyTempCSV(BodyTempCSVPath); C->registerSensorValues(BodyTempSensor, CSVFloat(BodyTempCSV), CSVFloat()); // // Simulate. // C->simulate(NumberOfSimulationCycles); return 0; } diff --git a/examples/deluxe-interface/deluxe-interface.cpp b/examples/deluxe-interface/deluxe-interface.cpp old mode 100755 new mode 100644 index 43222e4..78638ff --- a/examples/deluxe-interface/deluxe-interface.cpp +++ b/examples/deluxe-interface/deluxe-interface.cpp @@ -1,191 +1,338 @@ //===-- examples/deluxe-interface/deluxe-interface.cpp ----------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file examples/deluxe-interface/deluxe-interface.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief A simple example on the \c rosa::deluxe::DeluxeContext and related /// classes. //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/deluxe/DeluxeContext.hpp" #include #include #include using namespace rosa; using namespace rosa::deluxe; using namespace rosa::terminal; /// How many cycles of simulation to perform. const size_t NumberOfSimulationCycles = 16; -/// Helper function creating a deluxe agent for logging and forwarding values. -/// -/// Received values are dumped to \c LOG_INFO_STREAM and then returned as -/// result. -/// -/// \tparam T type of values to handle -/// -/// \param C the deluxe context to create the agent in -/// \param Name name of the new agent -/// -/// \return handle for the new agent -template -AgentHandle createLowLevelAgent(std::unique_ptr &C, - const std::string &Name) { - using handler = DeluxeAgent::D; - using result = Optional; - return C->createAgent( - Name, handler([&, Name](std::pair I) -> result { - LOG_INFO_STREAM << "\n******\n" - << Name << " " << (I.second ? "" : "") - << " value: " << I.first << "\n******\n"; - return {I.first}; - })); -} - int main(void) { LOG_INFO_STREAM << '\n' << library_string() << " -- " << Color::Red << "deluxe-interface example" << Color::Default << '\n'; std::unique_ptr C = DeluxeContext::create("Deluxe"); // // Create deluxe sensors. // LOG_INFO("Creating sensors."); // All sensors are created without defining a normal generator function, but - // with the default value of the second argument. That, however, requires the + // with the default value of the last argument. That, however, requires the // data type to be explicitly defined. This is good for simulation only. + + // The first and second sensors do not receive master-input. + AgentHandle BoolSensor = C->createSensor("BoolSensor"); AgentHandle IntSensor = C->createSensor("IntSensor"); - AgentHandle FloatSensor = C->createSensor("FloatSensor"); + // This sensor receives master-input and dumps it to \c LOG_INFO_STREAM. + const std::string FloatSensorName = "FloatSensor"; + AgentHandle FloatSensor = C->createSensor( + FloatSensorName, [&FloatSensorName](std::pair I) { + LOG_INFO_STREAM << "\n******\n" + << FloatSensorName + << " master-input " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + }); + + // This sensor do not receive master-input but produces tuples. + using TupleType = DeluxeTuple; + AgentHandle TupleSensor = C->createSensor("TupleSensor"); + + // // Check and set execution policy for sensors. + // LOG_INFO("Execution policies for sensors."); LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor))); C->setExecutionPolicy(IntSensor, DeluxeExecutionPolicy::decimation(2)); C->setExecutionPolicy(FloatSensor, DeluxeExecutionPolicy::decimation(2)); LOG_INFO(std::to_string(*C->getExecutionPolicy(IntSensor))); // // Create low-level deluxe agents with \c createLowLevelAgent. // LOG_INFO("Creating low-level agents."); - AgentHandle IntAgent = createLowLevelAgent(C, "IntAgent"); - AgentHandle FloatAgent = createLowLevelAgent(C, "FloatAgent"); + // All agents below dump their received values to \c LOG_INFO_STREAM on each + // triggering. + + // This agent does not receive master-input and does not produce + // master-output. It results in the value it received. + const std::string BoolAgentName = "BoolAgent"; + using BoolResult = Optional; + using BoolHandler = std::function)>; + AgentHandle BoolAgent = C->createAgent( + BoolAgentName, + BoolHandler([&BoolAgentName](std::pair I) -> BoolResult { + LOG_INFO_STREAM << "\n******\n" + << BoolAgentName << " " + << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return {I.first}; + })); + + // This agent receives master-input but does not produce master-output. The + // agent maintains a state in \c IntAgentOffset. The master-input handler + // updates \c IntAgentOffset according to each received (new) value from its + // master. The slave-input handler results in the sum of the received value + // and the actual value of \c IntAgentOffset. + const std::string IntAgentName = "IntAgent"; + using IntMasterHandler = std::function)>; + using IntResult = Optional; + using IntHandler = std::function)>; + uint32_t IntAgentOffset = 0; + AgentHandle IntAgent = C->createAgent( + IntAgentName, + // Master-input handler. + IntMasterHandler([&IntAgentName, + &IntAgentOffset](std::pair I) { + LOG_INFO_STREAM << "\n******\n" + << IntAgentName + << " master-input " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + if (I.second) { + IntAgentOffset = I.first; + } + }), + // Slave-input handler. + IntHandler([&IntAgentName, + &IntAgentOffset](std::pair I) -> IntResult { + LOG_INFO_STREAM << "\n******\n" + << IntAgentName << " " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return {I.first + IntAgentOffset}; + })); + + // This agent receives master-input and produces master-output. The + // master-input handler propagaates each received (new) value to its slave as + // master-output. The slave-input handler results in the value it received and + // produces no actual master-output. + const std::string FloatAgentName = "FloatAgent"; + using FloatMasterResult = std::tuple>; + using FloatMasterHandler = + std::function)>; + using FloatResult = std::tuple, Optional>; + using FloatHandler = std::function)>; + AgentHandle FloatAgent = C->createAgent( + FloatAgentName, + // Master-input handler. + FloatMasterHandler([&FloatAgentName]( + std::pair I) -> FloatMasterResult { + LOG_INFO_STREAM << "\n******\n" + << FloatAgentName + << " master-input " << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + const auto Output = + I.second ? Optional(I.first) : Optional(); + return {Output}; + }), + // Slave-input handler. + FloatHandler([&FloatAgentName](std::pair I) -> FloatResult { + LOG_INFO_STREAM << "\n******\n" + << FloatAgentName << " " + << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return {{I.first}, {}}; + })); + + // This agent does not receive master-input and does not produce + // master-output. It results in the sum of the values it receives in a tuple. + const std::string TupleAgentName = "TupleAgent"; + using TupleSumResult = Optional>; + using TupleHandler = + std::function)>; + AgentHandle TupleAgent = C->createAgent( + TupleAgentName, + TupleHandler( + [&TupleAgentName](std::pair I) -> TupleSumResult { + LOG_INFO_STREAM << "\n******\n" + << TupleAgentName << " " + << (I.second ? "" : "") + << " value: " << I.first << "\n******\n"; + return {std::get<0>(I.first) + std::get<1>(I.first)}; + })); + // // Set execution policies for low-level agents. + // LOG_INFO("Setting Execution policies for low-level agents."); C->setExecutionPolicy(IntAgent, DeluxeExecutionPolicy::awaitAll({0})); C->setExecutionPolicy(FloatAgent, DeluxeExecutionPolicy::awaitAll({0})); // // Connect sensors to low-level agents. // LOG_INFO("Connect sensors to their corresponding low-level agents."); + C->connectSensor(BoolAgent, 0, BoolSensor, "Bool Sensor Channel"); C->connectSensor(IntAgent, 0, IntSensor, "Int Sensor Channel"); C->connectSensor(FloatAgent, 0, FloatSensor, "Float Sensor Channel"); + C->connectSensor(TupleAgent, 0, TupleSensor, "Tuple Sensor Channel"); // // Create a high-level deluxe agent. // LOG_INFO("Create high-level agent."); - // The new agent logs its input values and results in the the sum of them. + using SingleDoubleOutputType = Optional>; + using SingleUInt32OutputType = Optional>; + using NoOutputType = Optional; + + // This agent does not receive master-input but produces master-output for its + // slaves at positions `1` and `2` but not for that at position `0`. The agent + // maintains a state in \c SumAgentState. The handler increments \c + // SumAgentState upon each received (new) `true` value from its slave at + // position `0`. Whenever \c SumAgentState has been updated, it is sent to the + // slaves at positions `1` and `2`. The handler results in the sum of the + // values received from slaves at positions `1`, `2`, and `3`. + using SumResult = + std::tuple; + using SumHandler = std::function, bool>, std::pair, bool>, + std::pair, bool>, + std::pair, bool>)>; + uint32_t SumAgentState = 0; AgentHandle SumAgent = C->createAgent( - "Sum Agent", DeluxeAgent::D( - [](std::pair I1, - std::pair I2) -> Optional { - LOG_INFO_STREAM - << "\n*******\nSum Agent triggered with values:\n" - << (I1.second ? "" : "") - << " int value: " << I1.first << "\n" - << (I2.second ? "" : "") - << " float value: " << I2.first << "\n******\n"; - return {I1.first + I2.first}; - })); + "Sum Agent", + SumHandler([&SumAgentState]( + std::pair, bool> I0, + std::pair, bool> I1, + std::pair, bool> I2, + std::pair, bool> I3) -> SumResult { + const auto V0 = std::get<0>(I0.first); + const auto V1 = std::get<0>(I1.first); + const auto V2 = std::get<0>(I2.first); + const auto V3 = std::get<0>(I3.first); + LOG_INFO_STREAM << "\n*******\nSum Agent triggered with values:\n" + << (I0.second ? "" : "") + << " bool value: " << V0 << "\n" + << (I1.second ? "" : "") + << " int value: " << V1 << "\n" + << (I2.second ? "" : "") + << " float value: " << V2 << "\n" + << (I3.second ? "" : "") + << " double value: " << V3 << "\n******\n"; + if (I0.second && V0) { + ++SumAgentState; + } + const SingleUInt32OutputType MasterOutput = + I0.second && V0 + ? SingleUInt32OutputType(DeluxeTuple(SumAgentState)) + : SingleUInt32OutputType(); + const DeluxeTuple Output = {V1 + V2 + V3}; + return {{Output}, {}, {MasterOutput}, {MasterOutput}, {}}; + })); // // Connect low-level agents to the high-level agent. // LOG_INFO("Connect low-level agents to the high-level agent."); - C->connectAgents(SumAgent, 0, IntAgent, "Int Agent Channel"); - C->connectAgents(SumAgent, 1, FloatAgent, "Float Agent Channel"); + C->connectAgents(SumAgent, 0, BoolAgent, "Bool Agent Channel"); + C->connectAgents(SumAgent, 1, IntAgent, "Int Agent Channel"); + C->connectAgents(SumAgent, 2, FloatAgent, "Float Agent Channel"); + C->connectAgents(SumAgent, 3, TupleAgent, "Tuple Agent Channel"); // // For simulation output, create a logger agent writing the output of the // high-level agent into a log stream. // LOG_INFO("Create a logger agent."); - // The agent logs each new input value and produces nothing. + // The agent dumps each received (new) value to \c LOG_INFO_STREAM and + // produces nothing; does not receive mater-input and does not produce + // master-output. AgentHandle LoggerAgent = C->createAgent("Logger Agent", - DeluxeAgent::D( + std::function(std::pair)>( [](std::pair Sum) -> Optional { if (Sum.second) { LOG_INFO_STREAM << "Result: " << Sum.first << "\n"; } return {}; })); // // Connect the high-level agent to the logger agent. // LOG_INFO("Connect the high-level agent to the logger agent."); C->connectAgents(LoggerAgent, 0, SumAgent, "Sum Agent Channel"); // // Do simulation. // LOG_INFO("Setting up and performing simulation."); // // Initialize deluxe context for simulation. // C->initializeSimulation(); // // Create some vectors and register them for their corresponding sensors. // + std::vector BoolValues(NumberOfSimulationCycles); + std::generate(BoolValues.begin(), BoolValues.end(), + [i = 0](void) mutable -> bool { return (++i % 4) == 0; }); + C->registerSensorValues(BoolSensor, BoolValues.begin(), BoolValues.end()); + std::vector IntValues(NumberOfSimulationCycles); std::generate(IntValues.begin(), IntValues.end(), [i = 0](void) mutable { return ++i; }); C->registerSensorValues(IntSensor, IntValues.begin(), IntValues.end()); std::vector FloatValues(NumberOfSimulationCycles); std::generate(FloatValues.begin(), FloatValues.end(), [f = 0.5f](void) mutable { f += 0.3f; return std::floor(f) + 0.5f; }); C->registerSensorValues(FloatSensor, FloatValues.begin(), FloatValues.end()); + std::vector TupleValues(NumberOfSimulationCycles); + std::generate(TupleValues.begin(), TupleValues.end(), + [f1 = 0.f, f2 = 3.14f](void) mutable -> TupleType { + f1 += f2; + f2 -= f1; + return {f1, f2}; + }); + C->registerSensorValues(TupleSensor, TupleValues.begin(), TupleValues.end()); + // // Simulate. // C->simulate(NumberOfSimulationCycles); return 0; } diff --git a/examples/messaging/messaging.cpp b/examples/messaging/messaging.cpp index 7d1269b..640a2d1 100644 --- a/examples/messaging/messaging.cpp +++ b/examples/messaging/messaging.cpp @@ -1,126 +1,127 @@ //===-- examples/messaging/messaging.cpp ------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file examples/messaging/messaging.cpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief An example showcasing features related to the \c rosa::Message class. /// //===----------------------------------------------------------------------===// #include "rosa/config/version.h" #include "rosa/core/MessageHandler.hpp" #include "rosa/support/log.h" #include "rosa/support/terminal_colors.h" using namespace rosa; using namespace rosa::terminal; int main(void) { LOG_INFO_STREAM << library_string() << " -- " << Color::Red << "messaging" << Color::Default << '\n'; auto &Log = LOG_INFO_STREAM << '\n'; // Message interface. auto PMsg = Message::create(1, 2); auto &Msg = *PMsg; Log << "Checking on a 'Message with TypeList<>':" << "\n Size: " << Msg.Size << "\n Pos 0 is uint8_t: " << Msg.isTypeAt(0) << "\n Pos 1 is uint16_t: " << Msg.isTypeAt(1) << "\n Pos 2 is uint32_t: " << Msg.isTypeAt(1) << "\n Value at pos 0: " << PRINTABLE(Msg.valueAt(0)) << "\n Value at pos 1: " << Msg.valueAt(1) << "\n\n"; // MessageMatcher. using MyMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << "\n matching: " << MyMatcher::doesStronglyMatch(Msg); auto Vs = MyMatcher::extractedValues(Msg); Log << "\n value: '(" << PRINTABLE(std::get<0>(Vs)) << ", " << std::get<1>(Vs) << ")'\n\n"; using MyWrongMatcher = MsgMatcher; Log << "Matching against 'TypeList':" << "\n matching: " << MyWrongMatcher::doesStronglyMatch(Msg) << "\n\n"; using MyAtom = AtomConstant; const MyAtom &A = MyAtom::Value; using MyNAtom = AtomConstant; auto PAMsg = Message::create(A); auto &AMsg = *PAMsg; Log << "Checking on a 'Message with TypeList>':" << "\n Size: " << AMsg.Size << "\n Pos 0 is 'AtomValue': " << AMsg.isTypeAt(0) << "\n Pos 0 is 'AtomConstant': " << AMsg.isTypeAt(0) << "\n\n"; using MyAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << "\n matching: " << MyAtomMatcher::doesStronglyMatch(AMsg) << "\n value: '(" - << to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) << ")'" + << std::to_string(std::get<0>(MyAtomMatcher::extractedValues(AMsg))) + << ")'" << "\n\n"; using MyWrongAtomMatcher = MsgMatcher; Log << "Matching against 'TypeList>':" << "\n matching: " << MyWrongAtomMatcher::doesStronglyMatch(AMsg) << "\n\n"; // Invoker. auto IP = Invoker::wrap(Invoker::F([&Log](MyAtom) noexcept->void { Log << "** Handling 'Message with " "TypeList>'.\n"; })); auto &I = *IP; // Get a reference from the pointer. Log << "Invoking a function of signature 'void(AtomConstant) " "noexcept':" << "\n with 'Message with TypeList'" << "\n does Message match Invoker: " << I.match(Msg) << "\n invoking..."; I(Msg); Log << "\n with 'Message with TypeList>'..." << "\n does Message match Invoker: " << I.match(AMsg) << "\n invoking..."; I(AMsg); Log << "\n\n"; // MessageHandler. MessageHandler Handler{ Invoker::F([&Log](uint8_t, uint16_t) { Log << "** Handling 'Message with TypeList'\n"; }), Invoker::F([&Log](MyAtom) { Log << "** Handling 'Message with " "TypeList>'\n"; })}; auto PANMsg = Message::create(MyNAtom::Value); auto &ANMsg = *PANMsg; Log << "Handling Messages with 'MessageHandler " "{ Invoker::F, " "Invoker::F> }':" << "\n 'Message with TypeList'" << "\n can handle: " << Handler.canHandle(Msg) << "\n handling..."; Handler(Msg); Log << "\n 'Message with TypeList>'" << "\n can handle: " << Handler.canHandle(AMsg) << "\n handling..."; Handler(AMsg); Log << "\n 'Message with TypeList>'" << "\n can handle: " << Handler.canHandle(ANMsg) << "\n handling..."; Handler(ANMsg); Log << "\n\n"; Log << "Terminating, destroying automatic variables.\n"; return 0; } 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::Value; Log << "\n type number: " << PRINTABLE_TN(TN) << "\n size: " << TypeForNumber::Size << "\n name: " << TypeForNumber::Name << "\n\n"; Log << "Type number information on 'std::string':"; constexpr TypeNumber TNS = TypeNumberOf::Value; Log << "\n type number: " << PRINTABLE_TN(TNS) << "\n size: " << TypeForNumber::Size << "\n name: " << TypeForNumber::Name << "\n\n"; Log << "Type number information of AtomConstants:"; using Atom1 = AtomConstant; using Atom2 = AtomConstant; Log << "\n std::is_same::value: " << std::is_same::value << "\n TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << "\n TypeNumberOf::Value: " << PRINTABLE_TN(TypeNumberOf::Value) << "\n name: " << TypeForNumber::Value>::Name << "\n\n"; Log << "Type token information on 'TypeList':"; // \c rosa::Token is generated statically. constexpr Token T = TypeToken::Value; STATIC_ASSERT( (T == TypeListToken>::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(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(T_) << "\n is head uint16_t: " << isHeadOfTokenTheSameType(T_) << "\n is head std::string: " << isHeadOfTokenTheSameType(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/Invoker.hpp b/include/rosa/core/Invoker.hpp index 9fa294f..10786aa 100644 --- a/include/rosa/core/Invoker.hpp +++ b/include/rosa/core/Invoker.hpp @@ -1,259 +1,258 @@ //===-- rosa/core/Invoker.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/Invoker.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Facilities for providing actual arguments for functions as /// \c rosa::Messageobjects. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_INVOKER_HPP #define ROSA_CORE_INVOKER_HPP #include "rosa/core/MessageMatcher.hpp" #include "rosa/support/log.h" #include "rosa/support/sequence.hpp" #include #include namespace rosa { /// Wraps a function and provides a simple interface to invoke the stored /// function by passing actual arguments as a \c rosa::Message object. /// /// \note A \c rosa::Invoker instance is supposed to be owned by a /// \c rosa::MessageHandler instance, and not being used directly from user /// code. class Invoker { protected: /// Creates an instance. /// /// \note Protected constructor restricts instantiation to derived classes. Invoker(void) noexcept; public: /// Destroys \p this object. virtual ~Invoker(void); /// Possible results of an invocation. enum class Result { NoMatch, ///< The wrapped function could not be invoked Invoked ///< The wrapped function has been invoked }; /// Type alias for a smart-pointer for \c rosa::Invoker. using invoker_t = std::unique_ptr; /// Type alias for \c rosa::Invoker::Result. using result_t = Result; /// Tells if a \c rosa::Message object can be used to invoke the function /// wrapped in \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p Msg can be used to invoke the wrapped function virtual bool match(const Message &Msg) const noexcept = 0; /// Tries to invoke the wrapped function with a \c rosa::Message object. /// /// The wrapped function is invoked if the actual \c rosa::Message object can /// be used to invoke it. /// /// \param Msg \c rosa::Message to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with \p Msg virtual result_t operator()(const Message &Msg) const noexcept = 0; /// Instantiates an implementation of \c rosa::Invoker with the given /// function. /// /// \note As there is no empty \c rosa::Message, no \c rosa::Invoker wraps a /// function without any argument. /// /// \todo Enforce F does not potentially throw exception. /// /// \tparam T type of the first mandatory argument /// \tparam Ts types of any further arguments /// /// \param F function to wrap /// /// \return new \c rosa::Invoker::invoker_t object created from the given /// function template static invoker_t wrap(std::function &&F) noexcept; /// Convenience template alias for casting callable stuff to function objects /// for wrapping. /// /// \tparam Ts types of arguments /// /// \todo Should make it possible to avoid using an explicit conversion for /// the arguments of wrap. template using F = std::function; /// Convenience template for preparing non-static member functions into /// function objects for wrapping. /// /// \tparam C type whose non-static member the function is /// \tparam Ts types of arguments /// /// \see \c THISMEMBER template static inline F M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept; }; /// Convenience preprocessor macro for the typical use of \c rosa::Invoker::M. /// It can be used inside a class to turn a non-static member function into a /// function object capturing this pointer, so using the actual object when /// handling a \c rosa::Message. /// /// \param FUN the non-static member function to wrap /// /// \note Inside the class \c MyClass, use\code /// THISMEMBER(fun) /// \endcode instead of\code /// Invoker::M(this, &MyClass::fun) /// \endcode #define THISMEMBER(FUN) \ Invoker::M(this, &std::decay::type::FUN) /// Nested namespace with implementation of \c rosa::Invoker and helper /// templates, consider it private. namespace { /// \defgroup InvokerImpl Implementation for rosa::Invoker /// /// Implements the \c rosa::Invoker interface for functions with different /// signatures. /// ///@{ /// Declaration of \c rosa::InvokerImpl implementing \c rosa::Invoker. /// /// \tparam Fun function to wrap template class InvokerImpl; /// Implementation of \c rosa::InvokerImpl for \c std::function. /// /// \tparam T type of the first mandatory argument /// \tparam Ts types of further arguments /// /// \note As there is no empty \c rosa::Message, no \c rosa::Invoker wraps a /// function without any argument, i.e., no /// \c 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 /// \c rosa::Message. using args_t = std::tuple; /// Alias for \c rosa::MessageMatcher for the arguments of the stored /// function. using Matcher = MsgMatcher; /// The wrapped function. const function_t F; /// Invokes \c InvokerImpl::F by unpacking arguments from a \c std::tuple with /// the help of the actual template arguments. /// /// \tparam S sequence of numbers indexing \c std::tuple for arguments /// /// \param Args arguments to invoke \c InvokerImpl::F with /// /// \pre the length of \p S and size of \p Args are matching:\code /// sizeof...(S) == std::tuple_size::value /// \endcode template inline void invokeFunction(Seq, const args_t &Args) const noexcept; public: /// Creates an instance. /// /// \param F function to wrap /// /// \pre \p F is valid:\code /// bool(F) /// \endcode InvokerImpl(function_t &&F) noexcept : F(F) { ASSERT(bool(F)); // Sanity check. } /// Destroys \p this object. ~InvokerImpl(void) = default; /// Tells if a \c rosa::Message object can be used to invoke the function /// wrapped in \p this object. /// /// \param Msg \c rosa::Message to check /// /// \return whether \p Msg can be used to invoke the wrapped function bool match(const Message &Msg) const noexcept override { return Matcher::doesStronglyMatch(Msg); - }; + } /// Tries to invoke the wrapped function with a \c rosa::Message object. /// /// The wrapped function is invoked if the actual \c rosa::Message object can /// be used to invoke it. /// /// \param Msg \c rosa::Message to try to invoke the wrapped function with /// /// \return whether the wrapped function could be invoked with \p Msg 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)); + invokeFunction(seq_t(), 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 { STATIC_ASSERT(sizeof...(S) == std::tuple_size::value, "wrong number of type parameters"); 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))); } template Invoker::F Invoker::M(C *O, void (C::*Fun)(Ts...) noexcept) noexcept { return [ O, Fun ](Ts... Vs) noexcept->void { (O->*Fun)(Vs...); }; } } // End namespace rosa #endif // ROSA_CORE_INVOKER_HPP diff --git a/include/rosa/core/Message.hpp b/include/rosa/core/Message.hpp index ce9e6ec..7a1fb74 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 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 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 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 bool isTypeAt(const size_t Pos) const noexcept; + template 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(Pos) /// \endcode - template const Type &valueAt(const size_t Pos) const noexcept; + template + 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 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 class LocalMessage final : public Message, private TokenizedStorage { 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(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(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::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 Message::Message(const Type &, const Types &...) noexcept : T(TypeToken::type, typename std::decay::type...>::Value), Size(lengthOfToken(T)) { ASSERT(validToken(T) && lengthOfToken(T) == (1 + sizeof...(Types))); // Sanity check. - LOG_TRACE("Creating Message with Token(" + to_string(T) + ")"); + LOG_TRACE("Creating Message with Token(" + std::to_string(T) + ")"); } /// \note The implementation instantiates a private local template class /// \c LocalMessage. template message_t Message::create(const Type &T, const Types &... Ts) noexcept { return message_t(new LocalMessage(T, Ts...)); } /// \note The implementation instantiates a private local template class /// \c LocalMessage. template 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 { +bool Message::isTypeAt(const token_size_t Pos) const noexcept { ASSERT(Pos < Size); Token TT = T; dropNOfToken(TT, Pos); return isHeadOfTokenTheSameType(TT); } template -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(Pos)); return *static_cast(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 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 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 struct MessageMatcher> { /// \c rosa::Token associated to the given \c rosa::TypeList. static constexpr Token T = TypeToken::Value; /// Tells if the values stored in a \c rosa::Message instance are matching /// types given as \c rosa::TypeList, 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 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:\code /// doesStronglyMatch(Msg) /// \endcode static inline std::tuple extractedValues(const Message &Msg) noexcept; }; /// Turns a list of types into a \c rosa::TypeList for \c rosa::MessageMatcher. template using MsgMatcher = MessageMatcher>; /// 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 struct MessageMatcherImpl; /// Specialization for \c rosa::EmptyTypeList. template <> struct MessageMatcherImpl { 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 struct MessageMatcherImpl, 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(Pos) && Msg.valueAt(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>::doesStronglyMatchFrom(Msg, Pos + 1); } static std::tuple &, 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::Value), MessageMatcherImpl>::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 struct MessageMatcherImpl> { 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(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>::doesStronglyMatchFrom(Msg, Pos + 1); } static std::tuple - 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(Pos)), MessageMatcherImpl>::extractedValuesFrom(Msg, Pos + 1)); } }; ///@} } // End namespace template bool MessageMatcher>::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>::Type>::doesStronglyMatchFrom(Msg, 0); } template std::tuple MessageMatcher>::extractedValues( const Message &Msg) noexcept { ASSERT(doesStronglyMatch(Msg)); // \note Match against a list with squashed integers as \c rosa::Token is // generated. return MessageMatcherImpl>::Type>::extractedValuesFrom(Msg, 0); } } // End namespace rosa #endif // ROSA_CORE_MESSAGEMATCHER_HPP diff --git a/include/rosa/core/Unit.h b/include/rosa/core/Unit.h index 377443a..47c42f6 100644 --- a/include/rosa/core/Unit.h +++ b/include/rosa/core/Unit.h @@ -1,114 +1,114 @@ //===-- rosa/core/Unit.h ----------------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/core/Unit.h /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017 /// /// \brief Declaration of \c rosa::Unit base-class. /// //===----------------------------------------------------------------------===// #ifndef ROSA_CORE_UNIT_H #define ROSA_CORE_UNIT_H #include "rosa/support/atom.hpp" #include "rosa/core/forward_declarations.h" #include #include namespace rosa { /// Base class for every entity in a \c rosa::System that has to be identified /// and traced. /// /// \note Life-cycle of \c rosa::Unit instances is supposed to be managed by the /// \c rosa::System owning the instance, do not create and destroy any /// \c rosa::Unit directly. class Unit { public: /// Identifies the *kind* of \p this object. /// /// \note Kind is dependent on the \c rosa::System owning \p this object. const AtomValue Kind; /// Unique identifier for \p this object. /// /// \note The unique identifier is assigned by the \c rosa::System owning /// \p this object upon creation. const id_t Id; /// Textual identifier of \p this object. /// /// \note Textual identifiers of \c rosa::Unit instances are not necessarily /// unique in their owning \c rosa::System. const std::string Name; protected: /// The \c rosa::System owning \p this object. System &S; public: - /// Full qualified name of \p this object. + /// Fully qualified name of \p this object. const std::string FullName; public: /// Creates a new instnace. /// /// \param Kind the kind of the new instance /// \param Id the unique identifier of the new instance /// \param Name the name of the new instance /// \param S \c rosa::System owning the new instance /// /// \pre \p Name is not empty:\code /// !Name.empty() /// \endcode Unit(const AtomValue Kind, const id_t Id, const std::string &Name, System &S) noexcept; /// No copying and moving of \c rosa::Unit instances is possible. ///@{ Unit(const Unit &) = delete; Unit(Unit &&) = delete; Unit &operator=(const Unit &) = delete; Unit &operator=(Unit &&) = delete; ///@} /// Destroys \p this object. virtual ~Unit(void); /// Dumps \p this object into a \c std::string for tracing purposes. /// /// Subclasses are supposed to override this function. /// /// \return \c std::string representing the state of \p this object virtual std::string dump(void) const noexcept; protected: /// Returns a reference to the \c rosa::System owning \p this object. /// /// \note Subclasses may override the function to return a reference of a /// subtype of \c rosa::System. /// /// \return reference of \c rosa::Unit::S virtual System &system() const noexcept; }; /// Dumps a \c rosa::Unit instance to a given \c std::ostream. /// /// \param [in,out] OS output stream to dump to /// \param U \c rosa::Unit to dump /// /// \return \p OS after dumping \p U to it std::ostream &operator<<(std::ostream &OS, const Unit &U); } // End namespace rosa #endif // ROSA_CORE_UNIT_H diff --git a/include/rosa/deluxe/DeluxeAgent.hpp b/include/rosa/deluxe/DeluxeAgent.hpp old mode 100755 new mode 100644 index 809290a..7a94a57 --- a/include/rosa/deluxe/DeluxeAgent.hpp +++ b/include/rosa/deluxe/DeluxeAgent.hpp @@ -1,706 +1,1479 @@ //===-- 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 "rosa/deluxe/DeluxeExecutionPolicy.h" +#include "rosa/deluxe/DeluxeTuple.hpp" #include /// 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 DAHANDLERNAME(N) handleSlave_##N +#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 DAHANDLERDEFN(T, N) \ - void DAHANDLERNAME(N)(atoms::Slave, id_t SlaveId, T Value) noexcept { \ - saveInput(SlaveId, Value); \ +#define DASLAVEHANDLERDEFN(T, N) \ + void DASLAVEHANDLERNAME(N)(atoms::Slave, id_t SlaveId, token_size_t Pos, \ + T Value) noexcept { \ + saveInput(SlaveId, Pos, 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, token_size_t Pos, \ + T Value) noexcept { \ + saveMasterInput(MasterId, Pos, Value); \ } -/// Convenience macro for \c DAHANDLERDEFN with identical arguments. +/// Convenience macro for \c DASLAVEHANDLERDEFN with identical arguments. /// -/// \see \c DAHANDLERDEFN +/// \see \c DASLAVEHANDLERDEFN /// -/// This macro can be used instead of \c DAHANDLERDEFN if the actual value of -/// \p T can be used as a part of a valid identifier. +/// 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 DAHANDLERDEF(T) DAHANDLERDEFN(T, T) +#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 DAHANDLERDEFN. +/// \c DAMASTERHANDLERDEFN. /// /// Used in the constructor of \c rosa::deluxe::DeluxeAgent to initialize super -/// class \c rosa::Agent with member function defined by \c DAHANDLERDEFN. +/// class \c rosa::Agent with member function defined by \c DAMASTERHANDLERDEFN. /// -/// \see \c DAHANDLERDEFN, \c THISMEMBER +/// \see \c DAMASTERHANDLERDEFN, \c THISMEMBER /// /// \param N name suffix for the function identifier -#define DAHANDLERREF(N) THISMEMBER(DAHANDLERNAME(N)) +#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 There is a compatible *execution policy* set, all input-related /// container objects have a size matching \c /// rosa::deluxe::DeluxeAgent::NumberOfInputs, thus having a corresponding entry -/// for each input. Types of input values are consistent throughout all the -/// input-related containers. No *slave* is registered at more than one input -/// position. *Slave* registrations and corresponding reverse lookup information -/// are consistent. +/// 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 and type-related +/// information of input and master-output values are consistent throughout all +/// the input-related and master-output-related containers, respectively. The +/// actual values in \c rosa::deluxe::DeluxeAgent::InputNextPos and \c +/// rosa::deluxe::DeluxeAgent::MasterInputNextPos are valid with respect to the +/// corresponding types. 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; /// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of /// \c this object. std::unique_ptr ExecutionPolicy; public: - /// Template alias for function objects used to process input and generate - /// output for \c rosa::deluxe::DeluxeAgent. - /// - /// The output generated by the function is optional as an agent may decide - /// not to output anything at some situation. - /// - /// \note The function used for \c D is to be \c noexcept. - /// - /// \tparam T type of output - /// \tparam As types of input values - template - using D = std::function(std::pair...)>; - /// The type of values produced by \p this object. /// - /// That is the type of values \p this object sends to its *master*. + /// That is the types of values \p this object sends to its *master* in a \c + /// rosa::deluxe::DeluxeTUple. /// /// \see \c rosa::deluxe::DeluxeAgent::master - const TypeNumber OutputType; + const Token OutputType; /// Number of inputs processed by \p this object. const size_t NumberOfInputs; -private: + /// The type of values \p this object processes from its *master*. + /// + /// That is the types of values \p this object receives from its *master* in a + /// \c rosa::deluxe::DeluxeTuple. + /// + /// \see \c rosa::deluxe::DeluxeAgent::master + const Token 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 \c rosa::Token values stored correspond to \c + /// rosa::deluxe::DeluxeTuple instances at each argument position. The \c + /// rosa::TypeNumber values from the stored \c rosa::Token values match the + /// corresponding values in \c rosa::deluxe::DeluxeAgent::InputValues in + /// order. /// - /// \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::D. - const std::vector InputTypes; + /// \note The position of a \c rosa::Token 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 InputTypes; + + /// Indicates which element of an input is expected from any particular + /// *slave*. + /// + /// The *slave* is supposed to send one \c rosa::deluxe::DeluxeTuple value + /// element by element in their order of definition. This member field tells + /// the element at which position in the tuple should be received next from + /// the *slave* at a given position. + /// + /// \p this object is supposed to be triggered only when input values has been + /// received completely, that is all values in the field should hold the value + /// `0`. + /// + /// \see \c rosa::deluxe::DeluxeAgent::handleTrigger + /// \c rosa::deluxe::DeluxeAgent::saveInput + std::vector InputNextPos; /// 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::D. + /// \c rosa::deluxe::DeluxeAgent::DeluxeAgent. std::vector InputChanged; + /// Tells at which position in \c rosa::deluxe::DeluxeAgent::InputValues the + /// input from any particular *slave* starts. + /// + /// \note A value in the vector corresponds to the *slave* at the same + /// position and it is the sum of the elements of input values from *slaves* + /// at previous positions. + /// + /// \see \c rosa::deluxe::DeluxeAgent::saveInput + const std::vector InputStorageOffsets; + /// Stores the actual input values. /// /// \note The types of stored values match the corresponding - /// \c rosa::TypeNumber values in \c rosa::deluxe::DeluxeAgent::InputTypes. + /// \c rosa::TypeNumber values (in \c rosa::Token in order) 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::D. + /// indicates which element of the tuple of which argument of \p this object's + /// processing function it is. See also \c + /// rosa::deluxe::DeluxeAgent::DeluxeAgent. const std::unique_ptr InputValues; + /// Indicates which element of the master-input is expected from the *master*. + /// + /// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value + /// element by element in their order of definition. This member field tells + /// the element at which position should be received next. + /// + /// \p this object is supposed to be triggered only when a complete + /// master-input has been received, that is the field should hold the value + /// `0`. + /// + /// \see \c rosa::deluxe::DeluxeAgent::handleTrigger + /// \c rosa::deluxe::DeluxeAgent::saveMasterInput + token_size_t MasterInputNextPos; + + /// Indicates whether the input value from the *master* has been changed since + /// the last trigger received from the system. + /// + /// The flag is reset to \c false upon handling a trigger and then set to \c + /// true by \c rosa::deluxe::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 types indicated by \c + /// rosa::deluxe::DeluxeAgent::MasterInputType. + const std::unique_ptr MasterInputValue; + + /// Types of output values produced by \p this object for its *slaves*. + /// + /// That is the types of values \p this object sends to its *slaves* in a \c + /// rosa::deluxe::DeluxeTuple. + /// + /// \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 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; /// Handles trigger from the system. /// - /// The actual function processing *slave* inputs and generating optional - /// output to *master* is captured in a lambda expression that is in turn - /// wrapped in a \c std::function object. The lambda expression calls the - /// processing function with the actual input data and sends its result -- if - /// any -- to *master* by calling \c rosa::deluxe::DeluxeAgent::sendToMaster. - /// Also, all the flags stored in \c rose::deluxe::DeluxeAgent::InputChanged - /// are reset when the current input values are processed. The function - /// \c rosa::deluxe::DeluxeAgent::handleTrigger needs only to call the + /// 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::triggerHandlerFromProcessingFunction + /// \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 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::D. + /// \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> Slaves; /// Associates \c rosa::id_t values to corresponding indices of registered /// *slaves*. /// /// \see \c rosa::deluxe::DeluxeAgent::Slaves std::map SlaveIds; - /// Tells whether types \p As... match the input types of \p this object. + /// Tells the unique identifier of the *master* of \p this object, if any + /// registered. /// - /// \tparam As types to match against values in - /// \c rosa::deluxe::DeluxeAgent::InputTypes + /// \return the unique identifier of the *master* /// - /// \return if types \p As... match \c rosa::TypeNumber values stored in + /// \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 - template bool inputTypesMatch(void) const noexcept; + /// + /// \note Instatiation of the template fails if \p As is not \c + /// rosa::TypeList. + /// + /// \return if types in \p As are instances of \c rosa::deluxe::DeluxeTuple + /// and their types match \c rosa::Token values stored in \c + /// rosa::deluxe::DeluxeAgent::InputTypes + template 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::Token and in turn \c + /// rosa::TypeNumber values stored in \c + /// rosa::deluxe::DeluxeAgent::MasterOutputTypes + template bool masterOutputTypesMatch(void) const noexcept; + + /// Gives the current input value for slave position \p Pos. + /// + /// \tparam Pos slave position to get input value for + /// \tparam Ts types of elements of the input value + /// \tparam S0 indices for accessing elements of the input value + /// + /// \note The arguments provide types and indices statically as template + /// arguments \p Ts... \p S0..., respectively, so their actual values are + /// ignored. + /// + /// \return current input value for slave position \p Pos + /// + /// \pre Statically, the provided indices \p S0... match the length of \p + /// Ts...: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Pos is a valid slave position and type arguments + /// \p Ts... match the corresponding input value: \code + /// Pos < NumberOfInputs && DeluxeTuple::TT == InputTypes[Pos] + /// \endcode + template + DeluxeTuple prepareInputValueAtPos(TypeList, Seq) 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() && sizeof...(As) == sizeof...(S0) + /// \pre Statically, all type arguments \p As... are instances of \c + /// rosa::deluxe::DeluxeTuple and the provided indices \p S0... match the + /// length of \p As...: \code + /// TypeListAllDeluxeTuple>::Value && + /// sizeof...(As) == sizeof...(S0) + /// \endcode Dynamically, type arguments \p As... match the input types of \p + /// this object: \code + /// inputTypesMatch>() /// \endcode template std::tuple...> prepareCurrentInputs(Seq) const noexcept; - /// Invokes a processing function matching the output and input types of - /// \p this object with actual arguments provided in a \c std::tuple. + /// Invokes a processing function matching the input, output, and + /// master-output 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 - static Optional invokeWithTuple(D F, - std::tuple...> Args, - Seq) noexcept; + template + static std::tuple, Optional...> + invokeWithTuple(std::function, Optional...>( + std::pair...)> + F, + const std::tuple...> Args, + Seq) noexcept; + + /// Handles a master-output value for a particular *slave* position. + /// + /// \p Value is a \c rosa::Optional resulted by a processing function and + /// contains a master-output value for the *slave* at position \p Pos. The + /// function takes the master-output value and sends its actual value, if any, + /// to the corresponding *slave*. + /// + /// \note A master-output of type \c rosa::deluxe::EmptyDeluxeTuple indicates + /// no actual output and hence no message is generated for a position whose + /// corresponding master-output type is \c rosa::deluxe::EmptyDeluxeTuple. + /// + /// \note The function provides position-based implementation for \c + /// rosa::deluxe::DeluxeAgent::handleMasterOutputs. + /// + /// \tparam Pos the position of the master-output to send \p Value for + /// \tparam Ts types of elements in \p Value + /// + /// \param Value \c rosa::deluxe::DeluxeTuple resulted by the processing + /// function for *slave* position \p Pos + /// + /// \pre \p Pos is a valid master-output position and \p Value matches the + /// master-output type of \p this object at position \p Pos: \code + /// Pos < NumberOfMasterOutputs && + /// DeluxeTuple::TT == MasterOutputTypes[Pos] + /// \endcode + template + void + handleMasterOutputAtPos(const Optional> &Value) 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 + /// + /// \note Instantiation fails if any of the type arguments \p Ts... starting + /// at position \p Offset is not an instance of \c rosa::deluxe::DeluxeTuple + /// or the number of types \p Ts... is not consistent with the other template + /// arguments. + /// + /// \param Output \c std::tuple resulted by a processing function + /// + /// \pre Statically, type arguments \p Ts... starting at position \p Offset + /// are instances of \c rosa::deluxe::DeluxeTuple and the number of types \p + /// Ts... is consistent with the other template arguments: \code + /// TypeListAllDeluxeTuple< + /// typename TypeListDrop>::Type>::Value && + /// sizeof...(Ts) == Offset + sizeof...(S0) + /// \endcode Dynamically, \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 extracting all master-output values from + /// \p Output: \code + /// masterOutputTypesMatch>::Type>() && + /// sizeof...(S0) == NumberOfMasterOutputs + /// \endcode + template + void handleMasterOutputs(const std::tuple...> &Output, + Seq) noexcept; - /// Wraps a processing function into a trigger handler. + /// 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 MTs types of elements of master-input processed by \p MF /// \tparam T type of output + /// \tparam Ts types of master-output values /// \tparam As types of input values + /// \tparam S0 indices for accessing master-input values + /// + /// \note Instantiation fails if any of the type arguments \p T, \p Ts..., + /// and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple. /// + /// \param MF function processing master-input and generating output /// \param F function processing inputs and generating output /// - /// \pre Template arguments \p T and \p As... match the corresponding - /// types \p this object was created with: \code - /// OutputType == TypeNumberOf::Value && inputTypesMatch() + /// \note The last argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates + /// that \p this object does not receive master-input, \p MF is never called + /// if \p MTs is empty. + /// + /// \return trigger handler function based on \p F and \p MF + /// + /// \pre Statically, type arguments \p T, \p Ts..., and \p As... are + /// instances of \c rosa::deluxe::DeluxeTuple and the indices match + /// master-input elements: \code + /// TypeListAllDeluxeTuple>::Value && + /// sizeof...(MTs) == sizeof...(S0) + /// \endcode Dynamically, template arguments \p MTs..., \p T, \p Ts..., and + /// \p As... match the corresponding types \p this object was created with: + /// \code + /// MasterInputType == DeluxeTuple::TT && OutputType == T::TT && + /// inputTypesMatch>() && + /// masterOutputTypesMatch>() /// \endcode - template - H triggerHandlerFromProcessingFunction(D &&F) noexcept; + template + H triggerHandlerFromProcessingFunctions( + std::function...>( + std::pair, bool>)> &&MF, + std::function< + std::tuple, Optional...>(std::pair...)> &&F, + Seq) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// - /// \todo Enforce F does not potentially throw exception. + /// 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 T and \p As... - /// is not a built-in type. + /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p + /// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or + /// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple or the + /// number of inputs and master-outputs are not equal. + /// + /// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed + /// object does not receive master-input. Similarly, if any of \p Ts... is \c + /// rosa::deluxe::EmptyDeluxeTuple, 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 F function to process input values and generate output with - /// - /// \pre Statically, all of the type arguments \p T and \p As... is a - /// built-in type: \code - /// TypeListSubsetOf, BuiltinTypes>::Value - /// \endcode Dynamically, the instance is created as of kind - /// \c rosa::deluxe::atoms::AgentKind: \code + /// \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 the type arguments \p MT, \p T, \p Ts..., and \p + /// As... are instances of \c rosa::deluxe::DeluxeTuple, with \p T and \p + /// As... containing at least one element, and the number of input and + /// master-output types are equal: \code + /// TypeListAllDeluxeTuple::Value && + /// T::Length > 0 && (true && ... && As::Length > 0) && + /// 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 , BuiltinTypes>::Value>> - DeluxeAgent(const AtomValue Kind, const id_t Id, const std::string &Name, - MessagingSystem &S, D &&F) noexcept; + TypeListAllDeluxeTuple>::Value && + (T::Length > 0) && (true && ... && (As::Length > 0)) && + sizeof...(Ts) == sizeof...(As)>> + DeluxeAgent( + const AtomValue Kind, const id_t Id, const std::string &Name, + MessagingSystem &S, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept; /// Destroys \p this object. ~DeluxeAgent(void) noexcept; /// Returns the current execution policy of \p this object. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note The returned reference is valid only as long as \c /// rosa::deluxe::DeluxeAgent::setExecutionPolicy() is not called and \p this /// object is not destroyed. /// /// \return \c rosa::deluxe::DeluxeAgent::ExecutionPolicy const DeluxeExecutionPolicy &executionPolicy(void) const noexcept; /// Sets the current execution policy of \p this object to \p EP. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note \p EP is set only if it can handle \p this object. /// /// \param EP the new execution policy for \p this object /// /// \return if \p EP was successfully set for \p this object. bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; /// The *master* of \p this object, if any is registered. /// /// \see \c rosa::deluxe::DeluxeAgent::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// + /// \note Any call to \c rosa::deluxe::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 _Master) noexcept; - /// Tells the type of values consumed from the *slave* at a position. + /// Tells the types 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. + /// That is the type of values \p this object expect to be sent to it in a \c + /// rosa::deluxe::DeluxeTuple 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 + /// \return \c rosa::Token representing the types 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; + Token inputType(const size_t Pos) const noexcept; + + /// Tells the types of values produced for the *slave* at a position. + /// + /// That is the types of values \p this object potentially sends in a \c + /// rosa::deluxe::DeluxeTuple to its *slave* registered at position \p Pos. + /// + /// \see \c rosa::deluxe::DeluxeAgent::slave + /// + /// \param Pos position of *slave* + /// + /// \return \c rosa::Token representing the types of values produced for + /// the *slave* at position \p Pos + /// + /// \pre \p Pos is a valid index of input: \code + /// Pos < NumberOfMasterOutputs + /// \endcode + Token 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 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: + /// 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(unwrapAgent(*Slave)).OutputType == - /// InputTypes[Pos]) || + /// InputTypes[Pos] && + /// (emptyToken(MasterOutputTypes[Pos]) || + /// static_cast(unwrapAgent(*Slave)).MasterInputType + /// == MasterOutputTypes[Pos])) || /// (unwrapAgent(*Slave).Kind == rosa::deluxe::atoms::AgentKind && /// static_cast(unwrapAgent(*Slave)).OutputType == - /// InputTypes[Pos])) + /// InputTypes[Pos] && + /// (emptyToken(MasterOutputTypes[Pos]) || + /// static_cast(unwrapAgent(*Slave)).MasterInputType == + /// MasterOutputTypes[Pos]))) /// \endcode void registerSlave(const size_t Pos, const Optional 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 + /// The elements from \p Value are sent one by one in separate messages to the + /// *master*. + /// + /// \tparam Ts types of the elements in \p Value + /// \tparam S0 indices for accessing elements of \p Value + /// + /// \param Value value to send + /// + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Ts match \c + /// rosa::deluxe::DeluxeiAgent::OutputType: \code + /// OutputType == TypeToken::Value + /// \endcode + template + void sendToMaster(const DeluxeTuple &Value, Seq) 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. + /// + /// The elements from \p Value are sent one by one in separate messages to the + /// *slave*. + /// + /// \tparam Ts types of the elements in \p Value + /// \tparam S0 indices for accessing elements of \p Value /// + /// \param Pos the position of the *slave* to send \p Value to /// \param Value value to send /// - /// \pre \p T matches \c rosa::deluxe::DeluxeiAgent::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Pos is a valid *slave* position and \p Ts match + /// \c rosa::deluxe::DeluxeiAgent::MasterOutputTypes[Pos]: \code + /// Pos < NumberOfMasterOutputs && + /// MasterOutputTypes[Pos] == TypeToken::Value /// \endcode - template void sendToMaster(const T &Value) noexcept; + template + void sendToSlave(const size_t Pos, const DeluxeTuple &Value, + Seq) 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. + /// + /// \pre Master-input and all input from *slaves* are supposed to be + /// completely received upon triggering: \code + /// MasterInputNextPos == 0 && + /// std::all_of(InputNextPos.begin(), InputNextPos.end(), + /// [](const token_size_t &I){return I == 0;}) + /// \endcode 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. + /// The function stores \p Value at position \p Pos 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. The function also + /// takes care of checking and updating \c + /// rosa::deluxe::DeluxeSensor::MasterInputNextPos at the corresponding + /// position: increments the value and resets it to `0` when the last element + /// is received. /// /// \note Utilized by member functions of group \c DeluxeAgentInputHandlers. /// /// \tparam T type of input to store /// /// \param Id unique identifier of *slave* + /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple /// \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 + /// \pre The *slave* with \p Id is registered, \p Pos is the expected + /// position of input from the *slave*, 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::Value + /// Pos == InputNextPos[SlaveIds.find(Id)->second] && + /// typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) == + /// TypeNumberOf::Value + /// \endcode + template + void saveInput(id_t Id, token_size_t Pos, T Value) noexcept; + + /// Stores a new input value from the *master*. + /// + /// The function stores \p Value at position \p Pos in \c + /// rosa::deluxe::DeluxeAgent::MasterInputValue and also sets the + /// flag \c rosa::deluxe::DeluxeAgent::MasterInputChanged. The function also + /// takes care of checking and updating \c + /// rosa::deluxe::DeluxeAgent::MasterInputNextPos: increments its value and + /// reset to `0` when the last element is received. + /// + /// \note Utilized by member functions of group \c + /// DeluxeAgentMasterInputHandlers. + /// + /// \tparam T type of input to store + /// + /// \param Id unique identifier of the *master* + /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple + /// \param Value the input value to store + /// + /// \pre The *master* with \p Id is registered, \p Pos is the expected + /// position of master-input, and the input from the *master* at position \p + /// Pos is expected to be of type \p T: \code + /// Master && masterId() == Id && Pos == MasterInputNextPos && + /// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value /// \endcode - template void saveInput(id_t Id, T Value) noexcept; + template + void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept; - /// \defgroup DeluxeAgentInputHandlers Input handlers of rosa::deluxe::DeluxeAgent + /// \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. 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. + /// 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 DAHANDLERDEF. + /// \note The member functions in this group are defined by \c + /// DASLAVEHANDLERDEF. /// /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. /// ///@{ - DAHANDLERDEF(AtomValue) - DAHANDLERDEF(int16_t) - DAHANDLERDEF(int32_t) - DAHANDLERDEF(int64_t) - DAHANDLERDEF(int8_t) - DAHANDLERDEFN(long double, long_double) - DAHANDLERDEFN(std::string, std__string) - DAHANDLERDEF(uint16_t) - DAHANDLERDEF(uint32_t) - DAHANDLERDEF(uint64_t) - DAHANDLERDEF(uint8_t) - DAHANDLERDEF(unit_t) - DAHANDLERDEF(bool) - DAHANDLERDEF(double) - DAHANDLERDEF(float) + 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 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. + /// + ///@{ + + 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(unit_t) + DAMASTERHANDLERDEF(bool) + DAMASTERHANDLERDEF(double) + DAMASTERHANDLERDEF(float) + + /// @} }; -/// Anonymous namespace with implementation for -/// \c rosa::deluxe::DeluxeAgent::inputTypesMatch, consider it private. +/// Anonymous namespace with implementation for \c +/// rosa::deluxe::DeluxeAgent::DeluxeAgent, \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 rosa::deluxe::DeluxeAgent::inputTypesMatch. +/// Calculates storage offsets for values of \p Ts... stored in a \c +/// rosa::TokenizedStorage. +/// +/// \note Utilized by \c rosa::deluxe::DeluxeAgnet::DeluxeAgent to initialize \c +/// rosa::deluxe::DeluxeAgent::InputStorageOffsets. +/// +/// \tparam Ts types whose offsets to calculate +/// \tparam S0 indices for referring to positions in \p Ts... /// -/// \note Matching a list of types \p As... against a \c std::vector of -/// \c rosa::TypeNumber values, \c InputTypes, like \code -/// bool match = InputTypesMatchImpl::f(InputTypes, 0); +/// \note Instantiation fails if any of the type arguments \p Ts... is not an +/// instance of \c rosa::deluxe::DeluxeTuple. +/// +/// \note The only argument provides indices statically as template +/// arguments \p S0..., so its actual value is ignored. +/// +/// \return \c std::vector containing the calculated offsets +/// +/// \pre Statically, all the type arguments \p Ts... are instances of \c +/// rosa::deluxe::DeluxeTuple and the indices match the types: \code +/// TypeListAllDeluxeTuple>::Value && +/// sizeof...(Ts) == sizeof...(S0) /// \endcode +template < + typename... Ts, size_t... S0, + typename = std::enable_if_t>::Value>> +static std::vector storageOffsets(Seq) noexcept { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + std::vector Offsets(sizeof...(Ts)); + // Do nothing for no types. + if constexpr (sizeof...(Ts) != 0) { + Offsets[0] = 0; // The offset of the very first value is always `0`. + // Calculate further offsets... + (((S0 != sizeof...(Ts) - 1) && + (Offsets[S0 + 1] = Offsets[S0] + Ts::Length)), + ...); + } + return Offsets; +} + +/// Template \c struct whose specializations provide a recursive implementation +/// for \c TypesMatchList. /// /// \tparam As types to match -template struct InputTypesMatchImpl; +template struct TypesMatchImpl; -/// Template specialization for the general case, when at least one type is to -/// be matched. +/// Template specialization for the case, when at least one type is to +/// be matched and that is an instance of \c rosa::deluxe::DeluxeTuple. /// -/// \tparam A first type to match +/// \tparam Ts types of elements in the \c rosa::deluxe::DeluxeTuple to match /// \tparam As further types to match -template struct InputTypesMatchImpl { - /// Tells whether types \p A, \p As... match \c rosa::TypeNumber values - /// stored in \p InputTypes starting at position \p Pos. +template +struct TypesMatchImpl, As...> { + /// Tells whether types \c rosa::deluxe::DeluxeTuple and \p As... match + /// \c rosa::Token values stored in \p Tokens 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 - /// InputTypes, then further types \p As.. are matched recursively starting - /// at position \c (Pos + 1). - /// - /// \param InputTypes container of \c rosa::TypeNumber values to match - /// types against - /// \param Pos position in \p InputTypes to start matching at - /// - /// \return if types \p A, \p As... match \c rosa::TypeNumber values stored - /// in \p InputTypes starting at position \p Pos - static bool f(const std::vector &InputTypes, - size_t Pos) noexcept { - return Pos < InputTypes.size() && - TypeNumberOf::Value == InputTypes[Pos] && - InputTypesMatchImpl::f(InputTypes, Pos + 1); + /// type \c rosa::deluxe::DeluxeTuple against \c rosa::Token at + /// position \p Pos of \p Tokens, then further types \p As... are matched + /// recursively starting at position \c (Pos + 1). + /// + /// \param Tokens container of \c rosa::Token values to match types against + /// \param Pos position in \p Tokens to start matching at + /// + /// \return if types \c rosa::deluxe::DeluxeTuple and \p As... match \c + /// rosa::Token values stored in \p Tokens starting at position \p Pos + static bool f(const std::vector &Tokens, size_t Pos) noexcept { + return Pos < Tokens.size() && TypeToken::Value == Tokens[Pos] && + TypesMatchImpl::f(Tokens, Pos + 1); } }; +/// Template specialization for the case, when at least one type is to +/// be matched and that is *not* an instance of \c rosa::deluxe::DeluxeTuple. +/// +/// \tparam T first type to match +/// \tparam As further types to match +template +struct TypesMatchImpl { + /// Tells whether types \p T and \p As... match \c rosa::Token values stored + /// in \p Tokens starting at position \p Pos. + /// + /// This specialization is used only when \p T is not an instance of \c + /// rosa::deluxe::DeluxeTuple, in which case the match is not successful. + /// + /// \note The function takes two parameters to match the general signature but + /// the actual values are ignored. + /// + /// \return `false` + static bool f(const std::vector &, size_t) noexcept { return false; } +}; + /// Template specialization for the terminal case, when no type remains to /// check. -template <> struct InputTypesMatchImpl<> { - /// Tells whether \p Pos is the number of values stored in \p InputTypes. +template <> struct TypesMatchImpl<> { + /// Tells whether \p Pos is the number of values stored in \p Tokens. /// - /// In this terminal case, there is no more types to matchi because all the + /// 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 InputTypes. That is true if \p Pos points exactly to the end of - /// \p InputTypes. + /// \p Tokens. That is true if \p Pos points exactly to the end of \p Tokens. /// - /// \param InputTypes container of \c rosa::TypeNumber values to match - /// types against - /// \param Pos position in \p InputTypes to start matching at + /// \param Tokens container of \c rosa::Token values to match types against + /// \param Pos position in \p Tokens to start matching at /// - /// \return if \p Pos is the number of values stored in \p InputTypes - static bool f(const std::vector &InputTypes, - size_t Pos) noexcept { - return Pos == InputTypes.size(); + /// \return if \p Pos is the number of values stored in \p Tokens + static bool f(const std::vector &Tokens, size_t Pos) noexcept { + return Pos == Tokens.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 List against a \c std::vector of +/// \c rosa::Token values, \c Tokens, like \code +/// bool match = TypesMatchList::f(Tokens); +/// \endcode +/// If any type in \c rosa::TypeList \p Listis not an instance of \c +/// rosa::deluxe::DeluxeTuple, the match gives a negative result. +/// +/// \tparam List \c rosa::TypeList that contains types to match +template struct TypesMatchList; + +/// Template specialization implementing the feature. +/// +/// \tparam As types to match +template struct TypesMatchList> { + /// Tells whether types \p As... match \c rosa::Token values stored in \p + /// Tokens. + /// + /// The function unwraps the types from \c rosa::TypeList and utilizes \c + /// TypesMatchImpl to do the check. + /// + /// \param Tokens container of \c rosa::Token values to match types against + /// + /// \return if types \p As... match \c rosa::Token values stored in \p Tokens + static bool f(const std::vector &Tokens) noexcept { + return TypesMatchImpl::f(Tokens, 0); } }; } // End namespace -template +template bool DeluxeAgent::inputTypesMatch(void) const noexcept { - return InputTypesMatchImpl::f(InputTypes, 0); + return TypesMatchList::f(InputTypes); +} + +template +bool DeluxeAgent::masterOutputTypesMatch(void) const noexcept { + return TypesMatchList::f(MasterOutputTypes); +} + +template +DeluxeTuple DeluxeAgent::prepareInputValueAtPos(TypeList, + Seq) const + noexcept { + using T = DeluxeTuple; + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); + ASSERT(inv() && Pos < NumberOfInputs && T::TT == InputTypes[Pos]); + + const token_size_t StorageOffset = InputStorageOffsets[Pos]; + + // The below should hold because of the above, just leave it for sanity check. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + + // Get all elements of the tuple in a fold expression. + return T(*static_cast(InputValues->pointerTo( + static_cast(StorageOffset + S0)))...); } template std::tuple...> - DeluxeAgent::prepareCurrentInputs(Seq) 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; - ASSERT(inv() && (this->*MFP)() && sizeof...(As) == sizeof...(S0)); - - return std::make_tuple( - std::make_pair(*static_cast(InputValues->pointerTo(S0)), - InputChanged[S0])...); +DeluxeAgent::prepareCurrentInputs(Seq) const noexcept { + STATIC_ASSERT(TypeListAllDeluxeTuple>::Value, + "not tuple types"); + STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "inconsistent type arguments"); + ASSERT(inv() && inputTypesMatch>()); + + return std::make_tuple(std::make_pair( + prepareInputValueAtPos(typename UnwrapDeluxeTuple::Type(), + seq_t()), + InputChanged[S0])...); } -template -Optional DeluxeAgent::invokeWithTuple( - D F, - std::tuple...> Args, - Seq) noexcept { +template +std::tuple, Optional...> DeluxeAgent::invokeWithTuple( + std::function< + std::tuple, Optional...>(std::pair...)> + F, + const std::tuple...> Args, Seq) noexcept { STATIC_ASSERT(sizeof...(As) == sizeof...(S0), "wrong number of type parameters"); return F(std::get(Args)...); } -template -DeluxeAgent::H -DeluxeAgent::triggerHandlerFromProcessingFunction(D &&F) 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; - ASSERT(OutputType == TypeNumberOf::Value && (this->*MFP)()); - - return [ this, F ]() noexcept { +template +void DeluxeAgent::handleMasterOutputAtPos( + const Optional> &Value) noexcept { + using MOT = DeluxeTuple; + ASSERT(inv() && Pos < NumberOfMasterOutputs && + MOT::TT == MasterOutputTypes[Pos]); + // Do not do anything for master-output of type \c + // rosa::deluxe::EmptyDeluxeTuple and when \p Value is empty. + if constexpr (!std::is_same::value) { + if (Value) { + sendToSlave(Pos, *Value, seq_t()); + } + } else { + (void)Value; + } + ASSERT(inv()); +} + +template +void DeluxeAgent::handleMasterOutputs(const std::tuple...> &Output, + Seq) noexcept { + using MOTs = typename TypeListDrop>::Type; + STATIC_ASSERT(TypeListAllDeluxeTuple::Value, + "not tuple type arguments"); + STATIC_ASSERT(sizeof...(Ts) == Offset + sizeof...(S0), + "inconsistent arguments"); + ASSERT(inv() && masterOutputTypesMatch() && + sizeof...(S0) == NumberOfMasterOutputs); + // Handle each master-output position in a fold expression. + (handleMasterOutputAtPos(std::get(Output)), ...); + ASSERT(inv()); +} + +template +DeluxeAgent::H DeluxeAgent::triggerHandlerFromProcessingFunctions( + std::function< + std::tuple...>(std::pair, bool>)> &&MF, + std::function< + std::tuple, Optional...>(std::pair...)> &&F, + Seq) noexcept { + using MT = DeluxeTuple; + STATIC_ASSERT((TypeListAllDeluxeTuple>::Value), + "not tuple type arguments"); + STATIC_ASSERT(sizeof...(MTs) == sizeof...(S0), "inconsistent arguments"); + ASSERT(MasterInputType == MT::TT && OutputType == T::TT && + inputTypesMatch>() && + masterOutputTypesMatch>()); + + return [ this, MF, F ]() noexcept { + // \note These indices work for both inputs and master-outputs. + using SlaveIndices = seq_t; + + // Handle master-input. + // Do not do anything for master-input type \c + // rosa::deluxe::EmptyDeluxeTuple. + if (!std::is_same::value) { + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles master-input." + << std::endl; + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + const auto MasterInputArg = std::make_pair( + // Get all elements of the tuple in a fold expression. + MT(*static_cast( + MasterInputValue->pointerTo(static_cast(S0)))...), + MasterInputChanged); + MasterInputChanged = false; + const std::tuple...> MasterOutput = MF(MasterInputArg); + handleMasterOutputs<0>(MasterOutput, SlaveIndices()); + } + + // Handle inputs. // Call the processing function only if \p ExecutionPolicy allows. if (ExecutionPolicy->shouldProcess(InputChanged)) { - using Indices = typename GenSeq::Type; - auto Args = prepareCurrentInputs(Indices()); + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " handles input." + << std::endl; + const auto InputArgs = prepareCurrentInputs(SlaveIndices()); std::fill(InputChanged.begin(), InputChanged.end(), false); - Optional R = invokeWithTuple(F, Args, Indices()); - if (R) { - sendToMaster(*R); + const std::tuple, Optional...> Output = + invokeWithTuple(F, InputArgs, SlaveIndices()); + const auto OutputToMaster = std::get<0>(Output); + if (OutputToMaster) { + sendToMaster(*OutputToMaster, seq_t()); } + handleMasterOutputs<1>(Output, SlaveIndices()); + } else { + LOG_TRACE_STREAM << "DeluxeAgent " << Name << " skips input." + << std::endl; } }; } -template -DeluxeAgent::DeluxeAgent(const AtomValue Kind, const id_t Id, - const std::string &Name, MessagingSystem &S, - D &&F) noexcept +template +DeluxeAgent::DeluxeAgent( + const AtomValue Kind, const id_t Id, const std::string &Name, + MessagingSystem &S, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), - DAHANDLERREF(AtomValue), DAHANDLERREF(int16_t), - DAHANDLERREF(int32_t), DAHANDLERREF(int64_t), DAHANDLERREF(int8_t), - DAHANDLERREF(long_double), DAHANDLERREF(std__string), - DAHANDLERREF(uint16_t), DAHANDLERREF(uint32_t), - DAHANDLERREF(uint64_t), DAHANDLERREF(uint8_t), DAHANDLERREF(unit_t), - DAHANDLERREF(bool), DAHANDLERREF(double), DAHANDLERREF(float)), - ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), - OutputType(TypeNumberOf::Value), NumberOfInputs(sizeof...(As)), - InputTypes({TypeNumberOf::Value...}), - InputChanged(NumberOfInputs, false), - InputValues(new TokenizedStorage()), - FP(triggerHandlerFromProcessingFunction(std::move(F))), + 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(unit_t), DAMASTERHANDLERREF(bool), + DAMASTERHANDLERREF(double), DAMASTERHANDLERREF(float)), + ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT), + NumberOfInputs(sizeof...(As)), MasterInputType(MT::TT), + NumberOfMasterOutputs(NumberOfInputs), InputTypes({As::TT...}), + InputNextPos(NumberOfInputs, 0), InputChanged(NumberOfInputs, false), + InputStorageOffsets(storageOffsets(seq_t())), + InputValues(new typename TokenizedStorageForTypeList< + typename TypeListUnwrapDeluxeTuple>::Type>:: + Type()), + MasterInputNextPos(0), MasterInputChanged(false), + MasterInputValue(new typename TokenizedStorageForTypeList< + typename UnwrapDeluxeTuple::Type>::Type()), + MasterOutputTypes({Ts::TT...}), + FP(triggerHandlerFromProcessingFunctions(std::move(MF), std::move(F), + seq_t())), Slaves(NumberOfInputs) { ASSERT(Kind == atoms::AgentKind); - LOG_TRACE("DeluxeAgent is created."); + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << " is created." << std::endl; ASSERT(inv()); } -template -void DeluxeAgent::sendToMaster(const T &Value) noexcept { - ASSERT(inv() && OutputType == TypeNumberOf::Value); +template +void DeluxeAgent::sendToMaster(const DeluxeTuple &Value, + Seq) noexcept { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + ASSERT(inv() && OutputType == TypeToken::Value); + + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + // Create a static constant array for these indices to be available as lvalue + // references when creating messages below. \c S0... when used directly in a + // fold expression is a temporary value, which would result in \c + // rosa::Message instances being created with rvalue references. Further, all + // other values would to copied into a temporary variable for making them + /// available as rvalue references (they are constant lvalue references here). + static constexpr std::array Indices{{S0...}}; + + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id + << ") sends to master (" + << static_cast(Master && *Master) << "): " << Value + << " (" << sizeof...(S0) << ")" << std::endl; // 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)); + // Handle each element of the tuple in a fold expression. + (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0], + std::get(Value))), + ...); } ASSERT(inv()); } -template void DeluxeAgent::saveInput(id_t Id, T Value) noexcept { +template +void DeluxeAgent::sendToSlave(const size_t Pos, const DeluxeTuple &Value, + Seq) noexcept { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + ASSERT(inv() && Pos < NumberOfMasterOutputs && + MasterOutputTypes[Pos] == TypeToken::Value); + + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + // Create a static constant array for these indices to be available as lvalue + // references when creating messages below. \c S0... when used directly in a + // fold expression is a temporary value, which would result in \c + // rosa::Message instances being created with rvalue references. Further, all + // other values would to copied into a temporary variable for making them + /// available as rvalue references (they are constant lvalue references here). + static constexpr std::array Indices{{S0...}}; + + // There is a handle and the referred *slave* is in a valid state. + auto Slave = Slaves[Pos]; + + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id + << ") sends to slave (" << static_cast(Slave && *Slave) + << ") at position " << Pos << ": " << Value << " (" + << sizeof...(S0) << ")" << std::endl; + + if (Slave && *Slave) { + // Handle each element of the tuple in a fold expression. + (Slave->sendMessage(Message::create(atoms::Master::Value, Id, Indices[S0], + std::get(Value))), + ...); + } +} + +template +void DeluxeAgent::saveInput(id_t Id, token_size_t Pos, T Value) noexcept { ASSERT(inv() && SlaveIds.find(Id) != SlaveIds.end() && - InputTypes[SlaveIds.find(Id)->second] == TypeNumberOf::Value); + Pos == InputNextPos[SlaveIds.find(Id)->second] && + typeAtPositionOfToken(InputTypes[SlaveIds.find(Id)->second], Pos) == + TypeNumberOf::Value); + + size_t SlavePos = SlaveIds.at(Id); + + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id + << ") saves value from slave at position " << SlavePos + << ": (" << static_cast(Pos) << ") " << Value + << std::endl; + + // Save value. + size_t StoragePos = (size_t)InputStorageOffsets[SlavePos] + Pos; + // This assert must hold if \p this object was successfully constructed. + ASSERT(static_cast(static_cast(StoragePos)) == + StoragePos); + *static_cast( + InputValues->pointerTo(static_cast(StoragePos))) = Value; + + // Update position of next value. + if (++InputNextPos[SlavePos] == lengthOfToken(InputTypes[SlavePos])) { + InputNextPos[SlavePos] = 0; + } - size_t Pos = SlaveIds.at(Id); - *static_cast(InputValues->pointerTo(Pos)) = Value; - InputChanged[Pos] = true; + // Set flag. + InputChanged[SlavePos] = true; ASSERT(inv()); } +template +void DeluxeAgent::saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept { + ASSERT(inv() && Master && masterId() == Id && Pos == MasterInputNextPos && + typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); + + LOG_TRACE_STREAM << "DeluxeAgent " << FullName << "(" << Id + << ") saves value from master: (" << static_cast(Pos) + << ") " << Value << std::endl; + + // Save value. + *static_cast(MasterInputValue->pointerTo(Pos)) = Value; + + // Update position of next value. + if (++MasterInputNextPos == lengthOfToken(MasterInputType)) { + MasterInputNextPos = 0; + } + + // Set flag. + MasterInputChanged = true; + + ASSERT(inv()); +} } // End namespace deluxe } // End namespace rosa -#undef DAHANDLEREF -#undef DAHANDLEDEF -#undef DAHANDLEDEFN -#undef DAHANDLENAME +#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/deluxe/DeluxeAtoms.hpp b/include/rosa/deluxe/DeluxeAtoms.hpp index a2aab57..70963b2 100755 --- a/include/rosa/deluxe/DeluxeAtoms.hpp +++ b/include/rosa/deluxe/DeluxeAtoms.hpp @@ -1,61 +1,59 @@ //===-- rosa/deluxe/DeluxeAtoms.hpp -----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeAtoms.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Definition of \c rosa::AtomValue values and \c rosa::AtomConstant /// types for the implementation of the *deluxe interface*. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXEATOMS_HPP #define ROSA_DELUXE_DELUXEATOMS_HPP #include "rosa/support/atom.hpp" namespace rosa { namespace deluxe { /// Contains some definitions used in the implementation of the *deluxe /// interface* to denote various roles and events /// /// \see \c rosa::deluxe::DeluxeContext /// /// \note Do not apply `using namespace` to this namespace as that may result in /// some identifiers in the original namespace being hidden by those of /// \c rosa::deluxe::atoms. namespace atoms { /// Value to be used as the *kind* of \c rosa::deluxe::DeluxeSensor. /// /// \see \c rosa::Unit::Kind constexpr AtomValue SensorKind = atom("dl_sensor"); /// Value to be used as the *kind* of \c rosa::deluxe::DeluxeAgent. /// /// \see \c rosa::Unit::Kind constexpr AtomValue AgentKind = atom("dl_agent"); /// Type alias denoting system trigger messages. using Trigger = AtomConstant; /// Type alias denoting messages from a slave. using Slave = AtomConstant; /// Type alias denoting messages from a master. -/// -/// \note This one is not used at the moment. using Master = AtomConstant; } // End namespace atoms } // End namespace deluxe } // End namespace rosa #endif // ROSA_DELUXE_DELUXEATOMS_HPP diff --git a/include/rosa/deluxe/DeluxeContext.hpp b/include/rosa/deluxe/DeluxeContext.hpp old mode 100755 new mode 100644 index 6ee0c03..a54a2da --- a/include/rosa/deluxe/DeluxeContext.hpp +++ b/include/rosa/deluxe/DeluxeContext.hpp @@ -1,336 +1,932 @@ //===-- rosa/deluxe/DeluxeContext.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeContext.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Public interface for the *deluxe interface* for working with agent /// systems. /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXECONTEXT_HPP #define ROSA_DELUXE_DELUXECONTEXT_HPP #include "rosa/deluxe/DeluxeSystem.hpp" #include "rosa/support/types.hpp" #include #include #include /// Local helper macro to log and return a /// \c rosa::deluxe::DeluxeContext::ErrorCode value. /// /// Creates a debug message with the stringified value and returns the value. /// /// \param Err \c rosa::deluxe::DeluxeContext::ErrorCode value to log and /// return #define DCRETERROR(Err) \ { \ LOG_DEBUG(#Err); \ return Err; \ } namespace rosa { namespace deluxe { /// Defines the *deluxe interface*. +/// +/// \todo The classes \c rosa::deluxe::DeluxeSensor and \c +/// rosa::deluxe::DeluxeAgent share some common features in relation to their +/// *slave* role in the *deluxe interface*. But their definitions are completely +/// independent. It could be investigated how to lift their common parts into a +/// new *deluxe slave* class, which would serve as base for both, to avoid code +/// duplication. class DeluxeContext { /// A system owned by \p this object. /// /// \note The reference is kept in a \c std::shared_ptr because of the member /// function \c rosa::deluxe::DeluxeContext::getSystem. std::shared_ptr System; /// References to all *sensors* and *agents* created by \p this object. std::set DeluxeUnits; public: /// Errors that may be resulted by some of the member functions of the class. enum struct ErrorCode { NoError, TypeMismatch, NotSensor, NotAgent, NotUnit, WrongPosition, AlreadyHasSlave, AlreadyHasMaster, AlreadyHasValueStream, UnsuitableExecutionPolicy }; /// Returns a new instance of \c rosa::deluxe::DeluxeContext. /// /// \param Name name of the underlying \c rosa::DeluxeSystem /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::deluxe::DeluxeContext with a new, empty \c rosa::DeluxeSystem static std::unique_ptr create(const std::string &Name) noexcept; private: /// Creates a new instance. /// /// \note Private constructor restricts instantiation to member functions of /// the class. /// /// \param Name name of the underlying \c rosa::MessagingSystem DeluxeContext(const std::string &Name) noexcept; public: /// Destroys \p this object. ~DeluxeContext(void) noexcept; /// Returns a reference for the underlying \c rosa::MessagingSystem. /// /// \note One cannot do much with a \c rosa::MessagingSystem currently, this /// is for future use. /// /// \return reference for the underlying \c rosa::MessagingSystem. std::weak_ptr getSystem(void) const noexcept; +private: + /// Creates a new *sensor* in the context of \p this object. + /// + /// The new *sensor* handles master-input by \p MF. + /// + /// \tparam MT type of master-input the new *sensor* handles + /// \tparam T type of data the new *sensor* operates on + /// + /// \note Instantiation fails if any of the type arguments \p MT and \p T + /// is not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c + /// rosa::deluxe::EmptyDeluxeTuple. + /// + /// \param Name name of the new *sensor* + /// \param MF function for the new *sensors* to process master-input + /// values with + /// \param F function for the new *sensor* to generate the next value with + /// during normal operation + /// + /// \note \p F is not used during simulation, in which case + /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to + /// register an alternative simulation data source with \c + /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may + /// safely keep relying on the default value of \p F as long as only + /// simulation of the system is to be done. + /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// + /// \return \c rosa::AgentHandle for the new *sensor* + template >::Value && + !std::is_same::value>> + AgentHandle createSensorImpl(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept; + +public: /// Creates a new *sensor* in the context of \p this object. /// + /// The new *sensor* does not receive master-input. + /// /// \tparam T type of data the new *sensor* operates on /// + /// \note Instantiation fails if type argument \p T is neither a built-in type + /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element. + /// /// \param Name name of the new *sensor* /// \param F function for the new *sensor* to generate the next value with /// during normal operation /// /// \note \p F is not used during simulation, in which case /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to register /// an alternative simulation data source with /// \c rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may /// safely keep relying on the default value of \p F as long as only /// simulation of the system is to be done. /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// + /// \return \c rosa::AgentHandle for the new *sensor* + template ::Value || + (IsDeluxeTuple::Value && + !std::is_same::value)>> + AgentHandle createSensor( + const std::string &Name, + std::function &&F = [](void) { return T(); }) noexcept; + + /// Creates a new *sensor* in the context of \p this object. + /// + /// The new *sensor* handles master-input by \p MF. + /// + /// \tparam MT type of master-input the new *sensor* handles + /// \tparam T type of data the new *sensor* operates on + /// + /// \note The type arguments \p MT and \p T must be either all built-in types + /// or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, \p T cannot be + /// \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions + /// do not hold. + /// + /// \param Name name of the new *sensor* + /// \param MF function for the new *sensors* to process master-input + /// values with \param F function for the new *sensor* to generate + /// the next value with during normal operation + /// + /// \note \p F is not used during simulation, in which case + /// \c rosa::deluxe::DeluxeContext::registerSensorValues is used to + /// register an alternative simulation data source with \c + /// rosa::deluxe::DeluxeSensor::registerSimulationDataSource. One may + /// safely keep relying on the default value of \p F as long as only + /// simulation of the system is to be done. + /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// /// \return \c rosa::AgentHandle for the new *sensor* - template - AgentHandle createSensor(const std::string &Name, - DeluxeSensor::D &&F = [](void) { - return T(); - }) noexcept; + template , BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same::value)>> + AgentHandle createSensor( + const std::string &Name, std::function)> &&MF, + std::function &&F = [](void) { return T(); }) noexcept; + +private: + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* receives master-input by \p MF and produces + /// master-output. + /// + /// \tparam MT type of master-input the new *agent* handles + /// \tparam T type of data the new *agent* outputs + /// \tparam Ts types of master-output the new *agent* produces + /// \tparam As types of inputs the new *agent* takes + /// + /// \note Instantiation fails if any of the type arguments \p MT, \p T, \p + /// Ts..., and \p As... is not an instance of \c rosa::deluxe::DeluxeTuple or + /// any of \p T and \p As... is \c rosa::deluxe::EmptyDeluxeTuple. + /// + /// \param Name name of the new *agent* + /// \param MF function for the new *agent* to process master-input + /// values with \param F function for the new *agent* to process + /// input values and generate output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template >::Value && + !std::is_same::value && + (true && ... && (!std::is_same::value))>> + AgentHandle createAgentImpl( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept; +public: /// Creates a new *agent* in the context of \p this object. /// + /// The new *agent* neither receives master-input nor produces + /// master-output. + /// /// \tparam T type of data the new *agent* outputs /// \tparam As types of inputs the new *agent* takes /// + /// \note The type arguments \p T and \p As... must be either all built-in + /// types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, none of + /// them can be \c rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if + /// these conditions do not hold. + /// /// \param Name name of the new *agent* /// \param F function for the new *agent* to process input values and /// generate output with /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// /// \return \c rosa::AgentHandle for the new *agent* - template - AgentHandle createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept; + template < + typename T, typename... As, + typename = std::enable_if_t< + TypeListSubsetOf, BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && (!std::is_same::value)))>> + AgentHandle + createAgent(const std::string &Name, + std::function(std::pair...)> &&F) noexcept; + + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* receives master-input by \p MF but does not + /// produce master-output. + /// + /// \tparam MT type of master-input the new *agent* handles + /// \tparam T type of data the new *agent* outputs + /// \tparam As types of inputs the new *agent* takes + /// + /// \note The type arguments \p MT, \p T, and \p As... must be either all + /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, + /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple. + /// Instantiation fails if these conditions do not hold. + /// + /// \param Name name of the new *agent* + /// \param MF function for the new *agent* to process master-input + /// values with + /// \param F function for the new *agent* to process input values and + /// generate output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template < + typename MT, typename T, typename... As, + typename = std::enable_if_t< + TypeListSubsetOf, BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && (!std::is_same::value)))>> + AgentHandle + createAgent(const std::string &Name, + std::function)> &&MF, + std::function(std::pair...)> &&F) noexcept; + + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* does not receive master-input but produces + /// master-output. + /// + /// \tparam T type of data the new *agent* outputs + /// \tparam Ts types of master-output the new *agent* produces + /// \tparam As types of inputs the new *agent* takes + /// + /// \note The type arguments \p T, \p Ts, and \p As... must be either all + /// built-in types or all instances of \c rosa::deluxe::DeluxeTuple. Moreover, + /// none of \p T and \p As... can be \c rosa::deluxe::EmptyDeluxeTuple. + /// Instantiation fails if these conditions do not hold. + /// + /// \param Name name of the new *agent* + /// \param F function for the new *agent* to process input values and + /// generate output with + /// + /// \note \p F does not produce master-output for a given position if the + /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not + /// possible to disable master-output at any position by using built-in types. + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template < + typename T, typename... Ts, typename... As, + typename = std::enable_if_t< + TypeListSubsetOf, BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && (!std::is_same::value)))>> + AgentHandle + createAgent(const std::string &Name, + std::function, Optional...>( + std::pair...)> &&F) noexcept; + + /// Creates a new *agent* in the context of \p this object. + /// + /// The new *agent* receives master-input by \p MF and produces + /// master-output. + /// + /// \tparam MT type of master-input the new *agent* handles + /// \tparam T type of data the new *agent* outputs + /// \tparam Ts types of master-output the new *agent* produces + /// \tparam As types of inputs the new *agent* takes + /// + /// \note The type arguments \p MT, \p T, \p Ts, and \p As... must be either + /// all built-in types or all instances of \c rosa::deluxe::DeluxeTuple. + /// Moreover, none of \p T and \p As... can be \c + /// rosa::deluxe::EmptyDeluxeTuple. Instantiation fails if these conditions + /// do not hold. + /// + /// \param Name name of the new *agent* + /// \param MF function for the new *agent* to process master-input + /// values with + /// \param F function for the new *agent* to process input values and + /// generate output with + /// + /// \note \p F does not produce master-output for a given position if the + /// corresponding type is \c rosa::deluxe::EmptyDeluxeTuple. It is not + /// possible to disable master-output at any position by using built-in types. + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. + /// + /// \return \c rosa::AgentHandle for the new *agent* + template < + typename MT, typename T, typename... Ts, typename... As, + typename = std::enable_if_t< + TypeListSubsetOf, + BuiltinTypes>::Value || + (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && (!std::is_same::value)))>> + AgentHandle createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept; /// Returns the current execution policy of the referred \p Unit /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note The referred \p Unit is either *sensor* or *agent*. /// /// \note The returned reference is valid only as long as \c /// rosa::deluxe::DeluxeContext::setExecutionPolicy() is not called with the /// *unit* referred by \p Unit and the *unit* is not destroyed. /// /// \param Unit the *unit* whose execution policy is to be obtained /// /// \return the \c rosa::deluxe::DeluxeExecutionPolicy from \p Unit if \p Unit /// is valid Optional getExecutionPolicy(AgentHandle Unit) const noexcept; /// Sets the current execution policy of the referred \p Unit to \p /// ExecutionPolicy. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note The referred \p Unit is either *sensor* or *agent*. /// /// \param Unit the *unit* whose execution policy is to be set /// \param ExecutionPolicy the new execution policy for \p Unit /// /// \return how successful setting \p ExecutionPolicy for \p Unit was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotUnit` | Referred \p Unit is not valid /// `UnsuitableExecutionPolicy` | \p ExecutionPolicy cannot handle \p Unit ErrorCode setExecutionPolicy( AgentHandle Unit, std::unique_ptr &&ExecutionPolicy) noexcept; - /// Connectes a *sensor* to an *agent* in the context of \p this object. + /// Connects a *sensor* to an *agent* in the context of \p this object. /// /// \param Agent the *agent* to connect to /// \param Pos the index of slot of \p Agent to connect \p Sensor to /// \param Sensor the *sensor* to connect /// \param Description optional textual description of the connection /// - /// \return how successfull connecting \p Sensor to \p Agent at slot index - /// \p Pos was + /// \return how successfull connecting \p Sensor to \p Agent at slot + /// index \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Agent is not \c rosa::deluxe::DeluxeAgent /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor /// `WrongPosition` | \p Pos is not a valid input position of \p Agent - /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other than the output type of \p Sensor + /// `TypeMismatch` | Expected input type at position \p Pos of \p Agent is other thanthe output type of \p Sensor or expected master-input of \p Sensor is other than master-output at position \p Pos of \p Agent if any /// `AlreadyHasSlave` | \p Agent at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Sensor already has a *master* registered ErrorCode connectSensor(AgentHandle Agent, const size_t Pos, AgentHandle Sensor, const std::string &Description = "") noexcept; /// Connectes two *agents* in the context of \p this object. /// /// \param Master the *agent* to connect to /// \param Pos the index of slot of \p Master to connect \p Slave to /// \param Slave the *agent* to connect /// \param Description optional textual description of the connection /// - /// \return how succesfull connecting \p Slave to \p Master at slot index - /// \p Pos was + /// \return how succesfull connecting \p Slave to \p Master at slot + /// index \p Pos was /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success /// `NotAgent` | Referred \p Master or \p Slave is not \c rosa::deluxe::DeluxeAgent /// `WrongPosition` | \p Pos is not a valid input position of \p Master - /// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave + /// `TypeMismatch` | Expected input type at position \p Pos of \p Master is other than the output type of \p Slave or expected master-input of \p Slave is other than master-output at position \p Pos of \p Master if any /// `AlreadyHasSlave` | \p Master at position \p Pos already has a *slave* registered /// `AlreadyHasMaster` | \p Slave already has a *master* registered ErrorCode connectAgents(AgentHandle Master, const size_t Pos, AgentHandle Slave, const std::string &Description = "") noexcept; - /// Initializes \c this object and others managed by \p this object for - /// setting up and performing simulation. + /// Initializes \c this object and others managed by \p this object + /// for setting up and performing simulation. /// /// \see \c rosa::deluxe::DeluxeContext::registerSensorValues, /// \c rosa::deluxe::DeluxeContext::simulate /// /// Need to clear simulation data sources from all the *sensors*. void initializeSimulation(void) noexcept; - /// Registers a stream providing values for a *sensor* during simulation. +public: + /// Registers a stream providing values for a *sensor* during + /// simulation. /// /// \tparam Iterator type of iterator providing values for \p Sensor - /// \tparam T type of values \p Sensor is operating on, always use default! + /// \tparam T type of values \p Sensor is operating on, always use + /// default! + /// + /// \note Instantiation fails if type argument \p T is neither a built-in type + /// nor an instance of \c rosa::deluxe::DeluxeTuple with at least one element. /// /// \param Sensor the *sensor* to register values for /// \param Start provides values for \p Sensor /// \param End denotes the end of stream of values - /// \param Default value to be used when input stream is depleted during - /// simulation + /// \param Default value to be used when input stream is depleted + /// during simulation /// /// \return how successful registering \p Source for \p Sensor /// /// \note The function may return the following /// \c rosa::deluxe::DeluxeContext::ErrorCode values: /// `ErrorCode` | Comment /// ----------- | ------- /// `NoError` | Success - /// `TypeMismatch` | \p Sensor generates values of a type other than \p T - /// `NotSensor` | Referred \p Sensor is not \c rosa::deluxe::DeluxeSensor - /// `AlreadyHasValueStream` | \p Sensor already has simulation data source set - template + /// `TypeMismatch` | \p Sensor generates values of a type other than + /// \p T `NotSensor` | Referred \p Sensor is not \c + /// rosa::deluxe::DeluxeSensor `AlreadyHasValueStream` | \p Sensor already has + /// simulation data source set + template < + typename Iterator, typename T = typename Iterator::value_type, + typename = std::enable_if_t::Value || + (IsDeluxeTuple::Value && + !std::is_same::value)>> ErrorCode registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default = {}) noexcept; /// Performs the system contained by \p this object. /// - /// The function performs \p NumCycles cycle of simulation. In each cycle, - /// all the *agents* and *sensors* registered in - /// \c rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for execution. + /// The function performs \p NumCycles cycle of simulation. In each + /// cycle, all the *agents* and *sensors* registered in \c + /// rosa::deluxe::DeluxeContext::DeluxeUnits are trigged for + /// execution. /// /// \param NumCycles number of cycles to perform /// - /// \pre All the *sensors* in the system contained by \p this object generate - /// their output from simulation data sources. + /// \pre All the *sensors* in the system contained by \p this object + /// generate their output from simulation data sources. void simulate(const size_t NumCycles) const noexcept; }; +/// Anonymous namespace with helper features for implementing +/// \c rosa::deluxe::DeluxeContext, consider it private. +namespace { + +/// Maps any type \p T to \c rosa::deluxe::EmptyDeluxeTuple. +template struct MapToEmptyDeluxeTuple { + using Type = EmptyDeluxeTuple; +}; + +/// Convenience template alias for \c MapToEmptyDeluxeTuple. template -AgentHandle DeluxeContext::createSensor(const std::string &Name, - DeluxeSensor::D &&F) noexcept { - AgentHandle H = System->createSensor(Name, std::move(F)); +using empty_deluxe_t = typename MapToEmptyDeluxeTuple::Type; + +/// Converts a \c std::tuple of \c rosa::Optional built-in types into a +/// corresponding \c std::tuple of \c rosa::Optional with each actual value +/// wrapped in \c rosa::deluxe::DeluxeTuple. +/// +/// \tparam Ts types of the values +/// \tparam S0 indices for accessing values in \p Values +/// +/// \param Values the \c std::tuple of \c rosa::Optional with built-in values +/// +/// \note The second argument provides indices statically as template arguments +/// \p S0..., so its actual value is ignored. +/// +/// \return a \c std::tuple of \c rosa::Optional corresponding to \p Values +/// with each actual value wrapped in \c rosa::deluxe::DeluxeTuple +/// +/// \pre Statically, all type arguments \p Ts... are built-in types and the +/// provided indices \p S0... match the length of \p Ts...: \code +/// TypeListSubsetOf, BuiltinTypes>::Value && +/// sizeof...(Ts) == sizeof...(S0) +/// \endcode +template +std::tuple>...> +wrapBuiltinInDeluxeTuple(const std::tuple...> &Values, + Seq) noexcept { + STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), + "not built-in types"); + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent type arguments"); + + return std::make_tuple(std::get(Values) + ? Optional>( + make_deluxe_tuple(*std::get(Values))) + : Optional>()...); +} + +} // End namespace + +template +AgentHandle +DeluxeContext::createSensorImpl(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept { + AgentHandle H = System->createSensor(Name, std::move(MF), std::move(F)); DeluxeUnits.emplace(H); return H; } -template -AgentHandle DeluxeContext::createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept { - AgentHandle H = System->createAgent(Name, std::move(F)); +template +AgentHandle DeluxeContext::createSensor(const std::string &Name, + std::function &&F) noexcept { + auto EmptyMF = std::function)>( + [](std::pair) {}); + + if constexpr (TypeListContains::Value) { + using OutputType = DeluxeTuple; + return createSensorImpl( + Name, std::move(EmptyMF), + std::function( + [F{std::move(F)}](void) { return OutputType(F()); })); + + } else if constexpr (IsDeluxeTuple::Value && + !std::is_same::value) { + return createSensorImpl(Name, std::move(EmptyMF), std::move(F)); + + } else { + ASSERT(false && "Unexpected type argument"); + } +} + +template +AgentHandle +DeluxeContext::createSensor(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept { + + if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { + using MasterInputType = DeluxeTuple; + using OutputType = DeluxeTuple; + return createSensorImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + MF({std::get<0>(Arg.first), Arg.second}); + }), + std::function( + [F{std::move(F)}](void) { return OutputType(F()); })); + + } else if constexpr (TypeListAllDeluxeTuple>::Value && + !std::is_same::value) { + return createSensorImpl(Name, std::move(MF), std::move(F)); + + } else { + ASSERT(false && "Unexpected type arguments"); + } +} + +template +AgentHandle DeluxeContext::createAgentImpl( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept { + AgentHandle H = System->createAgent(Name, std::move(MF), std::move(F)); DeluxeUnits.emplace(H); return H; } -template +template +AgentHandle DeluxeContext::createAgent( + const std::string &Name, + std::function(std::pair...)> &&F) noexcept { + + using NoMasterOutputType = std::tuple>...>; + auto EmptyMF = + std::function)>( + [](std::pair) { + return NoMasterOutputType(); + }); + + if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, std::move(EmptyMF), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return std::tuple_cat( + wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), + NoMasterOutputType()); + })); + + } else if constexpr (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && + (!std::is_same::value))) { + return createAgentImpl( + Name, std::move(EmptyMF), + std::function, Optional>...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Result = F(Args...); + return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } +} + +template +AgentHandle DeluxeContext::createAgent( + const std::string &Name, std::function)> &&MF, + std::function(std::pair...)> &&F) noexcept { + + using NoMasterOutputType = std::tuple>...>; + + if constexpr (TypeListSubsetOf, BuiltinTypes>::Value) { + using MasterInputType = DeluxeTuple; + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + MF({std::get<0>(Arg.first), Arg.second}); + return NoMasterOutputType(); + }), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return std::tuple_cat( + wrapBuiltinInDeluxeTuple(std::tuple(Result), seq_t<1>()), + NoMasterOutputType()); + })); + + } else if constexpr (TypeListAllDeluxeTuple>::Value && + !std::is_same::value && + (true && ... && + (!std::is_same::value))) { + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + MF(Arg); + return NoMasterOutputType(); + }), + std::function, Optional>...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Result = F(Args...); + return std::tuple_cat(std::tuple(Result), NoMasterOutputType()); + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } +} + +template +AgentHandle DeluxeContext::createAgent( + const std::string &Name, + std::function, Optional...>( + std::pair...)> &&F) noexcept { + + if constexpr (TypeListSubsetOf, + BuiltinTypes>::Value) { + using MasterOutputType = std::tuple>...>; + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, + std::function)>( + [](std::pair) { + return MasterOutputType(); + }), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return wrapBuiltinInDeluxeTuple(Result, + seq_t<1 + sizeof...(Ts)>()); + })); + + } else if constexpr (TypeListAllDeluxeTuple< + TypeList>::Value && + !std::is_same::value && + (true && ... && + (!std::is_same::value))) { + using MasterOutputType = std::tuple...>; + return createAgentImpl( + Name, + std::function)>( + [](std::pair) { + return MasterOutputType(); + }), + std::function, Optional...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Output = F(Args...); + return Output; + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } +} + +template +AgentHandle DeluxeContext::createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept { + + if constexpr (TypeListSubsetOf, + BuiltinTypes>::Value) { + using MasterInputType = DeluxeTuple; + using MasterOutputType = std::tuple>...>; + using OutputType = DeluxeTuple; + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + const auto Result = MF({std::get<0>(Arg.first), Arg.second}); + return wrapBuiltinInDeluxeTuple(Result, seq_t()); + }), + std::function< + std::tuple, Optional>...>( + std::pair, bool>...)>( + [F{std::move(F)}](std::pair, bool>... Args) { + const auto Result = F({std::get<0>(Args.first), Args.second}...); + return wrapBuiltinInDeluxeTuple(Result, + seq_t<1 + sizeof...(Ts)>()); + })); + + } else if constexpr (TypeListAllDeluxeTuple< + TypeList>::Value && + !std::is_same::value && + (true && ... && + (!std::is_same::value))) { + using MasterOutputType = std::tuple...>; + return createAgentImpl( + Name, + std::function)>( + [MF{std::move(MF)}](std::pair Arg) { + const auto Output = MF(Arg); + return Output; + }), + std::function, Optional...>( + std::pair...)>( + [F{std::move(F)}](std::pair... Args) { + const auto Output = F(Args...); + return Output; + })); + + } else { + ASSERT(false && "Unexpected type arguments"); + } +} + +template DeluxeContext::ErrorCode DeluxeContext::registerSensorValues(AgentHandle Sensor, Iterator &&Start, const Iterator &End, T Default) noexcept { // Get the type of values provided by \p Iterator. STATIC_ASSERT((std::is_same::value), "type mismatch"); // Make sure preconditions are met. if (!System->isDeluxeSensor(Sensor)) { DCRETERROR(ErrorCode::NotSensor); } auto S = System->getDeluxeSensor(Sensor); ASSERT(S); // Sanity check. - if (S->OutputType != TypeNumberOf::Value) { - DCRETERROR(ErrorCode::TypeMismatch); - } else if (S->simulationDataSourceIsSet()) { + if (S->simulationDataSourceIsSet()) { DCRETERROR(ErrorCode::AlreadyHasValueStream); } - // Register input stream. - // \note Need to capture parameters by value so having local copies. - S->registerSimulationDataSource( - DeluxeSensor::D([=](void) mutable noexcept { - if (Start != End) { - LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName - << "': " << *Start << '\n'; - return *Start++; - } else { - LOG_TRACE_STREAM << "Providing default value for sensor '" - << S->FullName << "': " << Default << '\n'; - return Default; - } - })); + if constexpr (TypeListContains::Value) { + if (S->OutputType != TypeToken::Value) { + DCRETERROR(ErrorCode::TypeMismatch); + } + + // Register input stream. + // \note Need to capture parameters by value so having local copies. + S->registerSimulationDataSource(std::function(void)>([= + ](void) mutable noexcept->DeluxeTuple { + if (Start != End) { + LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName + << "': " << *Start << '\n'; + return make_deluxe_tuple(*Start++); + } else { + LOG_TRACE_STREAM << "Providing default value for sensor '" + << S->FullName << "': " << Default << '\n'; + return make_deluxe_tuple(Default); + } + })); + + } else if constexpr (IsDeluxeTuple::Value && + !std::is_same::value) { + if (S->OutputType != T::TT) { + DCRETERROR(ErrorCode::TypeMismatch); + } + + // Register input stream. + // \note Need to capture parameters by value so having local copies. + S->registerSimulationDataSource( + std::function([=](void) mutable noexcept->T { + if (Start != End) { + LOG_TRACE_STREAM << "Reading next value for sensor '" << S->FullName + << "': " << *Start << '\n'; + return *Start++; + } else { + LOG_TRACE_STREAM << "Providing default value for sensor '" + << S->FullName << "': " << Default << '\n'; + return Default; + } + })); + + } else { + ASSERT(false && "Unexpected type argument"); + } + return ErrorCode::NoError; } } // End namespace deluxe } // End namespace rosa // Undef local macro if not used in the corresponding implementation. #ifndef ROSA_LIB_DELUXE_DELUXECONTEXT_CPP #undef DCRETERROR #endif #endif // ROSA_DELUXE_DELUXECONTEXT_HPP diff --git a/include/rosa/deluxe/DeluxeSensor.hpp b/include/rosa/deluxe/DeluxeSensor.hpp old mode 100755 new mode 100644 index 55e766c..95bb94d --- a/include/rosa/deluxe/DeluxeSensor.hpp +++ b/include/rosa/deluxe/DeluxeSensor.hpp @@ -1,315 +1,668 @@ //===-- rosa/deluxe/DeluxeSensor.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSensor.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::Agent for *sensor* role of the the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESENSOR_HPP #define ROSA_DELUXE_DELUXESENSOR_HPP #include "rosa/core/Agent.hpp" #include "rosa/deluxe/DeluxeAtoms.hpp" #include "rosa/deluxe/DeluxeExecutionPolicy.h" +#include "rosa/deluxe/DeluxeTuple.hpp" + +/// Local helper macros to deal with built-in types. +/// +///@{ + +/// Creates function name for member functions in \c rosa::deluxe::DeluxeSensor. +/// +/// \param N name suffix to use +#define DSMASTERHANDLERNAME(N) handleMaster_##N + +/// Defines member functions for handling messages from *master* in +/// \c rosa::deluxe::DeluxeSensor. +/// +/// \see \c DeluxeSensorMasterInputHandlers +/// +/// \note No pre- and post-conditions are validated directly by these functions, +/// they rather rely on \c rosa::deluxe::DeluxeSensor::saveMasterInput to do +/// that. +/// +/// \param T the type of input to handle +/// \param N name suffix for the function identifier +#define DSMASTERHANDLERDEFN(T, N) \ + void DSMASTERHANDLERNAME(N)(atoms::Master, id_t MasterId, token_size_t Pos, \ + T Value) noexcept { \ + saveMasterInput(MasterId, Pos, Value); \ + } + +/// Convenience macro for \c DSMASTERHANDLERDEFN with identical arguments. +/// +/// \see \c DSMASTERHANDLERDEFN +/// +/// This macro can be used instead of \c DSMASTERHANDLERDEFN if the actual value +/// of \p T can be used as a part of a valid identifier. +/// +/// \param T the type of input to handle +#define DSMASTERHANDLERDEF(T) DSMASTERHANDLERDEFN(T, T) + +/// Results in a \c THISMEMBER reference to a member function defined by +/// \c DSMASTERHANDLERDEFN. +/// +/// Used in the constructor of \c rosa::deluxe::DeluxeSensor to initialize super +/// class \c rosa::Agent with member function defined by \c DSMASTERHANDLERDEFN. +/// +/// \see \c DSMASTERHANDLERDEFN, \c THISMEMBER +/// +/// \param N name suffix for the function identifier +#define DSMASTERHANDLERREF(N) THISMEMBER(DSMASTERHANDLERNAME(N)) + +///@} namespace rosa { namespace deluxe { /// Specialization of \c rosa::Agent for *sensor* role of the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// -/// \invariant There is a compatible *execution policy* set +/// \invariant There is a compatible *execution policy* set; the actual value in +/// \c rosa::deluxe::DeluxeSensor::MasterInputNextPos is valid with respect to +/// the corresponding types. /// /// \see Definition of \c rosa::deluxe::DeluxeSensor::inv on the class invariant /// /// \note All member functions validate the class invariant as part of their /// precondition. Moreover, non-const functions validate the invariant before /// return as their postcondition. class DeluxeSensor : public Agent { /// Checks whether \p this object holds the class invariant. /// /// \see Invariant of the class \c rosa::deluxe::DeluxeSensor /// /// \return if \p this object holds the class invariant bool inv(void) const noexcept; /// The \c rosa::deluxe::DeluxeExecutionPolicy that controls the execution of /// \c this object. std::unique_ptr ExecutionPolicy; public: - /// Template alias for function objects used as data source for - /// \c rosa::deluxe::DeluxeSensor. + /// The type of values produced by \p this object. /// - /// \note The function used for \c D is to be \c noexcept. + /// That is the types of values \p this object sends to its *master* in a + /// \c rosa::deluxe::DeluxeTuple. /// - /// \tparam T type of data provided by the function - template using D = std::function; + /// \see \c rosa::deluxe::DeluxeSensor::master + const Token OutputType; - /// The type of values produced by \p this object. + /// The type of values \p this object processes from its *master*. /// - /// That is the type of values \p this object sends to its *master*. + /// That is the types of values \p this object receives from its *master* in a + /// \c rosa::deluxe::DeluxeTuple. /// /// \see \c rosa::deluxe::DeluxeSensor::master - const TypeNumber OutputType; + const Token MasterInputType; private: + /// Indicates which element of the master-input is expected from the *master*. + /// + /// The *master* is supposed to send one \c rosa::deluxe::DeluxeTuple value + /// element by element in their order of definition. This member field tells + /// the element at which position should be received next. + /// + /// \p this object is supposed to be triggered only when a complete + /// master-input has been received, that is the field should hold the value + /// `0`. + /// + /// \see \c rosa::deluxe::DeluxeSensor::handleTrigger + /// \c rosa::deluxe::DeluxeSensor::saveMasterInput + token_size_t MasterInputNextPos; + + /// Indicates whether the input value from the *master* has been changed since + /// the last trigger received from the system. + /// + /// The flag is reset to \c false upon handling a trigger and then set to \c + /// true by \c rosa::deluxe::DeluxeSensor::saveMasterInput when storig a new + /// input value in \c rosa::deluxe::DeluxeSensor::MasterInputValue. + bool MasterInputChanged; + + /// Stores the actual input value from *master*. + /// + /// \note The type of the stored value matches the types indicated by \c + /// rosa::deluxe::DeluxeSensor::MasterInputType. + const std::unique_ptr MasterInputValue; + /// Alias for function objects used as trigger handler for /// \c rosa::deluxe::DeluxeSensor. /// /// \note The function used for \c H is to be \c noexcept. /// /// \see \c DeluxeSensorTriggerHandlers using H = std::function; - /// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of rosa::deluxe::DeluxeSensor + /// \defgroup DeluxeSensorTriggerHandlers Trigger handlers of + /// rosa::deluxe::DeluxeSensor /// /// \brief Trigger handler functions of \c rosa::deluxe::DeluxeSensor /// - /// The actual data source functions are captured in a lambda expression that - /// is in turn wrapped in a \c std::function object. The lambda expression - /// calls the data source function to obtain the next sensory value and sends - /// it to *master* by calling \c rosa::deluxe::DeluxeSensor::sendToMaster. The - /// function \c rosa::deluxe::DeluxeSensor::handleTrigger needs only to call - /// the proper function object. + /// The actual data source functions and master-input processing function are + /// captured in lambda expressions that are in turn wrapped in \c + /// std::function objects. The lambda expression calls a processing function, + /// either to handle master-input or obtain the next sensory value from data + /// source. The next sensory value is sent it to *master* by calling \c + /// rosa::deluxe::DeluxeSensor::sendToMaster. Also, the flag \c + /// rosa::deluxe::DeluxeSensor::MasterInputChanged is reset when the current + /// value is passed to the master-input processing function. The function \c + /// rosa::deluxe::DeluxeSensor::handleTrigger needs only to call the proper + /// function object. + + /// Processes master-input. + /// + /// \ingroup DeluxeSensorTriggerHandlers + /// + /// The function is called upon the sensor is trigged by the system. + const H MFP; - /// Handles trigger during normal execution. + /// Produces the next sensory value during normal execution. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is used during normal execution. During simulation, the /// simulation environment sets \c rosa::deluxe::DeluxeSensor::SFP, which is /// used instead of \c rosa::deluxe::DeluxeSensor::FP. const H FP; - /// Handles trigger during simulation. + /// Produces the next sensory value during simulation. /// /// \ingroup DeluxeSensorTriggerHandlers /// /// The function is empty by default. The simulation environment sets it to be /// used during simulation. H SFP; /// The *master* to send values to. /// /// \note *Masters* are set dynamically, hence it is possible that a /// \c rosa::deluxe::DeluxeSensor instance does not have any *master* at a /// given moment. Optional Master; + /// Tells the unique identifier of the *master* of \p this object, if any + /// registered. + /// + /// \return the unique identifier of the *master* + /// + /// \pre A *master* is registered for \p this object: \code + /// Master + /// \endcode + id_t masterId(void) const noexcept; + + /// Wraps a master-input processing function into a trigger handler. + /// + /// \see \c rosa::deluxe::DeluxeSensor::MFP and \c DeluxeSensorTriggerHandlers + /// + /// \tparam Ts types of elements of master-input processed by \p MF + /// \tparam S0 indices for accessing master-input values + /// + /// \param MF function that processes master-input + /// + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \note A master-input type of \c rosa::deluxe::EmptyDeluxeTuple indicates + /// that \p this object does not receive master-input, \p MF is never called + /// if \p Ts is empty. + /// + /// \return trigger handler function based on \p MF + /// + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Ts... match \c + /// rosa::deluxe::DeluxeSensor::MasterInputType: \code + /// MasterInputType == DeluxeTuple::TT + /// \endcode + template + H triggerHandlerFromProcessingFunction( + std::function, bool>)> &&MF, + Seq) noexcept; + /// Wraps a data source function into a trigger handler. /// - /// \see \c DeluxeSensorTriggerHandlers + /// \see \c rosa::deluxe::DeluxeSensor::FP, \c + /// rosa::deluxe::DeluxeSensor::SFP, and \c DeluxeSensorTriggerHandlers /// /// \tparam T type of data provided by \p F /// /// \param F function to generate value with /// \param inSimulation if F is a data source for Simulation /// - /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \return trigger handler function based on \p F + /// + /// \pre Statically, the type agument \p T is an instance of \c + /// rosa::deluxe::DeluxeTuple: \code + /// IsDeluxeTuple::Value + /// \endcode Dynamically, \p T matches \c + /// rosa::deluxe::DeluxeSensor::OutputType: \code + /// OutputType == T::TT /// \endcode template - H triggerHandlerFromDataSource(D &&F, bool inSimulation) noexcept; + H triggerHandlerFromDataSource(std::function &&F, + bool inSimulation) noexcept; public: /// Creates a new instance. /// /// The constructor instantiates the base-class with functions to handle /// messages as defined for the *deluxe interface*. /// - /// \todo Enforce F does not potentially throw exception. + /// \todo Enforce \p F and \p MF do not potentially throw exception. /// + /// \tparam MT type of master-input handled by \p MF /// \tparam T type of data to operate on /// + /// \note Instantiation fails if any of the type arguments \p MT and \p T is + /// not an instance of \c rosa::deluxe::DeluxeTuple or \p T is \c + /// rosa::deluxe::EmptyDeluxeTuple. + /// + /// \note If \p MT is \c rosa::deluxe::EmptyDeluxeTuple, the constructed + /// object does not receive master-input. + /// /// \param Kind kind of the new \c rosa::Unit instance /// \param Id unique identifier of the new \c rosa::Unit instance /// \param Name name of the new \c rosa::Unit instance /// \param S \c rosa::MessagingSystem owning the new instance + /// \param MF function to process master-input values with /// \param F function to generate the next value with during normal operation /// - /// \pre Statically, \p T is a built-in type:\code - /// TypeListContains::Value + /// \pre Statically, \p MT and \p T are instances of \c + /// rosa::deluxe::DeluxeTuple and \p T contains at least one element:\code + /// TypeListAllDeluxeTuple>::Value && T::Length > 0 /// \endcode /// Dynamically, the instance is created as of kind /// \c rosa::deluxe::atoms::SensorKind: /// \code /// Kind == rosa::deluxe::atoms::SensorKind /// \endcode - template ::Value>> + /// + /// \see \c rosa::deluxe::DeluxeTuple + template < + typename MT, typename T, + typename = std::enable_if_t< + TypeListAllDeluxeTuple>::Value && (T::Length > 0)>> DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, - MessagingSystem &S, D &&F) noexcept; + MessagingSystem &S, + std::function)> &&MF, + std::function &&F) noexcept; /// Destroys \p this object. ~DeluxeSensor(void) noexcept; /// Returns the current execution policy of \p this object. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note The returned reference is valid only as long as \c /// rosa::deluxe::DeluxeSensor::setExecutionPolicy() is not called and \p this /// object is not destroyed. /// /// \return \c rosa::deluxe::DeluxeSensor::ExecutionPolicy const DeluxeExecutionPolicy &executionPolicy(void) const noexcept; /// Sets the current execution policy of \p this object to \p EP. /// /// \see \c rosa::deluxe::DeluxeExecutionPolicy /// /// \note \p EP is set only if it can handle \p this object. /// /// \param EP the new execution policy for \p this object /// /// \return if \p EP was successfully set for \p this object. bool setExecutionPolicy(std::unique_ptr &&EP) noexcept; /// The *master* of \p this object, if any. /// /// \see \c rosa::deluxe::DeluxeSensor::registerMaster /// /// \return the *master* registered for \p this object Optional master(void) const noexcept; /// Registers a *master* for \p this object. /// /// The new *master* is registered by overwriting the reference to any /// already registered *master*. One can clear the registered reference by /// passing an *empty* \c rosa::Optional object as actual argument. /// /// \note The role of the referred *master* is validated by checking its /// *kind*. /// + /// \note Any call to \c rosa::deluxe::DeluxeSensor::registerMaster should be + /// paired with a corresponding call of \c + /// rosa::deluxe::DeluxeAgent::registerSlave, which validates that + /// input/output types of master and slave matches. + /// /// \param _Master the *master* to register /// /// \pre \p Master is empty or of kind \c rosa::deluxe::atoms::AgentKind: /// \code /// !_Master || unwrapAgent(*_Master).Kind == rosa::deluxe::atoms::AgentKind /// \endcode void registerMaster(const Optional _Master) noexcept; /// Clears the simulation trigger handler of \p this object. /// /// The function assigns \c rosa::deluxe::DeluxeSensor::SFP with \c nullptr. void clearSimulationDataSource(void) noexcept; /// Tells whether a simulation trigger handler is set for \p this object. /// /// The function returns whether \c rosa::deluxe::DeluxeSensor::SFP is not /// \c nullptr. /// /// \return if a simulation trigger handler is set for \p this object. bool simulationDataSourceIsSet(void) const noexcept; /// Registers a simulation data source for \p this object. /// /// A new simulation trigger handler wrapping \p SF is stored in /// \c rosa::deluxe::DeluxeSensor::SFP by overwriting any already registered /// simulation data source. /// /// \todo Enforce SF does not potentially throw exception. /// - /// \tparam T type of data provided by \p SF + /// \tparam Ts types of elements of values provided by \p SF /// /// \param SF function to generate value with /// - /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \pre \p Ts... match \c rosa::deluxe::DeluxeSensor::OutputType: \code + /// OutputType == TypeToken::Value /// \endcode - template void registerSimulationDataSource(D &&SF) noexcept; + template + void registerSimulationDataSource( + std::function(void)> &&SF) noexcept; private: /// Sends a value to the *master* of \p this object. /// /// \p Value is getting sent to \c rosa::deluxe::DeluxeSensor::Master if it /// contains a valid handle for a \c rosa::deluxe::DeluxeAgent. The function /// does nothing otherwise. /// - /// \tparam T type of the value to send + /// The elements from \p Value are sent one by one in separate messages to the + /// *master*. + /// + /// \tparam Ts types of the elements in \p Value + /// \tparam S0 indices for accessing elements of \p Value /// /// \param Value value to send /// - /// \pre \p T matches \c rosa::deluxe::DeluxeSensor::OutputType: \code - /// OutputType == TypeNumberOf::Value + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \pre Statically, the indices match the elements: \code + /// sizeof...(Ts) == sizeof...(S0) + /// \endcode Dynamically, \p Ts match \c + /// rosa::deluxe::DeluxeSensor::OutputType: \code + /// OutputType == TypeToken::Value /// \endcode - template void sendToMaster(const T &Value) noexcept; + template + void sendToMaster(const DeluxeTuple &Value, Seq) noexcept; - /// Generates the next sensory value upon trigger from the system. + /// Handles master-input and generates the next sensory value upon trigger + /// from the system. /// - /// Executes \c rosa::deluxe::DeluxeSensor::FP or - /// \c rosa::deluxe::DeluxeSensor::SFP if set. + /// Executes \c rosa::deluxe::DeluxeSensor::MFP for processing master-input + /// and data generating function \c rosa::deluxe::DeluxeSensor::FP or \c + /// rosa::deluxe::DeluxeSensor::SFP if set. /// /// \note The only argument is a \c rosa::AtomConstant, hence its actual /// value is ignored. + /// + /// \pre Master-input is supposed to be completely received upon triggering: + /// \code + /// MasterInputNextPos == 0 + /// \endcode void handleTrigger(atoms::Trigger) noexcept; + + /// Stores a new input value from the *master*. + /// + /// The function stores \p Value at position \p Pos in \c + /// rosa::deluxe::DeluxeSensor::MasterInputValue and also sets the + /// flag \c rosa::deluxe::DeluxeSensor::MasterInputChanged. The function also + /// takes care of checking and updating \c + /// rosa::deluxe::DeluxeSensor::MasterInputNextPos: increments its value and + /// resets it to `0` when the last element is received. + /// + /// \note Utilized by member functions of group \c + /// DeluxeSensorMasterInputHandlers. + /// + /// \tparam T type of input to store + /// + /// \param Id unique identifier of the *master* + /// \param Pos position of the value in the \c rosa::deluxe::DeluxeTuple + /// \param Value the input value to store + /// + /// \pre The *master* with \p Id is registered, \p Pos is the expected + /// position of master-input, and the input from the *master* at position \p + /// Pos is expected to be of type \p T: \code + /// Master && masterId() == Id && Pos == MasterInputNextPos && + /// typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value + /// \endcode + template + void saveMasterInput(id_t Id, token_size_t Pos, T Value) noexcept; + + /// \defgroup DeluxeSensorMasterInputHandlers Master-input handlers of + /// rosa::deluxe::DeluxeSensor + /// + /// Definition of member functions handling messages from the *master* with + /// different types of input + /// + /// A *slave* generally needs to be prepared to deal with values of any + /// built-in type to handle messages from its *master*. Each type requires a + /// separate message handler, which are implemented by these functions. The + /// functions instantiate \c rosa::deluxe::DeluxeSensor::saveMasterInput with + /// the proper template argument and pass the content of the message on for + /// processing. + /// + /// \note The member functions in this group are defined by \c + /// DSMASTERHANDLERDEF. + /// + /// \note Keep these definitions in sync with \c rosa::BuiltinTypes. + /// + ///@{ + + DSMASTERHANDLERDEF(AtomValue) + DSMASTERHANDLERDEF(int16_t) + DSMASTERHANDLERDEF(int32_t) + DSMASTERHANDLERDEF(int64_t) + DSMASTERHANDLERDEF(int8_t) + DSMASTERHANDLERDEFN(long double, long_double) + DSMASTERHANDLERDEFN(std::string, std__string) + DSMASTERHANDLERDEF(uint16_t) + DSMASTERHANDLERDEF(uint32_t) + DSMASTERHANDLERDEF(uint64_t) + DSMASTERHANDLERDEF(uint8_t) + DSMASTERHANDLERDEF(unit_t) + DSMASTERHANDLERDEF(bool) + DSMASTERHANDLERDEF(double) + DSMASTERHANDLERDEF(float) + + /// @} }; +template +DeluxeSensor::H DeluxeSensor::triggerHandlerFromProcessingFunction( + std::function, bool>)> &&MF, + Seq) noexcept { + using MT = DeluxeTuple; + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + ASSERT(MasterInputType == MT::TT); + + // NOTE: Clang 6 warns about unused lambda captures; we suppress that + // warning (those variables need to be captured). +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-lambda-capture" +#endif // defined __clang__ + return [ this, MF ](void) noexcept { + // Do not do anything for master-input type \c + // rosa::deluxe::EmptyDeluxeTuple. + if constexpr (!std::is_same::value) { + LOG_TRACE_STREAM << "DeluxeSensor " << FullName + << " handles master-input." << std::endl; + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + const auto MasterInputArg = std::make_pair( + // Get all elements of the tuple in a fold expression. + DeluxeTuple(*static_cast( + MasterInputValue->pointerTo(static_cast(S0)))...), + MasterInputChanged); + MasterInputChanged = false; + MF(MasterInputArg); + } + }; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // defined __clang__ +} + template DeluxeSensor::H -DeluxeSensor::triggerHandlerFromDataSource(D &&F, +DeluxeSensor::triggerHandlerFromDataSource(std::function &&F, bool inSimulation) noexcept { - ASSERT(OutputType == TypeNumberOf::Value); + STATIC_ASSERT(IsDeluxeTuple::Value, "not tuple type argument"); + ASSERT(OutputType == T::TT); return [ this, F, inSimulation ](void) noexcept { // Get value and send it to master only if \p ExecutionPolicy allows it. if (ExecutionPolicy->shouldProcess({})) { - sendToMaster(F()); - } else if (inSimulation) { - // But read input value in Simulation anyway as input values are provided - // for the highest execution frequency for simulation - F(); + LOG_TRACE_STREAM << "DeluxeSensor " << Name << " obtains next value." + << std::endl; + sendToMaster(F(), seq_t()); + } else { + LOG_TRACE_STREAM << "DeluxeSensor " << Name << " skips next value." + << std::endl; + if (inSimulation) { + // But read input value in Simulation anyway as input values are + // provided for the highest execution frequency for simulation + F(); + } } }; } -template +template DeluxeSensor::DeluxeSensor(const AtomValue Kind, const id_t Id, const std::string &Name, MessagingSystem &S, - D &&F) noexcept - : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger)), - ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), - OutputType(TypeNumberOf::Value), + std::function)> &&MF, + std::function &&F) noexcept + : Agent(Kind, Id, Name, S, THISMEMBER(handleTrigger), + DSMASTERHANDLERREF(AtomValue), DSMASTERHANDLERREF(int16_t), + DSMASTERHANDLERREF(int32_t), DSMASTERHANDLERREF(int64_t), + DSMASTERHANDLERREF(int8_t), DSMASTERHANDLERREF(long_double), + DSMASTERHANDLERREF(std__string), DSMASTERHANDLERREF(uint16_t), + DSMASTERHANDLERREF(uint32_t), DSMASTERHANDLERREF(uint64_t), + DSMASTERHANDLERREF(uint8_t), DSMASTERHANDLERREF(unit_t), + DSMASTERHANDLERREF(bool), DSMASTERHANDLERREF(double), + DSMASTERHANDLERREF(float)), + ExecutionPolicy(DeluxeExecutionPolicy::decimation(1)), OutputType(T::TT), + MasterInputType(MT::TT), MasterInputChanged(false), + MasterInputValue(new typename TokenizedStorageForTypeList< + typename UnwrapDeluxeTuple::Type>::Type()), + MFP(triggerHandlerFromProcessingFunction(std::move(MF), + seq_t())), FP(triggerHandlerFromDataSource(std::move(F), false)), SFP(nullptr) { ASSERT(Kind == atoms::SensorKind); - LOG_TRACE("DeluxeSensor is created."); + LOG_TRACE_STREAM << "DeluxeSensor " << FullName << " is created." + << std::endl; ASSERT(inv()); } -template -void DeluxeSensor::registerSimulationDataSource(D &&SF) noexcept { - ASSERT(inv() && OutputType == TypeNumberOf::Value); +template +void DeluxeSensor::registerSimulationDataSource( + std::function(void)> &&SF) noexcept { + ASSERT(OutputType == TypeToken::Value); SFP = triggerHandlerFromDataSource(std::move(SF), true); ASSERT(inv()); } -template -void DeluxeSensor::sendToMaster(const T &Value) noexcept { - ASSERT(inv() && OutputType == TypeNumberOf::Value); +template +void DeluxeSensor::sendToMaster(const DeluxeTuple &Value, + Seq) noexcept { + STATIC_ASSERT(sizeof...(Ts) == sizeof...(S0), "inconsistent arguments"); + ASSERT(OutputType == TypeToken::Value); + + // The assert must hold if \p this object was successfuuly constructed. + ASSERT((true && ... && + (static_cast(static_cast(S0)) == S0))); + // Create a static constant array for these indices to be available as lvalue + // references when creating messages below. \c S0... when used directly in a + // fold expression is a temporary value, which would result in \c + // rosa::Message instances being created with rvalue references. Further, all + // other values would to copied into a temporary variable for making them + /// available as rvalue references (they are constant lvalue references here). + static constexpr std::array Indices{{S0...}}; + + LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id + << ") sends to master(" + << static_cast(Master && *Master) << "): " << Value + << std::endl; // There is a handle and the referred *master* is in a valid state. if (Master && *Master) { - Master->sendMessage(Message::create(atoms::Slave::Value, Id, Value)); + // Handle each element of the tuple in a fold expression. + (Master->sendMessage(Message::create(atoms::Slave::Value, Id, Indices[S0], + std::get(Value))), + ...); } ASSERT(inv()); } +template +void DeluxeSensor::saveMasterInput(id_t Id, token_size_t Pos, + T Value) noexcept { + ASSERT(Master && masterId() == Id && Pos == MasterInputNextPos && + typeAtPositionOfToken(MasterInputType, Pos) == TypeNumberOf::Value); + + LOG_TRACE_STREAM << "DeluxeSensor " << FullName << "(" << Id + << ") saves value from master: (" << static_cast(Pos) + << ") " << Value << std::endl; + + // Save value. + *static_cast(MasterInputValue->pointerTo(Pos)) = Value; + + // Update position of next value. + if (++MasterInputNextPos == lengthOfToken(MasterInputType)) { + MasterInputNextPos = 0; + } + + // Set flag. + MasterInputChanged = true; +} + } // End namespace deluxe } // End namespace rosa +#undef DSMASTERHANDLEREF +#undef DSMASTERHANDLEDEF +#undef DSMASTERHANDLEDEFN +#undef DSMASTERHANDLENAME + #endif // ROSA_DELUXE_DELUXESENSOR_HPP diff --git a/include/rosa/deluxe/DeluxeSystem.hpp b/include/rosa/deluxe/DeluxeSystem.hpp old mode 100755 new mode 100644 index 7e08e82..bc51cdf --- a/include/rosa/deluxe/DeluxeSystem.hpp +++ b/include/rosa/deluxe/DeluxeSystem.hpp @@ -1,211 +1,240 @@ //===-- rosa/deluxe/DeluxeSystem.hpp ----------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/deluxe/DeluxeSystem.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// /// \date 2017-2019 /// /// \brief Specialization of \c rosa::MessagingSystem for the *deluxe /// interface*. /// /// \see \c rosa::deluxe::DeluxeContext /// //===----------------------------------------------------------------------===// #ifndef ROSA_DELUXE_DELUXESYSTEM_HPP #define ROSA_DELUXE_DELUXESYSTEM_HPP #include "rosa/core/MessagingSystem.hpp" #include "rosa/deluxe/DeluxeAgent.hpp" #include "rosa/deluxe/DeluxeSensor.hpp" namespace rosa { namespace deluxe { /// Implements and extends the \c rosa::MessagingSystem interface to be /// used by \c rosa::deluxe::DeluxeContext. /// /// The class is a specialization of \c rosa::MessagingSystem, where objects /// of two specialized subtypes of \c rosa::Agent, \c rosa::deluxe::DeluxeSensor /// and \c rosa::deluxe::DeluxeAgent, constitute a system. The class extends the /// \c rosa::MessagingSystem interface with features required to implement the /// *deluxe interface*. /// /// \see rosa::deluxe::DeluxeContext class DeluxeSystem : public MessagingSystem { friend class DeluxeContext; friend class DeluxeExecutionPolicy; public: /// Returns an object implementing the \c rosa::deluxe::DeluxeSystem /// interface. /// /// \param Name name of the new instance /// /// \return \c std::unique_ptr for the new instance of /// \c rosa::DeluxeSystem static std::unique_ptr createSystem(const std::string &Name) noexcept; protected: /// Creates a new instance. /// /// \note Protected constructor restricts instantiation for subclasses. DeluxeSystem(void) noexcept = default; public: /// Creates a \c rosa::deluxe::DeluxeSensor instance owned by \p this object /// and returns a \p rosa::AgentHandle for it. /// + /// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeSensor + /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeSensor operates on /// + /// \note Type arguments \p MT and \p T must be instances of \c + /// rosa::deluxe::DeluxeTuple. + /// /// \param Name name of the new \c rosa::deluxe::DeluxeSensor + /// \param MF function to process master-input values /// \param F function to generate the next value with during normal operation /// + /// \see \c rosa::deluxe::DeluxeSensor::DeluxeSensor. + /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeSensor - template + template AgentHandle createSensor(const std::string &Name, - DeluxeSensor::D &&F) noexcept; + std::function)> &&MF, + std::function &&F) noexcept; /// Creates a \c rosa::deluxe::DeluxeAgent instance owned by \p this object /// and returns a \c rosa::AgentHandle for it. /// + /// \tparam MT type of master-input the new \c rosa::deluxe::DeluxeAgent + /// receives /// \tparam T type of data the new \c rosa::deluxe::DeluxeAgent outputs + /// \tparam Ts types of master-output the new \c rosa::deluxe::DeluxeAgent + /// produces /// \tparam As types of inputs the new \c rosa::deluxe::DeluxeAgent takes /// + /// \note Type arguments \p MT, \p T, \p Ts..., and \p As... must be + /// instances of \c rosa::deluxe::DeluxeTuple. + /// /// \param Name name of the new \c rosa::deluxe::DeluxeAgent + /// \param MF function for the new \c rosa::deluxe::DeluxeAgent to process + /// master-input values and generate master-output with /// \param F function for the new \c rosa::deluxe::DeluxeAgent to process - /// input values and generate output with + /// input values and generate output and master-output with + /// + /// \see \c rosa::deluxe::DeluxeAgent::DeluxeAgent. /// /// \return \c rosa::AgentHandle for new \c rosa::deluxe::DeluxeAgent - template - AgentHandle createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept; + template + AgentHandle createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept; protected: /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeSensor owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeSensor owned by /// \p this object virtual bool isDeluxeSensor(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeSensor reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeSensor object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeSensor and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeSensor if /// \p H refers to an object which is of that type and is owned by \p this /// object Optional getDeluxeSensor(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeSensor reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeSensor object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeSensor and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeSensor /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeSensor /// from /// /// \return reference to \c rosa::deluxe::DeluxeSensor if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeSensor(AgentHandle &H) const noexcept; /// Tells whether a \c rosa::AgentHandle refers to a /// \c rosa::deluxe::DeluxeAgent owned by \p this object. /// /// \param H \c rosa::AgentHandle to check /// /// \return whether \p H refers to a \c rosa::deluxe::DeluxeAgent owned by /// \p this object virtual bool isDeluxeAgent(const AgentHandle &H) const noexcept = 0; /// Extracts a const qualified \c rosa::deluxe::DeluxeAgent reference from a /// const qualified \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a const /// qualified reference to a \c rosa::deluxe::DeluxeAgent object extracted /// from a const qualified \c rosa::AgentHandle instance if the referred /// object is of type \c rosa::deluxeDeluxeAgent and owned by \p this object. /// The returned \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return const qualified reference to \c rosa::deluxe::DeluxeAgent if \p H /// refers to an object which is of that type and is owned by \p this object Optional getDeluxeAgent(const AgentHandle &H) const noexcept; /// Extracts a \c rosa::deluxe::DeluxeAgent reference from a /// \c rosa::AgentHandle if possible. /// /// The function returns a \c rosa::Optional object containing a reference to /// a \c rosa::deluxe::DeluxeAgent object extracted from a /// \c rosa::AgentHandle instance if the referred object is of type /// \c rosa::deluxeDeluxeAgent and owned by \p this object. The returned /// \c rosa::Optional object is empty otherwise. /// /// \see rosa::deluxe::DeluxeSystem::isDeluxeAgent /// /// \param H \c rosa::AgentHandle to extract a \c rosa::deluxe::DeluxeAgent /// from /// /// \return reference to \c rosa::deluxe::DeluxeAgent if \p H refers to an /// object which is of that type and is owned by \p this object Optional getDeluxeAgent(AgentHandle &H) const noexcept; }; -template -AgentHandle DeluxeSystem::createSensor(const std::string &Name, - DeluxeSensor::D &&F) noexcept { +template +AgentHandle +DeluxeSystem::createSensor(const std::string &Name, + std::function)> &&MF, + std::function &&F) noexcept { Agent &DS = createUnit( [&](const id_t Id, MessagingSystem &S) { - return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(F)); + return new DeluxeSensor(atoms::SensorKind, Id, Name, S, std::move(MF), + std::move(F)); }); return {DS}; } -template -AgentHandle DeluxeSystem::createAgent(const std::string &Name, - DeluxeAgent::D &&F) noexcept { - +template +AgentHandle DeluxeSystem::createAgent( + const std::string &Name, + std::function...>(std::pair)> &&MF, + std::function, Optional...>( + std::pair...)> &&F) noexcept { Agent &DA = createUnit( [&](const id_t Id, DeluxeSystem &S) { - return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(F)); + return new DeluxeAgent(atoms::AgentKind, Id, Name, S, std::move(MF), + std::move(F)); }); return {DA}; } } // End namespace deluxe } // End namespace rosa #endif // ROSA_DELUXE_DELUXESYSTEM_HPP diff --git a/include/rosa/deluxe/DeluxeTuple.hpp b/include/rosa/deluxe/DeluxeTuple.hpp new file mode 100644 index 0000000..e57d29b --- /dev/null +++ b/include/rosa/deluxe/DeluxeTuple.hpp @@ -0,0 +1,359 @@ +//===-- rosa/deluxe/DeluxeTuple.hpp -----------------------------*- C++ -*-===// +// +// The RoSA Framework +// +//===----------------------------------------------------------------------===// +/// +/// \file rosa/deluxe/DeluxeTuple.hpp +/// +/// \author David Juhasz (david.juhasz@tuwien.ac.at) +/// +/// \date 2019 +/// +/// \brief Facilities for handling multiple input/output values for connections +/// in the *deluxe interface*. +/// +/// \see \c rosa::deluxe::DeluxeContext +/// +//===----------------------------------------------------------------------===// + +#ifndef ROSA_DELUXE_DELUXETUPLE_HPP +#define ROSA_DELUXE_DELUXETUPLE_HPP + +#include "rosa/support/sequence.hpp" +#include "rosa/support/type_token.hpp" +#include +#include + +namespace rosa { +namespace deluxe { + +/// A tuple to manage multiple input/output values in the *deluxe interface*. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \note The template may be instantiated only with built-in types and the +/// number of those type may not exceed the capacity of a \c rosa::Token. +template +struct DeluxeTuple : public std::tuple { + // Statically enforce that the class template is instantiated only with + // built-in types. + STATIC_ASSERT((TypeListSubsetOf, BuiltinTypes>::Value), + "not built-in types"); + // Statically enforce that the class template is instantiated with not too + // many types. + // \note Instantiation would fail on \c rosa::deluxe::DeluxeTuple::TT if there + // are too any types; this assertion is for more readable error reporting. + STATIC_ASSERT(sizeof...(Ts) <= token::MaxTokenizableListSize, + "Too many types"); + + /// How many elements the instance has. + static constexpr token_size_t Length = sizeof...(Ts); + + /// What types the class contains. + /// + /// Type information encoded as \c rosa::Token. + static constexpr Token TT = TypeToken::Value; + + /// Default constructor, zero-initializes elements. + DeluxeTuple(void) = default; + + /// Constructor, initializes the underlying \c std::tuple with lvalue + /// references. + /// + /// \param Args value references to the values to store + DeluxeTuple(const std::decay_t &... Args) : std::tuple(Args...) {} + + /// Constructor, initializes the underlying \c std::tuple with rvalue + /// references. + /// + /// \param Args rvalue references to the values to store + DeluxeTuple(std::decay_t &&... Args) + : std::tuple(std::move(Args)...) {} + + /// Default copy-constructor. + DeluxeTuple(const DeluxeTuple &) = default; + + /// Default move-constructor. + DeluxeTuple(DeluxeTuple &&) = default; + + /// Default copy-assignment. + DeluxeTuple &operator=(const DeluxeTuple &) = default; + + /// Default move-assignment. + DeluxeTuple &operator=(DeluxeTuple &&) = default; + +private: + /// Dumps \p this object to a given \c std::ostream. + /// + /// \note Provides implementation for \c rosa::deluxe::DeluxeTuple::dump. + /// + /// \tparam S0 Indices for accessing elements. + /// + /// \param [in,out] OS output stream to dump to + /// + /// \note The second argument provides indices statically as template + /// arguments \p S0..., so its actual value is ignored. + /// + /// \pre Statically, \p S0... matches number of types \p this object was + /// created: \code + /// sizeof...(S0) == sizeof...(Ts) + /// \endcode + template + void dump(std::ostream &OS, Seq) const noexcept; + +public: + /// Dumps \p this object to a given \c std::ostream. + /// + /// \param [in,out] OS output stream to dump to + void dump(std::ostream &OS) const noexcept; +}; + +template +template +void DeluxeTuple::dump(std::ostream &OS, Seq) const noexcept { + STATIC_ASSERT(sizeof...(S0) == sizeof...(Ts), "inconsistent type arguments"); + // Convert value to std::string with std::to_string except for a value of + // std::string that does not need conversion. + auto dump_to_string = [](const auto &V) { + if constexpr (std::is_same, std::string>::value) { + return V; + } else { + return std::to_string(V); + } + }; + OS << "{"; + (OS << ... << (" " + dump_to_string(std::get(*this)))); + OS << " }"; +} + +template +void DeluxeTuple::dump(std::ostream &OS) const noexcept { + dump(OS, seq_t()); +} + +/// Type alias for a \c rosa::deluxe::DeluxeTuple that contains no elements. +using EmptyDeluxeTuple = DeluxeTuple<>; + +/// Template specialization for \c rosa::deluxe::EmptyDeluxeTuple. +template <> struct DeluxeTuple<> : public std::tuple<> { + /// How many elements the instance has. + static constexpr token_size_t Length = 0; + + /// What types the class contains. + /// + /// Type information encoded as \c rosa::Token. + static constexpr Token TT = TypeToken<>::Value; + + /// Constructor, initializes the underlying \c std::tuple. + DeluxeTuple(void) : std::tuple<>() {} + + /// Default copy-constructor. + DeluxeTuple(const DeluxeTuple &) = default; + + // Default move-constructor. + DeluxeTuple(DeluxeTuple &&) = default; + + /// Default copy-assignment. + DeluxeTuple &operator=(const DeluxeTuple &) = default; + + // Default move-assignment, + DeluxeTuple &operator=(DeluxeTuple &&) = default; + + /// Dumps \p this object to a given \c std::ostream. + /// + /// \param [in,out] OS output stream to dump to + static void dump(std::ostream &OS) noexcept; +}; + +/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given lvalues +/// references. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \see \c rosa::deluxe::DeluxeTuple +/// +/// \param Args values to store in the tuple +/// +/// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as +/// elements +template +inline DeluxeTuple make_deluxe_tuple(const Ts &... Args) noexcept { + return DeluxeTuple(Args...); +} + +/// Creates a \c rosa::deluxe::DeluxeTuple instance from the given rvalue +/// references. +/// +/// \tparam Ts types of elements of the tuple +/// +/// \see \c rosa::deluxe::DeluxeTuple +/// +/// \param Args values to store in the tuple +/// +/// \return an instance of \c rosa::deluxe::DeluxeTuple with \p Args as +/// elements +template +inline DeluxeTuple make_deluxe_tuple(Ts&&... Args) noexcept { + return DeluxeTuple(std::move(Args)...); +} + +/// \defgroup UnwrapDeluxeTuple Implementation of +/// rosa::deluxe::UnwrapDeluxeTuple +/// +/// \brief Unwraps element types from an instance of \c +/// rosa::deluxe::DeluxeTuple into a \c rosa::TypeList +/// +/// Types can be unwrapped from a \c rosa::deluxe::DeluxeTuple instance as \code +/// typename UnwrapDeluxeTuple::Type +/// \endcode +/// +/// For example, the following expression evaluates to `true`: \code +/// std::is_same>::Type, +/// TypeList>::value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam Tuple \c rosa::deluxe::DeluxeTuple to unwrap +template struct UnwrapDeluxeTuple; + +/// Implementation of the template for \c rosa::deluxe::DeluxeTuple instances. +template struct UnwrapDeluxeTuple> { + using Type = TypeList; +}; + +///@} + +/// \defgroup TypeListUnwrapDeluxeTuple Implementation of +/// \c rosa::deluxe::TypeListUnwrapDeluxeTuple +/// +/// \brief Unwraps element types from instances of \c +/// rosa::deluxe::DeluxeTuple in a \c rosa::TypeList. +/// +/// Types can be unwrapped from \c rosa::deluxe::DeluxeTuple instances as \code +/// typename TypeListUnwrapDeluxeTuple::Type +/// \endcode +/// +/// For example, the following expression evaluates to `true`: \code +/// std::is_same< +/// typename TypeListUnwrapDeluxeTuple, +/// T3>>::Type, +/// TypeList +/// >::value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam List \c rosa::TypeList to check +template struct TypeListUnwrapDeluxeTuple; + +/// Specialization for \c rosa::EmptyTypeList. +template <> struct TypeListUnwrapDeluxeTuple { + using Type = EmptyTypeList; +}; + +/// Specialization for the case when the first type in \p List is an instance of +/// \c rosa::deluxe::DeluxeTuple. +template +struct TypeListUnwrapDeluxeTuple, Ts...>> { + using Type = typename TypeListConcat< + typename UnwrapDeluxeTuple>::Type, + typename TypeListUnwrapDeluxeTuple>::Type>::Type; +}; + +/// Implementation for a general first type in \p List. +template +struct TypeListUnwrapDeluxeTuple> { + using Type = typename TypeListPush< + T, typename TypeListUnwrapDeluxeTuple>::Type>::Type; +}; + +///@} + +/// \defgroup IsDeluxeTuple Implementation of \c rosa::deluxe::IsDeluxeTuple +/// +/// \brief Tells if a type is an instance of \c rosa::deluxe::DeluxeTuple. +/// +/// Whether a type \c T is an instance of \c rosa::deluxe::DeluxeTuple can be +/// checked as \code +/// IsDeluxeTuple::Value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam T type to check +template struct IsDeluxeTuple; + +/// Specialization for the case when the type is an instance of \c +/// rosa::deluxe::DeluxeTuple. +template +struct IsDeluxeTuple> { + static constexpr bool Value = true; +}; + +/// Implementation for a general case of type \p T. +template +struct IsDeluxeTuple { + static constexpr bool Value = false; +}; + +///@} + +/// \defgroup TypeListAllDeluxeTuple Implementation of +/// \c rosa::deluxe::TypeListAllDeluxeTuple +/// +/// \brief Tells if all types in a \c rosa::TypeList is an instance of \c +/// rosa::deluxe::DeluxeTuple. +/// +/// Whether a \c rosa::TypeList \c List contains instances of \c +/// rosa::deluxe::DeluxeTuple only can be checked as \code +/// TypeListAllDeluxeTuple::Value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam List \c rosa::TypeList to check +template struct TypeListAllDeluxeTuple; + +/// Specialization for \c rosa::EmptyTypeList. +template <> struct TypeListAllDeluxeTuple { + static constexpr bool Value = true; +}; + +/// Implementation for the general case when there is at leasst one element in +/// the list. +template +struct TypeListAllDeluxeTuple> { + static constexpr bool Value = + IsDeluxeTuple::Value && TypeListAllDeluxeTuple>::Value; +}; + +///@} + +} // End namespace deluxe +} // End namespace rosa + +namespace std { + +/// Dumps a \c rosa::deluxe::Deluxe instance to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param Tuple \c rosa::deluxe::Deluxe to dump +/// +/// \return \p OS after dumping \p Tuple to it +template +ostream &operator<<(ostream &OS, + const rosa::deluxe::DeluxeTuple &Tuple) { + Tuple.dump(OS); + return OS; +} + +} // End namespace std + +#endif // ROSA_DELUXE_DELUXETUPLE_HPP diff --git a/include/rosa/support/atom.hpp b/include/rosa/support/atom.hpp index 60fbe06..1dfe3e0 100644 --- a/include/rosa/support/atom.hpp +++ b/include/rosa/support/atom.hpp @@ -1,179 +1,209 @@ //===-- rosa/support/atom.hpp -----------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/atom.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facility for *atoms*, short strings statically encoded as integers. /// /// \note This implementation is based on the \c atom implementation of CAF. /// \todo Check license. /// /// *Atoms* can be used to turn short string literals into statically generated /// types. The literals may consist of at most \c 10 non-special characters, /// legal characters are \c _0-9A-Za-z and the whitespace character. Special /// characters are turned into whitespace, which may result in different string /// literals being encoded into the same integer value, if any of those contain /// at least one special character. /// /// \note The usage of special characters in the string literals used to create /// *atoms* cannot be checked by the compiler. /// /// Example: /// /// \code /// constexpr AtomValue NameValue = atom("name"); /// using NameAtom = AtomConstant; /// /// [](NameAtom){ std::cout << "Argument of type NameAtom"; }(NameAtom::Value) /// \endcode /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_ATOM_HPP #define ROSA_SUPPORT_ATOM_HPP #include "rosa/support/debug.hpp" namespace rosa { /// Maximal length of valid atom strings. constexpr size_t MaxAtomLength = 10; /// Underlying integer type of atom values. using atom_t = uint64_t; /// Turn \c rosa::atom_t into a strongly typed enumeration. /// /// Values of \c rosa::atom_t casted to \c rosa::AtomValue may be used in a /// type-safe way. enum class AtomValue : atom_t {}; /// Anonymous namespace with implementational details, consider it private. namespace { // clang-format off /// Encodes ASCII characters to 6-bit encoding. constexpr unsigned char AtomEncodingTable[] = { /* ..0 ..1 ..2 ..3 ..4 ..5 ..6 ..7 ..8 ..9 ..A ..B ..C ..D ..E ..F */ /* 0.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2.. */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3.. */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0, /* 4.. */ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, /* 5.. */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 0, 0, 0, 0, 37, /* 6.. */ 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, /* 7.. */ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 0, 0, 0, 0, 0}; // clang-format on /// Decodes 6-bit characters to ASCII constexpr char AtomDecodingTable[] = " 0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ_" "abcdefghijklmnopqrstuvwxyz"; /// Encodes one character and updates the integer representation. /// /// \param Current an encoded value /// \param CharCode a character to add to \p Current /// /// \return \p Current updated with \p CharCode constexpr atom_t nextInterim(atom_t Current, size_t CharCode) { return (Current << 6) | AtomEncodingTable[(CharCode <= 0x7F) ? CharCode : 0]; } /// Encodes a C-string into an integer value to be used as \c rosa::AtomValue. /// /// \param CStr a string to encode /// \param Interim encoded value to add \p CStr to it /// /// \return \p Interim updated with \p CStr constexpr atom_t atomValue(const char *CStr, atom_t Interim = 0xF) { return (*CStr == '\0') ? Interim : atomValue(CStr + 1, nextInterim(Interim, static_cast(*CStr))); } } // End namespace -/// Converts a \c rosa::AtomValue into \c std::string. -/// -/// \param What value to convert -/// -/// \return \c std::string encoded in \p What -std::string to_string(const AtomValue &What); - /// Converts a \c std::string into a \c rosa::AtomValue. /// /// \param S \c std::string to convert /// /// \return \c rosa::AtomValue representing \p S AtomValue atom_from_string(const std::string &S); /// Converts a string-literal into a \c rosa::AtomValue. /// /// \tparam Size the length of \p Str /// /// \param Str the string-literal to convert /// /// \return \c rosa::AtomValue representating \p Str /// /// \pre \p Str is not too long:\code /// Size <= MaxAtomLength + 1 /// \endcode template constexpr AtomValue atom(char const (&Str)[Size]) { // Last character is the NULL terminator. STATIC_ASSERT(Size <= MaxAtomLength + 1, "Too many characters in atom definition"); return static_cast(atomValue(Str)); } /// Lifts a \c rosa::AtomValue to a compile-time constant. /// /// \tparam V \c rosa::AtomValue to lift template struct AtomConstant { /// Constructor has to do nothing. constexpr AtomConstant(void) {} /// Returns the wrapped value. /// /// \return \p V constexpr operator AtomValue(void) const { return V; } /// Returns the wrapped value as of type \c rosa::atom_t. /// /// \return \c rosa::atom_t value from \p V static constexpr atom_t value() { return static_cast(V); } /// An instance *of this constant* (*not* a \c rosa::AtomValue). static const AtomConstant Value; }; // Implementation of the static member field \c rosa::AtomConstant::Value. template const AtomConstant AtomConstant::Value = AtomConstant{}; +} // End namespace rosa + +namespace std { + +/// Converts a \c rosa::AtomValue into \c std::string. +/// +/// \param What value to convert +/// +/// \return \c std::string encoded in \p What +string to_string(const rosa::AtomValue &What); + +/// Dumps a \c rosa::AtomValue to a given \c std::ostream. +/// +/// \param [in,out] OS output stream to dump to +/// \param A \c rosa::AtomValue to dump +/// +/// \return \p OS after dumping \p N to it +inline ostream &operator<<(ostream &OS, const rosa::AtomValue &A) { + OS << to_string(A); + return OS; +} + /// Converts a \c rosa::AtomConstant into \c std::string. /// /// \tparam V \c rosa::AtomValue to convert /// /// \note The actual argument of type `const rosa::AtomConstant` is ignored /// because the \c rosa::AtomValue to convert is encoded in the type itself. /// /// \return the original string encoded in \p V -template std::string to_string(const AtomConstant &) { +template string to_string(const rosa::AtomConstant &) { return to_string(V); } -} // End namespace rosa +/// Dumps a \c rosa::AtomConstant to a given \c std::ostream. +/// +/// \tparam V the \c rosa::AtomValue to dump +/// +/// \param [in,out] OS output stream to dump to +/// \param A \c rosa::AtomConstant providing \p V +/// +/// \return \p OS after dumping \p V to it +template +inline ostream &operator<<(ostream &OS, const rosa::AtomConstant &A) { + (void)A; // Shut compiler about unused parameter. + OS << to_string(V); + return OS; +} + +} // End namespace std #endif // ROSA_SUPPORT_ATOM_HPP diff --git a/include/rosa/support/sequence.hpp b/include/rosa/support/sequence.hpp index 5764a80..62b8408 100755 --- a/include/rosa/support/sequence.hpp +++ b/include/rosa/support/sequence.hpp @@ -1,51 +1,57 @@ //===-- rosa/support/sequence.hpp -------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/sequence.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Template facilities to statically generate a sequence of numbers. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_SEQUENCE_HPP #define ROSA_SUPPORT_SEQUENCE_HPP #include namespace rosa { /// \defgroup Seq Implementation of rosa::Seq /// /// Facility to statically generate sequences of numbers. /// ///@{ /// Template with an empty struct to store a sequence of numbers in compile time /// as template arguments. /// /// Generate a sequence of numbers from `0` up to (including) `(N - 1)` like /// \code /// typename GenSeq::Type /// \endcode 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 \c Seq. template struct GenSeq<0, S...> { using Type = Seq; }; ///@} +/// Convenience template alias for using \c rosa::GenSeq to obtain an instance +/// of \c rosa::Seq. +/// +/// \see \c rosa::Seq and \c rosa::GenSeq +template using seq_t = typename GenSeq::Type; + } // End namespace rosa #endif // ROSA_SUPPORT_SEQUENCE_HPP diff --git a/include/rosa/support/squashed_int.hpp b/include/rosa/support/squashed_int.hpp index 48df20a..2d69d4b 100644 --- a/include/rosa/support/squashed_int.hpp +++ b/include/rosa/support/squashed_int.hpp @@ -1,132 +1,140 @@ //===-- rosa/support/squashed_int.hpp ---------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/squashed_int.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facilities for squashing integer types into standard equivalents. /// /// \note This implementation is partially based on the \c squashed_int /// implementation of CAF. /// \todo Check license. /// //===----------------------------------------------------------------------===// #ifndef ROSA_SUPPORT_SQUASHED_INT_HPP #define ROSA_SUPPORT_SQUASHED_INT_HPP #include "rosa/support/type_list.hpp" #include "rosa/support/type_pair.hpp" namespace rosa { /// Compile-time list of integer types. /// /// \note This list is used to select a proper type as \c rosa::type_nr_t, /// always make sure that \c rosa::type_nr_t remains correct whenever changing /// the list. using IntegerTypesBySize = TypeList< // bytes none_t, // 0 TypePair, // 1 TypePair, // 2 none_t, // 3 TypePair, // 4 none_t, // 5 none_t, // 6 none_t, // 7 TypePair // 8 >; -/// Squashes integer types into \c [u]int_[8|16|32|64]_t equivalents. +/// Squashes integral types (except \c bool) into \c [u]int_[8|16|32|64]_t +/// equivalents. /// /// The squashed type for a type \c T can be obtained as \code /// typename SquashedInt::Type /// \endcode /// -/// \tparam T the integer type to squash +/// \tparam T the integral type to squash /// /// \pre \p T is an integral type:\code /// std::is_integral::value /// \endcode template struct SquashedInt { - STATIC_ASSERT((std::is_integral::value), "squashing a non-integral type"); + STATIC_ASSERT((std::is_integral::value && !std::is_same::value), + "squashing a non-integral type or bool"); using TPair = typename TypeListAt::Type; using Type = typename std::conditional::value, typename TPair::First, typename TPair::Second>::type; }; /// Convenience alias for obtaining a squashed integer type. template using squashed_int_t = typename SquashedInt::Type; /// \defgroup SquashedType Implementation for squashing types /// /// \brief Squashes a type. /// /// The squashed type for a type \c T can be obtained as \code /// typename SquashedType::Type /// \endcode /// The resulting type is squashed with \c rosa::SquashedInt if \c T is -/// integral, and remains \p T otherwise. +/// integral but not \c bool, and remains \p T otherwise. ///@{ /// Definition for the general case, when squashing a non-integral type. /// /// \tparam T the type to squash /// \tparam IsIntegral Always use the default value! template ::value> struct SquashedType { using Type = T; }; /// Specialization for the case when squashing an integral type. /// /// \tparam T the type to squash template struct SquashedType { using Type = squashed_int_t; }; +/// Specialization for the type \c bool. +/// +/// \note The type \c bool is an integral type and would be squashed by the +/// general case to \c uint8_t without this specialization. +template <> struct SquashedType { using Type = bool; }; + ///@} /// Convenience alias for obtaining a squashed type. template using squashed_t = typename SquashedType::Type; /// \defgroup SquashedTypeList Implementation for squashing lists of types /// /// \brief Squashes a \c rosa::TypeList elementwise. /// /// Replaces all types in a \c rosa::TypeList with their corresponding squashed /// types by using \c rosa::SquashedType. The squashed \c rosa::TypeList /// corresponding to \c List can be obtained as \code /// typename SquashedTypeList::Type /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to squash template struct SquashedTypeList; // Specialization for \c rosa::EmptyTypeList. template <> struct SquashedTypeList { using Type = EmptyTypeList; }; /// Specialization for non-empty \c rosa::TypeList. template struct SquashedTypeList> { using Type = typename TypeListPush< squashed_t, typename SquashedTypeList>::Type>::Type; }; ///@} } // End namespace rosa #endif // ROSA_SUPPORT_SQUASHED_INT_HPP diff --git a/include/rosa/support/tokenized_storages.hpp b/include/rosa/support/tokenized_storages.hpp index 0f5bdbf..dca7f4e 100755 --- a/include/rosa/support/tokenized_storages.hpp +++ b/include/rosa/support/tokenized_storages.hpp @@ -1,510 +1,620 @@ //===-- 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 +/// \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 #include 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 class TokenizedStorage; +/// \defgroup TokenizedStorageForTypeList Implementation of +/// rosa::TokenizedStorageForTypeList +/// +/// \brief Transforms a \c rosa::TypeList instance to the corresponding +/// \c rosa::TokenizedStorage instance. +/// +/// A \c rosa::TypeList \c List instance can be turned into a corresponding \c +/// rosa::TokenizedStorage instance as \code +/// typename TokenizedStorageForTypeList::Type +/// \endcode +/// +/// For example, the following expression evaluates to `true`: \code +/// std::is_same>::Type, +/// TokenizedStorage>::value +/// \endcode +///@{ + +/// Declaration of the template. +/// +/// \tparam List \c rosa::TypeList to transform +template struct TokenizedStorageForTypeList; + +/// Implementation of the template for \c rosa::TypeList instances. +template +struct TokenizedStorageForTypeList> { + using Type = 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 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 inline void createArenaElement(void *const, const std::vector &Offsets) { ASSERT(Pos == Offsets.size()); } template inline void createArenaElement(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); createArenaElement(Arena, Offsets, Ts...); } template inline void createArenaElement(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; createArenaElement(Arena, Offsets, Ts...); } ///@} /// Implementation of the template. /// -/// \tparam Type the type of the mandatory first value to store -/// \tparam Types types of any further values to store +/// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to -/// \param T the first value to store in \p Arena˛ -/// \param Ts optional further values to store in \p Arena +/// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \p nullptr. -template -inline void createArenaElements(void *const Arena, const Type &T, +template +inline void createArenaElements(void *const Arena, const Types &... Ts) noexcept { ASSERT(Arena != nullptr); - createArenaElement<0>(Arena, TokenizedStorage::Offsets, T, - Ts...); + createArenaElement<0>(Arena, TokenizedStorage::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 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 inline void createArenaElement(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)); createArenaElement(Arena, Offsets, std::move(Ts)...); } template inline void createArenaElement(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; createArenaElement(Arena, Offsets, std::move(Ts)...); } ///@} /// Implementation of the template. /// -/// \tparam Type the type of the mandatory first value to store -/// \tparam Types types of any further values to store +/// \tparam Types types of values to store /// /// \param Arena pre-allocated memory area to store values to -/// \param T the first value to store in \p Arena -/// \param Ts optional further values to store in \p Arena +/// \param Ts values to store in \p Arena /// /// \pre \p Arena is not \c nullptr. -template -inline void createArenaElements(void *const Arena, Type &&T, - Types &&... Ts) noexcept { +template +inline void createArenaElements(void *const Arena, Types &&... Ts) noexcept { ASSERT(Arena != nullptr); - createArenaElement<0>(Arena, TokenizedStorage::Offsets, - std::move(T), std::move(Ts)...); + createArenaElement<0>(Arena, TokenizedStorage::Offsets, + std::move(Ts)...); } /// Destroys values allocated by \c createArenaElements. /// -/// \tparam Type type of the mandatory first value stored in \p Arena -/// \tparam Types futher types whose values are stored in \p Arena +/// \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 +template 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 inline void destroyArenaElement(void *const, const std::vector &Offsets) noexcept { ASSERT(Pos == Offsets.size()); } template inline void destroyArenaElement(void *const Arena, const std::vector &Offsets) noexcept { ASSERT(Arena != nullptr && Pos < Offsets.size()); static_cast( static_cast(static_cast(Arena) + Offsets[Pos])) ->~Type(); destroyArenaElement(Arena, Offsets); } ///@} /// Implementation of the template. /// -/// \tparam Type the type of the mandatory first value to destroy -/// \tparam Types types of any further values to destroy +/// \tparam Types types of values to destroy /// /// \param Arena the memory area to destroy values from /// /// \pre \p Arena is not \c nullptr. -template +template inline void destroyArenaElements(void *const Arena) noexcept { ASSERT(Arena != nullptr); - destroyArenaElement<0, Type, Types...>( - Arena, TokenizedStorage::Offsets); + destroyArenaElement<0, Types...>(Arena, TokenizedStorage::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 Type type of the first mandatory value to store -/// \tparam Types of any further values to store -template -class TokenizedStorage : public AbstractTokenizedStorage { +/// \tparam Types types of values to store +template +class TokenizedStorage : public AbstractTokenizedStorage { public: /// \c rosa::Token for the stored values. - static constexpr Token ST = - TypeToken::type, - typename std::decay::type...>::Value; + static constexpr Token ST = TypeToken...>::Value; /// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena. static const std::vector 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 offsets(void) noexcept { Token T = ST; // Need a mutable copy. - const size_t N = lengthOfToken(T); // Number of types encoded in \c T. - size_t I = 0; // Start indexing from position \c 0. + const token_size_t N = lengthOfToken(T); // Number of types encoded in \c T. std::vector O(N); // Allocate vector of proper size. - 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; + // Do nothing for 0 elements. + if (N > 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); } - 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 \c Type - /// and \c Types... are default constructible. + /// \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, Type(), Types()...); + createArenaElements(Arena, Types()...); } /// Creates an instance from constant lvalue references. /// - /// \param T the mandatory first value to store - /// \param Ts optional further values to store - TokenizedStorage(const Type &T, const Types &... Ts) noexcept + /// \param Ts values to store + TokenizedStorage(const std::decay_t &... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. - createArenaElements(Arena, T, Ts...); + createArenaElements(Arena, Ts...); } /// Creates an instance from rvalue references. /// - /// \param T the mandatory first value to store - /// \param Ts optional further values to store - TokenizedStorage(Type &&T, Types &&... Ts) noexcept + /// \param Ts values to store + TokenizedStorage(std::decay_t &&... Ts) noexcept : Arena(::operator new(sizeOfValuesOfToken(ST))) { ASSERT(Arena != nullptr); // Sanity check. - createArenaElements(Arena, std::move(T), std::move(Ts)...); + 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(Arena); + destroyArenaElements...>(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(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(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 bool isTypeAt(const size_t Pos) const noexcept { ASSERT(Pos < size()); Token TT = ST; dropNOfToken(TT, Pos); return isHeadOfTokenTheSameType(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 Type for the value stored at index \p Pos + /// \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(Pos) /// \endcode - template Type &valueAt(const size_t Pos) noexcept { - ASSERT(Pos < size() && isTypeAt(Pos)); - return *static_cast(pointerTo(Pos)); + template T &valueAt(const token_size_t Pos) noexcept { + ASSERT(Pos < size() && isTypeAt(Pos)); + return *static_cast(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 Type for the value stored at index \p Pos + /// \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(Pos) /// \endcode - template const Type &valueAt(const size_t Pos) const noexcept { + template + 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(this)->valueAt(Pos); } }; // Implementation of the static member field \c rosa::TokenizedStorage::Offsets. -template -const std::vector TokenizedStorage::Offsets = - TokenizedStorage::offsets(); +template +const std::vector + TokenizedStorage::Offsets = TokenizedStorage::offsets(); + +/// Specialization of the template \c rosa::TokenizedStorage for storing +/// nothing. +/// +/// \note The specialization implements the interface defined by \c +/// rosa::AbstractTokenizedStorage but most of the functions cannot be called +/// because nothing is stored in instances of the class. +template <> class TokenizedStorage<> : public AbstractTokenizedStorage { +public: + /// \c rosa::Token for the stored values. + static constexpr Token ST = TypeToken<>::Value; + + /// Byte offsets to access stored values in \c rosa::TokenizedStorage::Arena. + static const std::vector Offsets; + + /// Creates an instance. + TokenizedStorage(void) noexcept {} + + /// 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) {} + + /// Tells how many values are stored in \p this object. + /// + /// \return `0` + size_t size(void) const noexcept override { return 0; } + + /// Tells the type of the value stored at a position. + /// + /// \pre Do not call. + TypeNumber typeAt(const token_size_t) const noexcept override { + ASSERT(false); + return TypeNumber(0); + } + + /// Provides an untyped pointer for the value stored at a position. + /// + /// \pre Do not call. + void *pointerTo(const token_size_t) noexcept override { + ASSERT(false); + return nullptr; + } + + /// Provides a constant untyped pointer for the value stored at a position. + /// + /// \pre Do not call. + const void *pointerTo(const token_size_t) const noexcept override { + ASSERT(false); + return nullptr; + } + + /// Tells if the value stored at a given index is of a given type. + /// + /// \pre Do not call. + template bool isTypeAt(const size_t) const noexcept { + ASSERT(false); + return false; + } + + /// Gives a reference of a value of a given type stored at a given index. + /// + /// \tparam T type to give a reference of + /// \pre Do not call. + template T &valueAt(const token_size_t) noexcept { + ASSERT(false); + return *static_cast(nullptr); + } + + /// Gives a constant reference of a value of a given type stored at a given + /// index. + /// + /// \tparam T type to give a reference of + /// + /// \pre Do not call. + template const T &valueAt(const token_size_t) const noexcept { + // \note Just use the non-const implementation as that does not modify + // \p this object. + return *static_cast(nullptr); + } +}; } // End namespace rosa #endif // ROSA_SUPPORT_TOKENIZED_STORAGES_HPP diff --git a/include/rosa/support/type_list.hpp b/include/rosa/support/type_list.hpp index bb50be3..b3e8107 100644 --- a/include/rosa/support/type_list.hpp +++ b/include/rosa/support/type_list.hpp @@ -1,444 +1,468 @@ //===-- rosa/support/type_list.hpp ------------------------------*- C++ -*-===// // // The RoSA Framework // //===----------------------------------------------------------------------===// /// /// \file rosa/support/type_list.hpp /// /// \author David Juhasz (david.juhasz@tuwien.ac.at) /// -/// \date 2017 +/// \date 2017-2019 /// /// \brief Facilities for types representing lists of types. /// /// \note This implementation is partially based on the \c 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. /// /// \tparam Ts types to make a list of template struct TypeList { /// Constructor, needs to do nothing. constexpr TypeList(void) {} }; /// The empty \c rosa::Typelist. using EmptyTypeList = TypeList<>; /// \defgroup TypeListAtImpl Implementation of rosa::TypeListAt /// /// \brief Gets the type at index \p Pos from a list of types. /// /// \note Only to be used by the implementation of \c rosa::TypeListAt. ///@{ /// Declaration of the template. /// /// \tparam Pos index to take the element from /// \tparam Ts types template struct TypeListAtImpl; /// Definition for the general case when \p Pos is not \c 0 and there is type in /// the list. template struct TypeListAtImpl { using Type = typename TypeListAtImpl::Type; }; /// Specialization for the case when \p Pos is \c 0. template struct TypeListAtImpl<0, T, Ts...> { using Type = T; }; /// Specialization for the case when there is no more type. /// /// In this case, the found type is \c rosa::none_t. template struct TypeListAtImpl { using Type = none_t; }; ///@} /// \defgroup TypeListAt Definition of rosa::TypeListAt /// /// \brief Gets the element at index \p Pos of \p List. /// /// /// The type at index \c Pos in a \c rosa::TypeList \c List can be obtained as /// \code /// typename TypeListAt::Type /// \endcode /// /// \note The resulting type is \c rosa::none_t if \code /// TypeListSize::Value < Pos /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to take an element from /// \tparam Pos index to take the element from template struct TypeListAt; /// Implementation using \c rosa::TypeListAtImpl. template struct TypeListAt, Pos> { using Type = typename TypeListAtImpl::Type; }; ///@} /// \defgroup TypeListIndexOfImpl Implementation of rosa::TypeListIndexOf /// /// \brief Tells the index of the first occurence of a type in a list of types. /// /// \note Only to be used by the implementation of \c rosa::TypeListIndexOf. ///@{ /// Declaration of the template. /// /// \tparam Pos the number types already being checked from the beginning of the /// list /// \tparam X type to search for /// \tparam Ts remaining list of types template struct TypeListIndexOfImpl; /// Specialization for the case when the list is over. /// /// In this case, the found index is \c -1. template struct TypeListIndexOfImpl { static constexpr int Value = -1; }; /// Specialization for the case when the first type in the remaining list /// is a match. template struct TypeListIndexOfImpl { static constexpr int Value = Pos; }; /// Implementation for the general case when need to continue looking. template struct TypeListIndexOfImpl { static constexpr int Value = TypeListIndexOfImpl::Value; }; ///@} /// \defgroup TypeListIndexOf Definition of rosa::TypeListIndexOf /// /// \brief Tells the index of the first occurence of type in a /// \c rosa::TypeList. /// /// The index of the first occurence of type \c T in \c rosa::TypeList \c List /// can be obtained as \code /// TypeListIndexOf::Value /// \endcode /// /// \note The resulting index is \c -1 if \c T is not present in \c List. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to search in /// \tparam T type to search for template struct TypeListIndexOf; /// Implementation of the template using \c rosa::TypeListIndexOfImpl. template struct TypeListIndexOf, T> { static constexpr int Value = TypeListIndexOfImpl<0, T, Ts...>::Value; }; ///@} /// \defgroup TypeListHead Implementation of rosa::TypeListHead /// /// \brief Gets the first element of a \c rosa::TypeList. /// /// The first element of a \c rosa::TypeList \c List can be obtained as \code /// typename TypeListHead::Type /// \endcode /// /// \note The resulting type is \c rosa::none_t if \c List is /// \c rosa::EmptyTypeList. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to get the first element of template struct TypeListHead; /// Specialization for \c rosa::EmptyTypeList. /// /// In this case, the found type is \c rosa::none_t. template <> struct TypeListHead { using Type = none_t; }; /// Implementation for a non-empty \c rosa::TypeList. template struct TypeListHead> { using Type = T; }; ///@} /// \defgroup TypeListTail Implementation of rosa::TypeListTail /// /// \brief Gets the tail of a \c rosa::TypeList. /// /// The tail of a \c rosa::TypeList \c List, that is \c List except for its /// first element, can be obtained as \code /// typename TypeListTail::Type /// \endcode /// /// \note If \c List is \c rosa::EmptyTypeList, then the resulting type is also /// \c rosa::EmptyTypeList. ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to take the tail of template struct TypeListTail; /// Specialization for \c rosa::EmptyTypeList. /// /// In this case, the resulting type is \c rosa::EmptyTypeList. template <> struct TypeListTail { using Type = EmptyTypeList; }; /// Implementation for a non-empty \c rosa::TypeList. template struct TypeListTail> { using Type = TypeList; }; ///@} /// \defgroup TypeListPush Implementation of rosa::TypeListPush /// /// \brief Extends a \c rosa::TypeList with a type. /// /// Whether the new type is pushed in the front or in the back of the /// \c rosa::TypeList depends on the order of template arguments, as shown in /// the following example: \code /// using List = TypeList /// typename TypeListPush::Type; // TypeList /// typename TypeListPush::Type; // TypeList /// \endcode ///@{ /// Declaration of the template. /// /// \tparam P a type if \p Q is a \c rosa::TypeList, a \c rosa::TypeList /// otherwise /// \tparam Q a type if \p P is a \c rosa::TypeList, a \c rosa::TypeList /// otherwise template struct TypeListPush; /// Implementation for the case when pushing at the back of the /// \c rosa::TypeList. template struct TypeListPush, T> { using Type = TypeList; }; /// Implementation for the case when pushing to the front of the /// \c rosa::TypeList. template struct TypeListPush> { using Type = TypeList; }; ///@} /// \defgroup TypeListDrop Implementation of rosa::TypeListDrop /// /// \brief Drops some elements from the beginning of a \c rosa::TypeList. /// /// The first \c N types of a \c rosa::TypeList \c List can be dropped as \code /// typename TypeListDrop::Type /// \endcode ///@{ /// Declaration of the template. /// /// \tparam N number of types to drop /// \tparam List \c rosa::TypeList to drop the first \p N element of template struct TypeListDrop; /// Specialization for \c rosa::EmptyTypeList. template struct TypeListDrop { using Type = EmptyTypeList; }; /// Implementation for a non-empty \c rosa::TypeList. template struct TypeListDrop> { using Type = typename std::conditional< - N == 0, TypeList, + N == 0, TypeList, typename TypeListDrop>::Type>::type; }; ///@} +/// \defgroup TypeListConcat Implementation of rosa::TypeListConcat +/// +/// \brief Concatenates two \c rosa::TypeList instances. +/// +/// Two instances of \c rosa::TypeList \c List1 and \c List2 can be +/// concatenated as \code +/// typename TypeListConcat::Type +/// \endcode +///@{ + +/// Declaration of the template +/// +/// \tparam List1 the first instance of \c rosa::TypeList +/// \tparam List2 the second instance of \c rosa::TypeList +template struct TypeListConcat; + +/// Implementation of the template for \c rosa::TypeList instances. +template +struct TypeListConcat, TypeList> { + using Type = TypeList; +}; + +///@} + /// \defgroup TypeListSize Implementation of rosa::TypeListSize /// /// \brief Tells the number of types stored in a \c rosa::TypeList. /// /// The size of a \c rosa::TypeList \c List can be obtained as \code /// TypeListSize::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to get the size of template struct TypeListSize; /// Implementation of the template. template struct TypeListSize> { static constexpr size_t Value = sizeof...(Ts); }; template constexpr size_t TypeListSize>::Value; ///@} /// Tests whether a \c rosa::TypeList is empty. /// /// \tparam List \c rosa::TypeList to check template struct TypeListEmpty { /// Denotes whether \p List is an empty \c rosa::TypeList or not. static constexpr bool Value = std::is_same::value; }; /// \defgroup TypeListContains Implementation of rosa::TypeListContains /// /// \brief Tells if a \c rosa::TypeList contains a given type. /// /// Whether a \c rosa::TypeList \c List contains the type \c T can be checked as /// \code /// TypeListContains::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam List \c rosa::TypeList to search in /// \tparam T type to search for template struct TypeListContains; /// Implementation of the template. template struct TypeListContains, T> { static constexpr bool Value = std::conditional, T>::Value == -1, std::false_type, std::true_type>::type::value; }; ///@} /// \defgroup TypeListSubsetOf Implementation of rosa::TypeListSubsetOf /// /// \brief Tells if a \c rosa::TypeList is a subset of another one. /// /// Whether a \c rosa::TypeList \c ListA is a subset of another /// \c rosa::TypeList \c ListB can be checked as \code /// TypeListSubsetOf::Value /// \endcode ///@{ /// Declaration of the template. /// /// \tparam ListA \c rosa::TypeList to check if is a subset of \p ListB /// \tparam ListB \c rosa::TypeList to check if is a superset of \p ListA /// \tparam Fwd always use the default value! template struct TypeListSubsetOf; /// Specialization for the case when all the elements of the original \p ListA /// was found in \p ListB. template struct TypeListSubsetOf { static constexpr bool Value = true; }; /// Specializaton for the case when an element of the original \p ListA cannot /// be found in \p ListB. template struct TypeListSubsetOf { static constexpr bool Value = false; }; /// Definition for the general case. template struct TypeListSubsetOf, List> : TypeListSubsetOf, List, TypeListContains::Value> {}; ///@} /// \defgroup TypeListFindImpl Implementation of rosa::TypeListFind /// /// \brief Finds the first type in a list of types that satisfies a predicate. /// /// \note Only to be used by the implementation of \c rosa::TypeListFind. ///@{ /// Declaration of the template. /// /// \tparam Pred the predicate to check types against /// \tparam Ts list of types to check template