// ************************************************************************** /** @class EventImp @brief A general base-class describing events issues in a state machine @section General purpose The general purpose of this class is to describe an event which can occur in one of our StateMachines. It provides pointers to data associated with the event, a target state and stores the states in which issuing this event is allowed. The target state might be negative to describe that no transition of the state is requested. Such an event canjust be a description of an event, but can also be an issued event which already contain data. The format can, but need not, contain the format of the data area. As a rule, it should follow the format also used in the DIM network. @section Assigning functions to an event To any event a function call can be assigned. Thanks to boost::bind there are various and different very powerful ways to do that. The function assigned with AssignFunction must return int. When it is executed it is given a const reference of the current event as an argument, i.e. if you want to get such a reference in your function, you can reference it using the placeholder _1. (Remark: it is allowe to omit the _1 placeholder if no reference to the EventImp object is needed) A few examples: \code int function(const EventImp &evt, int i, const char *txt) { return i; } EventImp evt; evt.AssignFunction(boost::bind(function, _1, 7, "hallo")); cout << evt.Exec() << endl; // 7 \endcode When the function is executed later via ExecFunc() in will get a reference to the executing EventImp as its first argument (indicated by '_1'), it will get 7 and "hallo" as second and third argument. \code int function(int i, const char *txt, const EventImp &evt) { return i; } EventImp evt; evt.AssignFunction(boost::bind(function, 7, "hallo", _1)); cout << evt.Exec() << endl; // 7 \endcode Is the same example than the one above, but the arguments are in a different order. \code class A { int function(const EventImp &evt, int i, const char *txt) { cout << this << endl; return i; } }; A a; EventImp evt; evt.AssignFunction(boost::bind(&A::function, &a, _1, 7, "hallo")); cout << evt.Exec() << endl; // &a // 7 \endcode The advanatge of boost::bind is that it also works for member functions of classes. In this case the first argument after the function-pointer \b must be a pointer to a valid class-object. This can also be \em this if called from within a class object. Also note that everything (as usual) which is not a reference is copied when the bind function is invoked. If you want to distribute a reference instead use ref(something), like \code int function(int &i) { return i; } int j = 5; EventImp evt; evt.AssignFunction(bind(function, ref(j)); j = 7; cout << evt.Exec() << endl; // 7 \endcode Note, that you are responsible for the validity, that means: Do not destroy your object (eg. reference to j) while bind might still be called later, or a pointer to \em this. @section References - boost::bind (V1.45.0) @todo Add link to DIM format */ // ************************************************************************** #include "EventImp.h" #include #include "Time.h" #include "WindowLog.h" #include "Description.h" using namespace std; // -------------------------------------------------------------------------- // //! Copy the contents of an EventImp (fTargetState, fAllowedStates and //! fFunction) // EventImp::EventImp(const EventImp &cmd) : fTargetState(cmd.fTargetState), fAllowedStates(cmd.fAllowedStates), fFunction(cmd.fFunction) { } // -------------------------------------------------------------------------- // //! If the state is 0 or positive add it to fAllowedStates //! //! @param state //! The state which should be added // void EventImp::AddAllowedState(int state) { if (state>=0) fAllowedStates.push_back(state); } // -------------------------------------------------------------------------- // //! Add all states in the string to fAllowedStates. //! //! @param states //! A string containing the states. They can either be separated by //! whitespaces or commas, e.g. "1 2 3 4" or "1, 7, 9, 10". Note that //! no real consistency check is done. // void EventImp::AddAllowedStates(const char *states) { stringstream stream(states); const bool sep = stream.str().find(',')==string::npos; string buffer; while (getline(stream, buffer, sep ? ' ' : ',')) AddAllowedState(stoi(buffer)); } // -------------------------------------------------------------------------- // //! Return whether the given state is in the list of allowed states. //! //! @param state //! The state to look for in fAllowedStates //! //! @returns //! If the given state is negative returns false. If the list of allowed //! states is empty return true. Otherwise return whether the state //! is found in fAllowedList or not. // bool EventImp::IsStateAllowed(int state) const { // States with negative values are internal states and are // never allowed if (state<0) return false; // In case no allowed state is explicitly set // all positive states are allowed if (fAllowedStates.size()==0) return true; return find(fAllowedStates.begin(), fAllowedStates.end(), state)!=fAllowedStates.end(); } // -------------------------------------------------------------------------- // //! @returns the event data converted to a std::string. Trailing redundant //! \0's are removed. //! string EventImp::GetString() const { size_t s = GetSize()-1; while (s>0 && GetText()[s]==0) s--; return std::string(GetText(), s+1); } // -------------------------------------------------------------------------- // //! Print the contents of the event to the given stream. //! //! @param out //! An ostream to which the output should be redirected. //! //! @param strip //! defines whether a possible SERVER name in the event name //! should be stripped or not. //! void EventImp::Print(ostream &out, bool strip) const { out << " \xc2\xb7 "; const string str = GetName(); if (!str.empty()) out << kBold << str.substr(strip?str.find_first_of('/')+1:0); const string fmt = GetFormat(); if (!fmt.empty()) out << kBold << "[" << fmt << "]"; vector v = Description::SplitDescription(GetDescription()); if (!GetDescription().empty()) { for (vector::const_iterator j=v.begin()+1; j!=v.end(); j++) out << " <" << j->name << ">"; } out << kReset << ":"; for (unsigned int i=0; i" << fTargetState << "]"; const Time tm = GetTime(); const bool t = tm!=Time::None && tm!=Time(1970,1,1); const bool s = GetSize()>0; if (s || t) out << "("; if (t) out << tm.GetAsStr("%H:%M:%S.%f"); if (s && t) out << "/"; if (s) out << "size=" << GetSize(); if (s || t) out << ")"; out << endl; if (GetDescription().empty()) return; out << " " << v[0].comment << endl; for (vector::const_iterator j=v.begin()+1; j!=v.end(); j++) { out << " " << kGreen << j->name; if (!j->comment.empty()) out << kReset << ": " << kBlue << j->comment; if (!j->unit.empty()) out << kYellow << " [" << j->unit << "]"; out << endl; } } // -------------------------------------------------------------------------- // //! Calls Print(std::cout) //! //! @param strip //! defines whether a possible SERVER name in the event name //! should be stripped or not. // void EventImp::Print(bool strip) const { Print(cout, strip); }