The Sparta Modeling Framework
Loading...
Searching...
No Matches
NotificationSource.hpp
1// <NotificationSource> -*- C++ -*-
2
3#pragma once
4
5#include <array>
6#include <functional>
7#include <iostream>
8#include <sstream>
9#include <type_traits>
10#include <vector>
11
15#include "sparta/utils/StringManager.hpp"
16namespace sparta
17{
18
60 #define REGISTER_FOR_THIS(func) \
61 registerForThis<typename std::remove_reference<decltype(*this)>::type, \
62 &std::remove_reference<decltype(*this)>::type::func>(this);
63
69 #define DEREGISTER_FOR_THIS(func) \
70 deregisterForThis<typename std::remove_reference<decltype(*this)>::type, \
71 &std::remove_reference<decltype(*this)>::type::func>(this);
72
73 template<typename T>
74 class MirrorNotificationSource;
75
83 {
84 public:
90 template <typename T>
94
98
109
116 typedef std::function<void (NotificationSourceBase const &, uint32_t)> callback_t;
117
165
167 callback_t callback_;
168 TreeNode::ConstWeakPtr tree_node_weak_ptr_;
170
171 public:
172
175 callback_t callback)
176 : type_(type),
177 callback_(callback),
178 tree_node_weak_ptr_(ns.getWeakPtr()),
179 ns_(ns)
180 {
181 ObservationStateCallbackPrivateInterface::registerObservationStateChangeCallback_(ns_, this);
182 }
183
185 {
186 // Deregister from notification ONLY if the node has not been destroyed already
187 if (tree_node_weak_ptr_.expired() == false) {
188 ObservationStateCallbackPrivateInterface::deregisterObservationStateChangeCallback_(ns_, this);
189 }
190 }
191
194 ObservationStateCallback& operator= (const ObservationStateCallback&) = delete;
195
199 ObservationStateChange getType() const { return type_; }
200
205 uint32_t observers) const
206 {
207 callback_(ns, observers);
208 }
209
213 void dump(std::ostream& o) const {
214 o << "<ObservationStateCallback type=" << static_cast<int>(type_) << " node=";
215 if(tree_node_weak_ptr_.expired()){
216 o << "EXPIRED";
217 }else{
218 o << tree_node_weak_ptr_.lock()->getLocation();
219 }
220 o << ">";
221 }
222 };
223
229 private:
230 static void registerObservationStateChangeCallback_(NotificationSourceBase & ns,
231 ObservationStateCallback* const cbobject)
232 {
233 ns.registerObservationStateChangeCallback_(cbobject);
234 }
235
236
237 static void deregisterObservationStateChangeCallback_(NotificationSourceBase & ns,
238 ObservationStateCallback* const cbobject)
239 {
240 ns.deregisterObservationStateChangeCallback_(cbobject);
241 }
242
244 };
245
247
250
254
274 const std::string& name,
275 const std::string& group,
276 TreeNode::group_idx_type group_idx,
277 const std::string& desc,
278 const std::string* notification_name_id,
279 const std::type_info& notification_tinfo) :
280 TreeNode(name, group, group_idx, desc),
281 noti_id_(notification_name_id),
282 noti_tinfo_(notification_tinfo),
283 observed_(false),
284 num_posts_(0)
285 {
286 if(nullptr == parent){
287 throw SpartaException("NotificationSourceBase ")
288 << getLocation() << " must be constructed with a parent";
289 }
290
291 setExpectedParent_(parent);
292
293 if(nullptr == notification_name_id){
294 throw SpartaException("NotificationSourceBase ")
295 << getLocation() << " cannot be constructed with a null notification_name_id";
296 }
297
298 // Other initialization here
299 // ...
300
301 validateNotificationName(*notification_name_id);
302
303 parent->addChild(this);
304
305 determineObs_Nodes_();
306 }
307
313 const std::string& name,
314 const std::string& group,
315 TreeNode::group_idx_type group_idx,
316 const std::string& desc,
317 const std::string& notification_name,
318 const std::type_info& notification_tinfo) :
319 NotificationSourceBase(parent, name, group, group_idx, desc,
320 StringManager::getStringManager().internString(notification_name),
321 notification_tinfo)
322 { }
323
324
329
334 bool observed() const noexcept {
335 return observed_;
336 }
337
348 static void validateNotificationName(const std::string& nm)
349 {
350 for(const char* rsrv : RESERVED_WORDS){
351 if(nm == rsrv){
352 throw SpartaException("Notification name \"")
353 << nm << "\" is a reserved word. ";
354 }
355 }
356
357 if(nm.size() > 0 && nm[0] == '_'){
358 throw SpartaException(" Notification name \"")
359 << nm << "\" begins with an '_' which is not permitted";
360 }
361
362 if(nm.find("__") != std::string::npos){
363 throw SpartaException(" Notification name \"")
364 << nm << "\" contains two adjacent underscores which is not permitted";
365 }
366
367 if(nm.find_first_of(DIGIT_CHARS) == 0){
368 throw SpartaException(" Notification name \"")
369 << nm << "\" begins with a '" << nm[nm.size()-1] << "' character which is not permitted. "
370 << "A Notification name must not begin with a decimal digit.";
371 }
372
373 size_t pos = nm.find_first_not_of(ALPHANUM_CHARS);
374 if(pos != std::string::npos){
375 throw SpartaException("Notification name \"")
376 << nm << "\" contains a '" << nm[pos] << "', which is not permitted. "
377 << "A Notification name must contain only alphanumeric characters and underscores.";
378 }
379 }
380
383
387
394 const std::string* getNotificationID() const {
395 return noti_id_;
396 }
397
402 const std::string& getNotificationName() const {
403 return *noti_id_;
404 }
405
410 const std::type_info& getNotificationType() const {
411 return noti_tinfo_;
412 }
413
419 const std::string getNotificationTypeName() const {
420 return demangle(noti_tinfo_.name());
421 }
422
425
435 const std::vector<TreeNode*>& getObservationPoints() const {
436 return obs_nodes_;
437 }
438
448 uint32_t getNumObservationPoints() const {
449 return obs_nodes_.size();
450 }
451
457 uint32_t getNumObservers() const {
458 return dels_.size();
459 }
460
464 uint64_t getNumPosts() const {
465 return num_posts_;
466 }
467
468 protected:
469
470 const std::string* noti_id_;
471 const std::type_info& noti_tinfo_;
472
477 std::vector<TreeNode*> obs_nodes_;
478
487 std::vector<delegate> dels_;
488
490
495 mutable uint64_t num_posts_;
496
497 // Override from TreeNode
499 virtual void notificationObserverAdded_(const std::type_info& tinfo,
500 const std::string* name_id,
501 TreeNode* obs_node,
502 const delegate* del) override {
503 // Expected to only see observation on the notification that this source can
504 // generate. If these assertions are hit, then the framework is broken or
505 // canGenerateNotification_ is incorrect
506 sparta_assert(tinfo == noti_tinfo_);
507 sparta_assert(name_id == noti_id_ || name_id == StringManager::getStringManager().EMPTY);
508
509 // Add the observation point node if unique
510 auto itr = std::find(obs_nodes_.begin(), obs_nodes_.end(), obs_node);
511 if(itr == obs_nodes_.end()){
512 obs_nodes_.push_back(obs_node);
513 }
514
515 dels_.push_back(*del);
516
517 const bool was_observed = observed_;
518 observed_ = true;
519
520 // Callback happen after state changes are complete (or before) in order to allow recursion
521 if(not was_observed){
522 invokeObservationStateChangeCallbacks_(ObservationStateChange::SOLE_OBSERVER_REGISTERING);
523 }
524 invokeObservationStateChangeCallbacks_(ObservationStateChange::OBSERVER_REGISTERING);
525 }
526
527 // Override from TreeNode
529 virtual void notificationObserverRemoved_(const std::type_info& tinfo,
530 const std::string* name_id,
531 TreeNode* obs_node,
532 const delegate* del) override {
533 // Expected to only see observation on the notification that this source can
534 // generate. If these assertions are hit, then the framework is broken or
535 // canGenerateNotification_ is incorrect
536 sparta_assert(tinfo == noti_tinfo_);
537 sparta_assert(name_id == noti_id_ || name_id == StringManager::getStringManager().EMPTY);
538
539 const bool was_observed = observed_;
540
541 auto delitr = std::find(dels_.begin(), dels_.end(), *del);
542 if(delitr != dels_.end()){
543 dels_.erase(delitr);
544 }
545
546 observed_ = dels_.size() > 0;
547
548 // Remove the observation point node if no delegates refer to it any more
549 bool remaining = false;
550 for(auto d : dels_){
551 if(d.getObservationPoint() == obs_node){
552 remaining = true;
553 break;
554 }
555 }
556 if(false == remaining){
557 auto itr = std::find(obs_nodes_.begin(), obs_nodes_.end(), obs_node);
558 if(itr != obs_nodes_.end()){
559 obs_nodes_.erase(itr);
560 }
561 }
562
563 // Callback happen after state changes are complete (or before) in order to allow recursion
564 // Reverse order of callback from notificationObserverAdded_.
565 invokeObservationStateChangeCallbacks_(ObservationStateChange::OBSERVER_DEREGISTERING);
566 if(was_observed && not observed_){
567 invokeObservationStateChangeCallbacks_(ObservationStateChange::SOLE_OBSERVER_DEREGISTERING);
568 }
569 }
570
571 // Override from TreeNode
572 // Returns true only for matches on node's notification ID or the input pattern
573 bool canGenerateNotification_(const std::type_info& tinfo,
574 const std::string* id,
575 const std::string*& match) const override {
576 if(noti_tinfo_ != tinfo && tinfo != typeid(sparta::TreeNode::ANY_TYPE)){
577 return false;
578 }
579
581 match = noti_id_;
582 return true;
583 }
584
585 return false;
586 }
587
588 // Override from TreeNode
589 virtual std::string stringize(bool pretty=false) const override {
590 (void) pretty;
591 std::stringstream ss;
592 ss << '<' << getLocation() << " name:\""
593 << getNotificationName() << "\" datat:(" << getNotificationTypeName()
594 << ") " << " observers:" << getNumObservers()
595 << " posted:" << getNumPosts() << '>';
596 return ss.str();
597 }
598 private:
599
600 // Override from TreeNode
601 virtual void onAddedAsChild_() noexcept override final {
602 determineObs_Nodes_();
603 }
604
605
615 void determineObs_Nodes_() {
616 obs_nodes_.clear();
617 dels_.clear();
618 observed_ = false;
619
620 TreeNode* node = this;
621 while(node){
622 node->getDelegatesRegisteredForNotification(noti_tinfo_, noti_id_, dels_);
623 if(node->hasObserversRegisteredForNotification(noti_tinfo_, noti_id_)){
624 obs_nodes_.push_back(node);
625 }
626 node = node->getParent();
627 }
628
629 node = getVirtualGlobalNode();
630 node->getDelegatesRegisteredForNotification(noti_tinfo_, noti_id_, dels_);
631 if(node->hasObserversRegisteredForNotification(noti_tinfo_, noti_id_)){
632 obs_nodes_.push_back(node);
633 }
634
635 // Update observed flag
636 observed_ = dels_.size() > 0;
637 }
638
639 // Override from TreeNode
640 // Adds this node's notification info
641 void getPossibleNotifications_(std::vector<NotificationInfo>& infos) const override {
642 infos.emplace_back(this, &noti_tinfo_, noti_id_);
643 }
644
648 void registerObservationStateChangeCallback_(ObservationStateCallback* const hook) {
649 // User errors
650 sparta_assert(in_observation_state_change_callback_ == false,
651 "cannot add observation state change callbacks from within a callback");
652
653 // Internal errors
654 sparta_assert(hook); // Internal-only hook argument is null somehow?
655 sparta_assert(std::find(obs_state_change_cbs_.begin(), obs_state_change_cbs_.end(), hook) == obs_state_change_cbs_.end(),
656 "Internal notification hook " << *hook
657 << " was installed a second time to notification source " << *this);
658
659 // Track it in a list
660 obs_state_change_cbs_.emplace_back(hook);
661 }
662
666 void deregisterObservationStateChangeCallback_(ObservationStateCallback* const hook) {
667 // User errors
668 sparta_assert(in_observation_state_change_callback_ == false,
669 "cannot remove observation state change callbacks from within a callback");
670
671 const auto itr = std::find(obs_state_change_cbs_.begin(), obs_state_change_cbs_.end(), hook);
672
673 // Internal error checking
674 sparta_assert(itr != obs_state_change_cbs_.end(),
675 "Internal notification hook " << *hook
676 << " was not found while attemptint to deregister it from notification source " << *this);
677
678 // Remove it so it is no longer called
679 obs_state_change_cbs_.erase(itr);
680 }
681
687 void invokeObservationStateChangeCallbacks_(ObservationStateChange to_call) {
688 // User Error
689 sparta_assert(in_observation_state_change_callback_ == false,
690 "cannot recursively invoke observation state change callbacks from within a "
691 "callback. User may be changing Notification Observers in response to "
692 "changes in observation state. This is not allowed");
693
694 in_observation_state_change_callback_ = true;
695
696 // Break on first exception!
697 try {
698 for(ObservationStateCallback const * cb : obs_state_change_cbs_){
699 if(cb->getType() == to_call){
700 (*cb)(*this, getNumObservers());
701 }
702 }
703 } catch (...) {
704 in_observation_state_change_callback_ = false;
705
706 throw; // Re-throw
707 }
708
709 in_observation_state_change_callback_ = false;
710 }
711
720 std::vector<ObservationStateCallback*> obs_state_change_cbs_;
721
730 bool in_observation_state_change_callback_ = false;
731 };
732
733
742 template <typename NotificationDataT>
744 {
745 public:
746
747 typedef NotificationDataT data_type;
748 typedef NotificationDataT const const_data_type;
749
753
767 const std::string& name,
768 const std::string& group,
769 TreeNode::group_idx_type group_idx,
770 const std::string& desc,
771 const std::string* notification_name_id) :
772 NotificationSourceBase(parent, name, group, group_idx,
773 desc, notification_name_id,
774 typeid(NotificationDataT))
775 {
776 static_assert(std::is_same<
777 typename std::remove_cv<
778 typename std::remove_reference<
779 typename std::remove_pointer<NotificationDataT>::type
780 >::type
781 >::type,
782 NotificationDataT
783 >::value,
784 "NotificationDataT must NOT be a const, volatile, pointer, or reference"
785 "type. It violates at least one of these restrictions");
786
787 // Other initialization here
788 // ...
789 }
790
791 // Alternate constructor
793 const std::string& name,
794 const std::string& group,
795 TreeNode::group_idx_type group_idx,
796 const std::string& desc,
797 const std::string& notification_name) :
798 NotificationSource(parent,
799 name,
800 group,
801 group_idx,
802 desc,
803 StringManager::getStringManager().internString(notification_name))
804 {
805 // Initialization handled in constructor delegation
806 }
807
808 // Alternate constructor
810 const std::string& name,
811 const std::string& desc,
812 const std::string* notification_name_id) :
813 NotificationSource(parent,
814 name,
817 desc,
818 notification_name_id)
819 {
820 // Initialization handled in constructor delegation
821 }
822
823 // Alternate constructor
825 const std::string& name,
826 const std::string& desc,
827 const std::string& notification_name) :
828 NotificationSource(parent,
829 name,
832 desc,
833 StringManager::getStringManager().internString(notification_name))
834 {
835 // Initialization handled in constructor delegation
836 }
837
838 // Alternate constructor with anonymous name
840 const std::string& group,
841 TreeNode::group_idx_type group_idx,
842 const std::string& desc,
843 const std::string* notification_name_id) :
844 NotificationSource(parent,
845 "",
846 group,
847 group_idx,
848 desc,
849 notification_name_id)
850 {
851 // Initialization handled in constructor delegation
852 }
853
854 // Alternate constructor with anonymous name
856 const std::string& group,
857 TreeNode::group_idx_type group_idx,
858 const std::string& desc,
859 const std::string& notification_name) :
860 NotificationSource(parent,
861 "",
862 group,
863 group_idx,
864 desc,
865 StringManager::getStringManager().internString(notification_name))
866 {
867 // Initialization handled in constructor delegation
868 }
869
875
876
877
881
924 template <typename T, void (T::*TMethod)(const TreeNode&, const TreeNode&, const_data_type&)>
928
929 // Overload accepting a class member function signature having only a data argument
930 template <typename T, void (T::*TMethod)(const_data_type&)>
931 void registerForThis(T* obj) {
933 }
934
943 template <typename T, void (T::*TMethod)(const TreeNode&, const TreeNode&, const_data_type&)>
947
948 // Overload accepting a class member function signature having only a data argument
949 template <typename T, void (T::*TMethod)(const_data_type&)>
950 void deregisterForThis(T* obj) {
952 }
953
958 void postNotification(const NotificationDataT& data) const {
959 ++num_posts_;
960
961 // Could notify observers at this node and above through TreeNode's propagation interface
962 //sparta_assert(getParent() != nullptr,
963 // "Cannot postNotification from NotificationSource " << getLocation() << " because parent is null");
964 //postPropagatingNotification_(this, data, noti_id_);
965
966 // Could Notify all observing TreeNodes and allow them to invoke delegates
967 //for(TreeNode* o_node : obs_nodes_){
968 // invokeDelegatesOn_(o_node, data, noti_id_);
969 //}
970
971 // Directly invoke all applicable delegates.
972 // Note that this works even if ancestors are destroyed because
973 // deregistration does not take place.
974 for(const delegate& d : dels_){
975 d(*this, data);
976 }
977 }
978
981 };
982
998 {
999 public:
1000 using NotificationSource<std::string>::NotificationSource;
1001
1002 void setFormatString(const std::string& format_str)
1003 {
1004 // The format_str looks something like "%s_$i.json".
1005 // In the event that we have multiple reports setup
1006 // to be triggered by the same notif source, we need
1007 // to ensure that the format specifiers are the same
1008 // for all ReportDescriptor dest_file's we are given.
1009 auto specs = parseFormatSpecifiers_(format_str);
1010 if (!parsed_specs_.empty())
1011 {
1012 sparta_assert(specs == parsed_specs_);
1013 }
1014 else
1015 {
1016 parsed_specs_ = specs;
1017 }
1018
1019 format_str_ = format_str;
1020 }
1021
1022 bool hasFormatString() const {
1023 return !format_str_.empty();
1024 }
1025
1033 template <typename... Args>
1034 void postNotification(Args... args) const
1035 {
1036 sparta_assert(!format_str_.empty(),
1037 "setFormatString() must be called before postNotification() on " << getLocation());
1038
1039 constexpr std::array<SpecType, sizeof...(Args)> expected_types { specTypeFor_<Args>()... };
1040
1041 if(parsed_specs_.size() != sizeof...(Args)){
1042 throw SpartaException("Sprintf format string \"")
1043 << format_str_ << "\" for " << getLocation() << " has " << parsed_specs_.size()
1044 << " specifier(s) but " << sizeof...(Args) << " argument(s) were given";
1045 }
1046
1047 for(std::size_t i = 0; i < parsed_specs_.size(); ++i){
1048 if(parsed_specs_[i] != expected_types[i]){
1049 throw SpartaException("Sprintf format string \"")
1050 << format_str_ << "\" for " << getLocation()
1051 << " has a type mismatch at specifier index " << i;
1052 }
1053 }
1054
1055 // One stream-to-ostringstream lambda per argument, invoked in order as specifiers are encountered
1056 const std::array<std::function<void(std::ostringstream&)>, sizeof...(Args)> streamers {
1057 [&args](std::ostringstream& o){ o << args; }...
1058 };
1059
1060 std::ostringstream oss;
1061 std::size_t arg_idx = 0;
1062 for(std::size_t i = 0; i < format_str_.size(); ++i){
1063 if(format_str_[i] != '%'){
1064 oss << format_str_[i];
1065 continue;
1066 }
1067
1068 const char spec = format_str_[++i];
1069 if(spec == '%'){
1070 oss << '%';
1071 }else{
1072 streamers[arg_idx++](oss);
1073 }
1074 }
1075
1077 }
1078
1079 private:
1080 enum class SpecType { STRING, INT };
1081
1082 // Maps an argument type to the specifier it must match (%i for non-bool integrals, %s otherwise)
1083 template <typename T>
1084 static constexpr SpecType specTypeFor_()
1085 {
1086 using PlainT = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
1087 if constexpr (std::is_integral<PlainT>::value && !std::is_same<PlainT, bool>::value){
1088 return SpecType::INT;
1089 }else{
1090 return SpecType::STRING;
1091 }
1092 }
1093
1094 // Runtime scan of format_str for %s/%i/%% specifiers, in order of appearance
1095 static std::vector<SpecType> parseFormatSpecifiers_(const std::string& format_str)
1096 {
1097 std::vector<SpecType> specs;
1098 for(std::size_t i = 0; i < format_str.size(); ++i){
1099 if(format_str[i] != '%'){
1100 continue;
1101 }
1102
1103 if(i + 1 >= format_str.size()){
1104 throw SpartaException("Sprintf format string \"")
1105 << format_str << "\" ends with a lone '%' character";
1106 }
1107
1108 const char spec = format_str[++i];
1109 switch(spec){
1110 case 's':
1111 specs.push_back(SpecType::STRING);
1112 break;
1113 case 'i':
1114 specs.push_back(SpecType::INT);
1115 break;
1116 case '%':
1117 break; // Literal '%', not a specifier
1118 default:
1119 throw SpartaException("Sprintf format string \"")
1120 << format_str << "\" contains unsupported specifier '%" << spec
1121 << "'. Only %s and %i are supported";
1122 }
1123 }
1124 return specs;
1125 }
1126
1127 std::string format_str_;
1128 std::vector<SpecType> parsed_specs_;
1129 };
1130
1139 template<typename NotificationDataT>
1141 {
1142 typedef std::vector<NotificationSourceBase*> SrcList;
1143 public:
1160 const std::string& name,
1161 const std::string& group,
1162 TreeNode::group_idx_type group_idx,
1163 const std::string& desc,
1164 const std::string* notification_name_id):
1165 NotificationSourceBase(parent, name, group, group_idx, desc,
1166 notification_name_id,
1167 typeid(NotificationDataT))
1168 {}
1169
1175 const std::string& name,
1176 const std::string& group,
1177 TreeNode::group_idx_type group_idx,
1178 const std::string& desc,
1179 const std::string& notification_name) :
1180 NotificationSourceBase(parent, name, group, group_idx,
1181 desc,
1182 notification_name,
1183 typeid(NotificationDataT))
1184 {}
1185
1186 // Alternate constructor
1188 const std::string& name,
1189 const std::string& desc,
1190 const std::string& notification_name) :
1192 name,
1195 desc,
1196 StringManager::getStringManager().internString(notification_name))
1197 {}
1198
1210 void addLink(TreeNode *node, const std::string &label) override final
1211 {
1212 (void)label; // We do not care about the label.
1213 sparta_assert(node != nullptr);
1214 const uint32_t prev_size = concretes_.size();
1215 findMatchingSourcesBelowNode_(*node, concretes_);
1216 // Make sure we found atleast 1 node that we are shadowing,
1217 // otherwise this MirrorNotificationSource is not linked to
1218 // anyone and should be taken out.
1219 if (concretes_.size() - prev_size == 0)
1220 {
1221 SpartaException ex("MirrorNotificationSource \"");
1222 ex << getName() << "\" was unable to bind to any real NotificationSources"
1223 << " found under the nodes provided to addLink().";
1224 throw ex;
1225 }
1226 }
1227 private:
1228
1233 void findMatchingSourcesBelowNode_(sparta::TreeNode& node,
1234 SrcList& bases)
1235 {
1236 auto* as_src = dynamic_cast<NotificationSourceBase*>(&node);
1237 if (as_src)
1238 {
1239 // If this MirrorNotificationSource happened to live under the search
1240 // of node, then we would certainly not want to add ourselves as a
1241 // concrete instance!
1242 if (as_src != this)
1243 {
1244 // We won't mirror anything that isn't explicitly allowed by the concrete
1245 // source.
1246 if (as_src->getNotificationID() == getNotificationID() &&
1247 as_src->getNotificationType() == getNotificationType())
1248 {
1249 bases.emplace_back(dynamic_cast<NotificationSourceBase*>(&node));
1250 }
1251 }
1252 }
1253
1254 for (auto n : node.getChildren())
1255 {
1256 findMatchingSourcesBelowNode_(*n, bases);
1257 }
1258
1259 }
1260 // Override from TreeNode
1262 void notificationObserverAdded_(const std::type_info& tinfo,
1263 const std::string* name_id,
1264 TreeNode* obs_node,
1265 const delegate* del) override final
1266 {
1267 if (del->revealsOrigin())
1268 {
1269 SpartaException ex("Cannot register for notification \"");
1270 ex << *name_id << "\"" << " because this notification source "
1271 << "is only shadowing a private concrete notification source. "
1272 << "The callback signature type would reveal access to a private node "
1273 << "and be misleading. We do not support this feature at this time.";
1274 throw ex;
1275 }
1276
1277 if (concretes_.size() == 0)
1278 {
1279 SpartaException ex ("Cannot register for notification \"");
1280 ex << *name_id << "\" because this is a MirrorNotificationSource "
1281 << "that is not linked to any concrete sources. It is possible you "
1282 << "are registering for the notification too early and should be registering "
1283 << "during onBindTreeLate_()";
1284 throw ex;
1285 }
1286 for(auto concrete : concretes_)
1287 {
1288 concrete->notificationObserverAdded_(tinfo, name_id,
1289 obs_node, del);
1290 }
1291 }
1292 // Override from TreeNode
1294 void notificationObserverRemoved_(const std::type_info& tinfo,
1295 const std::string* name_id,
1296 TreeNode* obs_node,
1297 const delegate* del) override final
1298 {
1299 if (concretes_.size() == 0)
1300 {
1301 SpartaException ex ("Cannot deregister for notification \"");
1302 ex << *name_id << "\" because this is a MirrorNotificationSource "
1303 << "that is not linked to any concrete sources. It is possible you "
1304 << "are deregistering for the notification too early and should be registering "
1305 << "during onBindTreeLate_()";
1306 throw ex;
1307 }
1308
1309 for(auto concrete : concretes_)
1310 {
1311 concrete->notificationObserverRemoved_(tinfo, name_id,
1312 obs_node, del);
1313 }
1314 }
1315 // Override from TreeNode
1316 // Returns true only for matches on node's notification ID or the input pattern
1317 bool canGenerateNotification_(const std::type_info& tinfo,
1318 const std::string* id,
1319 const std::string*& match) const override final
1320 {
1321 if (concretes_.size() == 0)
1322 {
1323 return false;
1324 }
1325 bool can_generate = true;
1326 for (auto noti_src : concretes_)
1327 {
1328 can_generate &= noti_src->canGenerateNotification_(tinfo, id, match);
1329 }
1330 return can_generate;
1331 }
1332
1339 void validateNode_() const override final
1340 {
1341 if (concretes_.size() == 0)
1342 {
1343 SpartaException ex("Unbound MirrorNotificationSource ");
1344 ex << getName() << " was never bound to concrete notification sources.";
1345 throw ex;
1346 }
1347 }
1348
1349 // Override from TreeNode
1350 virtual std::string stringize(bool pretty=false) const override final {
1351 (void) pretty;
1352 std::stringstream ss;
1354 ss << " shadows: ";
1355 try{
1356 ss << concretes_.size() << " sources ";
1357 }
1358 catch (SpartaException&)
1359 {
1360 ss << "[UNAVAILABLE]";
1361 }
1362 return ss.str();
1363 }
1365 SrcList concretes_;
1366 };
1367
1368 inline std::ostream& operator<<(std::ostream& o, const NotificationSourceBase::ObservationStateCallback& osc)
1369 {
1370 osc.dump(o);
1371 return o;
1372 }
1373
1374 inline std::ostream& operator<<(std::ostream& o, const NotificationSourceBase::ObservationStateCallback* osc)
1375 {
1376 if(osc == nullptr){
1377 o << "nullptr";
1378 }else{
1379 o << *osc;
1380 }
1381 return o;
1382 }
1383
1384} // namespace sparta
1385
Set of macros for Sparta assertions. Caught by the framework.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
Exception class for all of Sparta.
Basic Node framework in sparta device tree composite pattern.
#define RESERVED_WORDS
Reserved words in Python language, Python builtins, and other reservations. TreeNode names and groups...
Definition TreeNode.hpp:69
#define ALPHANUM_CHARS
Enables tracing of TreeNode lifetimes in a set of output txt files.
Definition TreeNode.hpp:58
#define DIGIT_CHARS
Digit characters (for valid TreeNode groups)
Definition TreeNode.hpp:62
essentially a pass through notification source that is a placeholder in a shadow tree.
MirrorNotificationSource(TreeNode *parent, const std::string &name, const std::string &group, TreeNode::group_idx_type group_idx, const std::string &desc, const std::string *notification_name_id)
MirrorNotificationSource.
void addLink(TreeNode *node, const std::string &label) override final
MirrorNotificationSource(TreeNode *parent, const std::string &name, const std::string &group, TreeNode::group_idx_type group_idx, const std::string &desc, const std::string &notification_name)
Alternate construct that accepts a notification name string instead of an interned string pointer.
Controlled interface to NotificationSourceBase for regiistering and deregistering observation state c...
Callback for observation-state of a NotificationSourceBase.
void operator()(NotificationSourceBase const &ns, uint32_t observers) const
Invoked the callback function.
void dump(std::ostream &o) const
Dump string description to an ostream.
ObservationStateChange getType() const
Get tyhe type of change associated with this callback instance.
A TreeNode that generates a single specific type of notification which propagates up a tree of TreeNo...
virtual ~NotificationSourceBase()
Destructor.
static void validateNotificationName(const std::string &nm)
Validates the given notification name string.
bool observed_
Cached value of obs_nodes_.size() > 0 for faster queries.
friend class ObservationStateCallbackInterface
Allow controlled interaction with some members of this class.
bool canGenerateNotification_(const std::type_info &tinfo, const std::string *id, const std::string *&match) const override
Implements TreeNode::canGenerateNotification. Node subclasses which will post notifications must impl...
const std::string & getNotificationName() const
Returns the notification name string for slow string comparison or printing.
ObservationStateChange
Various hook types for which one can register internal callbacks on this notificaiton source.
@ SOLE_OBSERVER_REGISTERING
A sole observer is being registered to observe this notification source. This is a transition from 0 ...
@ OBSERVER_REGISTERING
An observer is being registered to observe this notification source. This may be the first,...
@ SOLE_OBSERVER_DEREGISTERING
A sole observer is being de-registered from observing this notification source. This is a transition ...
@ OBSERVER_DEREGISTERING
An observer is being de-registered from observing this notification source. This may be the first,...
uint32_t getNumObservers() const
Returns the number of delegates affected by postings of this notification.
std::vector< TreeNode * > obs_nodes_
Vector of TreeNodes at which the notification that this node can generate is being observed.
bool observed() const noexcept
Is this NotificationSourceBase being observed at this node or an ancestor of any distance.
virtual void notificationObserverRemoved_(const std::type_info &tinfo, const std::string *name_id, TreeNode *obs_node, const delegate *del) override
NotificationSourceBase(TreeNode *parent, const std::string &name, const std::string &group, TreeNode::group_idx_type group_idx, const std::string &desc, const std::string *notification_name_id, const std::type_info &notification_tinfo)
NotificationSourceBase.
virtual std::string stringize(bool pretty=false) const override
Create a string representation of this node.
std::function< void(NotificationSourceBase const &, uint32_t)> callback_t
Observation state change callback function signature.
const std::type_info & noti_tinfo_
Type of notification data.
virtual void notificationObserverAdded_(const std::type_info &tinfo, const std::string *name_id, TreeNode *obs_node, const delegate *del) override
uint64_t getNumPosts() const
Returns the number of notifications posted by this node.
NotificationSourceBase(TreeNode *parent, const std::string &name, const std::string &group, TreeNode::group_idx_type group_idx, const std::string &desc, const std::string &notification_name, const std::type_info &notification_tinfo)
Alternate construct that accepts a notification name string instead of an interned string pointer.
uint32_t getNumObservationPoints() const
Returns the number of observation points affecting this notification source.
const std::string getNotificationTypeName() const
Gets the demangled name of the C++ notification type which this NotificationSource can emit.
const std::vector< TreeNode * > & getObservationPoints() const
Returns the vector of observation points on this notification source.
const std::type_info & getNotificationType() const
Returns std::type_info from typeid() on the notification data type which this NotificationSource can ...
uint64_t num_posts_
Number of messages posted whether observed or not.
const std::string * noti_id_
Name of notification generated. Should be similar to node name.
std::vector< delegate > dels_
Vector of delegates directly observing this notification source.
const std::string * getNotificationID() const
Returns notification ID (string pointer from Notification ID interned in sparta::StringManager)
A TreeNode that generates a specific type of notification which propagates up a tree of TreeNodes (us...
void deregisterForThis(T *obj)
Deregisters a callback method that was registered with registerForThis.
void postNotification(const NotificationDataT &data) const
Post with reference to data with parent as message origin.
void registerForThis(T *obj)
Registers a callback method to listen for the notification generated by this NotificationSource....
NotificationDataT data_type
Type of notification data generated by this instance.
NotificationSource(TreeNode *parent, const std::string &name, const std::string &group, TreeNode::group_idx_type group_idx, const std::string &desc, const std::string *notification_name_id)
NotificationSource.
NotificationDataT const const_data_type
Const qualified data type.
Used to construct and throw a standard C++ exception. Inherits from std::exception.
Accepts a sprintf-style format string like "%s_%i.json" and uses the arguments given to postNotificat...
void postNotification(Args... args) const
Formats the current format string (see setFormatString) with args and posts the resulting std::string...
Manages string internment for SPARTA. This allows strings to be compared by pointer once interned.
static StringManager & getStringManager()
Returns the StringManager singleton.
Temporary delegate for notificaiton invokation until the implementation is working....
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:204
std::string getLocation() const override final
static constexpr char GROUP_NAME_NONE[]
Group name indicating that a node belongs to no group.
Definition TreeNode.hpp:313
static bool notificationCategoryMatch(const std::string *query_id, const std::string *node_id)
Checks if two notification categories match where one is an actual category.
void registerForNotification(T *obj, const std::string &name, bool ensure_possible=true)
Registers a callback method to listen for all notifications having the specified data type DataT and ...
TreeNode()=delete
Not default-constructable.
const ChildrenVector getChildren() const
Gets a vector of all children of this node in any group in the order in which they were added to this...
void addChild(TreeNode *child, bool inherit_phase=true)
Adds a TreeNode to this node as a child.
uint32_t group_idx_type
Index within a group.
Definition TreeNode.hpp:260
void deregisterForNotification(T *obj, const std::string &name)
Removes at most one registration made with registerForNotification.
WeakPtr getWeakPtr()
Gets a weak pointer to this TreeNode. This weak pointer is guaranteed to expire when this TreeNode is...
static TreeNode * getVirtualGlobalNode()
Gets the virtual global node singleton. This node can have no parent and no children....
static constexpr group_idx_type GROUP_IDX_NONE
GroupIndex indicating that a node has no group index because it belongs to no group.
Definition TreeNode.hpp:302
std::vector< delegate > DelegateVector
Vector of delegates representing a list of observers to notify.
const std::string & getName() const override
Gets the name of this node.
void setExpectedParent_(const TreeNode *parent)
Tracks a node as an expected parent without actually adding this node as a child. This is used almost...
std::weak_ptr< const TreeNode > ConstWeakPtr
Weak pointer to a const TreeNode. Acquire with getWeakPtr.
Definition TreeNode.hpp:270
Macros for handling exponential backoff.
std::ostream & operator<<(std::ostream &o, const SimulationInfo &info)
ostream insertion operator for SimulationInfo
std::string demangle(const std::string &name) noexcept
Demangles a C++ symbol.
Definition Utils.hpp:214
Type for indicating that ANY notification source type should be included in a search performed by loc...
Definition TreeNode.hpp:289