@@ -967,71 +967,122 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
967967 virtual void BuildComputedValues () const {}
968968};
969969
970+ #include < assert.h>
971+
970972class GameRep ::Infosets {
971973public:
972974 class iterator {
973975 public:
974- using iterator_category = std::input_iterator_tag;
976+ using iterator_category = std::forward_iterator_tag; // diagnostic: proper category
975977 using value_type = GameInfoset;
976978 using difference_type = std::ptrdiff_t ;
977- using pointer = GameInfoset ;
978- using reference = GameInfoset ;
979+ using pointer = value_type * ;
980+ using reference = value_type & ;
979981
980982 using outer_iter = std::vector<std::shared_ptr<GamePlayerRep>>::const_iterator;
981983 using inner_iter = std::vector<std::shared_ptr<GameInfosetRep>>::const_iterator;
982984
983- iterator () = default ;
985+ iterator () : owner_id_( 0 ), players_( nullptr ) {}
984986
985- iterator (outer_iter outer, outer_iter outer_end) : outer_(outer), outer_end_(outer_end)
987+ iterator (outer_iter outer, outer_iter outer_end,
988+ const std::vector<std::shared_ptr<GamePlayerRep>> *players, std::size_t owner_id)
989+ : outer_(outer), outer_end_(outer_end), players_(players), owner_id_(owner_id)
986990 {
991+ // ------ DIAGNOSTICS ----------------------------------------------------
992+
993+ // Detect dangling players pointer
994+ assert (players_ != nullptr && " Infosets iterator constructed with null players pointer." );
995+
996+ // Detect begin()==end() but outer mismatch
997+ if (outer_ == outer_end_) {
998+ assert (inner_ == inner_end_ && " Inner iterators must be empty for end iterator." );
999+ }
1000+
9871001 if (outer_ != outer_end_) {
9881002 init_inner ();
9891003 satisfy_invariant ();
9901004 }
9911005 }
9921006
993- GameInfoset operator *() const { return *inner_; }
994- GameInfoset operator ->() const { return *inner_; }
1007+ // -------------------------------------------------------------------------
1008+ // Dereference
1009+ // -------------------------------------------------------------------------
1010+ value_type operator *() const
1011+ {
1012+ assert (players_ != nullptr );
1013+ assert (!is_end () && " Dereferencing end iterator!" );
1014+
1015+ // Check many possible UB causes
1016+ assert (inner_ != inner_end_ && " Inner iterator invalid during dereference." );
1017+ assert (valid_owner () &&
1018+ " Iterator used after view destruction or copying from different view." );
9951019
1020+ // Additional optional check: ensure GameInfoset is safe to construct
1021+ // You can instrument your GameInfoset(value_type) here to assert its invariants.
1022+
1023+ return *inner_;
1024+ }
1025+
1026+ value_type operator ->() const { return operator *(); }
1027+
1028+ // -------------------------------------------------------------------------
1029+ // Increment
1030+ // -------------------------------------------------------------------------
9961031 iterator &operator ++()
9971032 {
1033+ assert (players_ != nullptr );
1034+ assert (!is_end () && " Incrementing end iterator!" );
1035+
9981036 ++inner_;
9991037 satisfy_invariant ();
10001038 return *this ;
10011039 }
10021040
1003- // -------- Windows/MSVC-safe equality ----------
1041+ // -------------------------------------------------------------------------
1042+ // Equality / Inequality
1043+ // -------------------------------------------------------------------------
10041044 bool operator ==(const iterator &other) const
10051045 {
1006- // Both are end() ⇒ equal
1046+ // Detect comparing iterators from different views
1047+ if (owner_id_ != other.owner_id_ ) {
1048+ assert (false && " Comparing iterators from different Infosets views!" );
1049+ }
1050+
1051+ // Both end iterators must compare equal
10071052 if (is_end () && other.is_end ()) {
10081053 return true ;
10091054 }
10101055
1011- // Otherwise, must match outer + inner (safe because both non-end)
1056+ // Detect illegal comparisons
1057+ assert (players_ == other.players_ &&
1058+ " Comparing iterators belonging to different container instances!" );
1059+
10121060 return outer_ == other.outer_ && inner_ == other.inner_ ;
10131061 }
10141062
10151063 bool operator !=(const iterator &other) const { return !(*this == other); }
10161064
10171065 private:
1066+ // -------------------------------------------------------------------------
1067+ // Helpers
1068+ // -------------------------------------------------------------------------
10181069 bool is_end () const { return outer_ == outer_end_; }
10191070
1020- // Safe initialization even when shared_ptr is null
1071+ bool valid_owner () const { return players_ != nullptr && owner_id_ != 0 ; }
1072+
1073+ // Initialize inner iter safely
10211074 void init_inner ()
10221075 {
10231076 const auto &playerPtr = *outer_;
10241077
1025- if (!playerPtr) {
1026- // Null shared_ptr → no infosets
1027- inner_ = inner_end_ = inner_iter{};
1028- return ;
1029- }
1078+ // Check for null shared_ptr (VERY COMMON SOURCE OF UB)
1079+ assert (playerPtr && " Null shared_ptr<GamePlayerRep> inside m_players!" );
10301080
10311081 inner_ = playerPtr->m_infosets .begin ();
10321082 inner_end_ = playerPtr->m_infosets .end ();
10331083 }
10341084
1085+ // Advance to next non-empty inner range
10351086 void satisfy_invariant ()
10361087 {
10371088 while (!is_end () && inner_ == inner_end_) {
@@ -1042,23 +1093,45 @@ class GameRep::Infosets {
10421093 }
10431094 }
10441095
1096+ // -------------------------------------------------------------------------
1097+ // Data members
1098+ // -------------------------------------------------------------------------
10451099 outer_iter outer_{};
10461100 outer_iter outer_end_{};
10471101 inner_iter inner_{};
10481102 inner_iter inner_end_{};
1103+
1104+ const std::vector<std::shared_ptr<GamePlayerRep>> *players_ = nullptr ;
1105+
1106+ // Unique stamp to detect view lifetime/dangling iterator
1107+ std::size_t owner_id_ = 0 ;
10491108 };
10501109
1110+ // -------------------------------------------------------------------------
10511111 // View interface
1052- iterator begin () const { return iterator (players_->begin (), players_->end ()); }
1053- iterator end () const { return iterator (players_->end (), players_->end ()); }
1112+ // -------------------------------------------------------------------------
1113+ iterator begin () const
1114+ {
1115+ return iterator (players_->begin (), players_->end (), players_, owner_id_);
1116+ }
1117+
1118+ iterator end () const { return iterator (players_->end (), players_->end (), players_, owner_id_); }
10541119
1055- explicit Infosets (const GameRep *game)
1056- : players_(&game->m_players) // ✔ store pointer, not reference
1120+ explicit Infosets (const GameRep *game) : players_(&game->m_players)
10571121 {
1122+ assert (game != nullptr );
1123+ owner_id_ = next_owner_id (); // unique ID for detecting illegal comparisons
10581124 }
10591125
10601126private:
10611127 const std::vector<std::shared_ptr<GamePlayerRep>> *players_;
1128+ std::size_t owner_id_;
1129+
1130+ static std::size_t next_owner_id ()
1131+ {
1132+ static std::size_t id = 1 ;
1133+ return id++;
1134+ }
10621135};
10631136
10641137inline GameRep::Infosets GameRep::GetInfosets () const { return Infosets (this ); }
0 commit comments