Skip to content

Commit 9d55320

Browse files
committed
Hold shared pointer to ensure lifetime
1 parent 89d36dd commit 9d55320

2 files changed

Lines changed: 30 additions & 97 deletions

File tree

src/games/game.h

Lines changed: 26 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -967,122 +967,66 @@ class GameRep : public std::enable_shared_from_this<GameRep> {
967967
virtual void BuildComputedValues() const {}
968968
};
969969

970-
#include <assert.h>
971-
972970
class GameRep::Infosets {
973971
public:
974972
class iterator {
975973
public:
976-
using iterator_category = std::forward_iterator_tag; // diagnostic: proper category
974+
using iterator_category = std::forward_iterator_tag;
977975
using value_type = GameInfoset;
978976
using difference_type = std::ptrdiff_t;
979-
using pointer = value_type *;
980-
using reference = value_type &;
977+
using pointer = value_type;
978+
using reference = value_type;
981979

982980
using outer_iter = std::vector<std::shared_ptr<GamePlayerRep>>::const_iterator;
983981
using inner_iter = std::vector<std::shared_ptr<GameInfosetRep>>::const_iterator;
984982

985-
iterator() : owner_id_(0), players_(nullptr) {}
983+
iterator() = default;
986984

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)
985+
iterator(std::shared_ptr<GameRep> game, outer_iter outer, outer_iter outer_end)
986+
: game_(std::move(game)), outer_(outer), outer_end_(outer_end)
990987
{
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-
1001988
if (outer_ != outer_end_) {
1002989
init_inner();
1003990
satisfy_invariant();
1004991
}
1005992
}
1006993

1007-
// -------------------------------------------------------------------------
1008-
// Dereference
1009-
// -------------------------------------------------------------------------
1010994
value_type operator*() const
1011995
{
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.");
1019-
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_;
996+
return GameInfoset(*inner_); // GameObjectPtr constructor
1024997
}
1025998

1026-
value_type operator->() const { return operator*(); }
999+
value_type operator->() const { return GameInfoset(*inner_); }
10271000

1028-
// -------------------------------------------------------------------------
1029-
// Increment
1030-
// -------------------------------------------------------------------------
10311001
iterator &operator++()
10321002
{
1033-
assert(players_ != nullptr);
1034-
assert(!is_end() && "Incrementing end iterator!");
1035-
10361003
++inner_;
10371004
satisfy_invariant();
10381005
return *this;
10391006
}
10401007

1041-
// -------------------------------------------------------------------------
1042-
// Equality / Inequality
1043-
// -------------------------------------------------------------------------
10441008
bool operator==(const iterator &other) const
10451009
{
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
1052-
if (is_end() && other.is_end()) {
1010+
// Compare end iterators correctly
1011+
if (outer_ == outer_end_ && other.outer_ == other.outer_end_) {
10531012
return true;
10541013
}
10551014

1056-
// Detect illegal comparisons
1057-
assert(players_ == other.players_ &&
1058-
"Comparing iterators belonging to different container instances!");
1059-
10601015
return outer_ == other.outer_ && inner_ == other.inner_;
10611016
}
10621017

10631018
bool operator!=(const iterator &other) const { return !(*this == other); }
10641019

10651020
private:
1066-
// -------------------------------------------------------------------------
1067-
// Helpers
1068-
// -------------------------------------------------------------------------
10691021
bool is_end() const { return outer_ == outer_end_; }
10701022

1071-
bool valid_owner() const { return players_ != nullptr && owner_id_ != 0; }
1072-
1073-
// Initialize inner iter safely
10741023
void init_inner()
10751024
{
1076-
const auto &playerPtr = *outer_;
1077-
1078-
// Check for null shared_ptr (VERY COMMON SOURCE OF UB)
1079-
assert(playerPtr && "Null shared_ptr<GamePlayerRep> inside m_players!");
1080-
1081-
inner_ = playerPtr->m_infosets.begin();
1082-
inner_end_ = playerPtr->m_infosets.end();
1025+
const auto &player = *outer_;
1026+
inner_ = player->m_infosets.begin();
1027+
inner_end_ = player->m_infosets.end();
10831028
}
10841029

1085-
// Advance to next non-empty inner range
10861030
void satisfy_invariant()
10871031
{
10881032
while (!is_end() && inner_ == inner_end_) {
@@ -1093,48 +1037,34 @@ class GameRep::Infosets {
10931037
}
10941038
}
10951039

1096-
// -------------------------------------------------------------------------
1097-
// Data members
1098-
// -------------------------------------------------------------------------
1040+
// HOLD GAME ALIVE
1041+
std::shared_ptr<GameRep> game_;
1042+
10991043
outer_iter outer_{};
11001044
outer_iter outer_end_{};
11011045
inner_iter inner_{};
11021046
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;
11081047
};
11091048

1110-
// -------------------------------------------------------------------------
1111-
// View interface
1112-
// -------------------------------------------------------------------------
1113-
iterator begin() const
1049+
// Construct a view storing a strong reference to the GameRep
1050+
explicit Infosets(std::shared_ptr<GameRep> game)
1051+
: game_(std::move(game)), players_(&game_->m_players)
11141052
{
1115-
return iterator(players_->begin(), players_->end(), players_, owner_id_);
11161053
}
11171054

1118-
iterator end() const { return iterator(players_->end(), players_->end(), players_, owner_id_); }
1055+
iterator begin() const { return iterator(game_, players_->begin(), players_->end()); }
11191056

1120-
explicit Infosets(const GameRep *game) : players_(&game->m_players)
1121-
{
1122-
assert(game != nullptr);
1123-
owner_id_ = next_owner_id(); // unique ID for detecting illegal comparisons
1124-
}
1057+
iterator end() const { return iterator(game_, players_->end(), players_->end()); }
11251058

11261059
private:
1060+
std::shared_ptr<GameRep> game_;
11271061
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-
}
11351062
};
11361063

1137-
inline GameRep::Infosets GameRep::GetInfosets() const { return Infosets(this); }
1064+
inline GameRep::Infosets GameRep::GetInfosets() const
1065+
{
1066+
return Infosets(std::const_pointer_cast<GameRep>(this->shared_from_this()));
1067+
}
11381068

11391069
//=======================================================================
11401070
// Inline members of game representation classes

src/games/gametree.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,10 @@ class GameTreeRep : public GameExplicitRep {
127127
/// Returns the iset'th information set in the game (numbered globally)
128128
GameInfoset GetInfoset(int iset) const override;
129129
/// Returns the set of information sets in the game
130-
Infosets GetInfosets() const override { return Infosets(this); }
130+
Infosets GetInfosets() const override
131+
{
132+
return Infosets(std::const_pointer_cast<GameRep>(this->shared_from_this()));
133+
}
131134
/// Sort the information sets for each player in a canonical order
132135
void SortInfosets() override;
133136
/// Returns the set of actions taken by the infoset's owner before reaching this infoset

0 commit comments

Comments
 (0)