Skip to content

Commit 943292d

Browse files
committed
Separate validation from memory management for actions.
1 parent 8d1989d commit 943292d

File tree

5 files changed

+80
-24
lines changed

5 files changed

+80
-24
lines changed

src/games/game.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#ifndef LIBGAMBIT_GAME_H
2424
#define LIBGAMBIT_GAME_H
2525

26+
#include <iostream>
2627
#include <list>
2728
#include <set>
2829

@@ -38,7 +39,7 @@ class GameOutcomeRep;
3839
using GameOutcome = GameObjectPtr<GameOutcomeRep>;
3940

4041
class GameActionRep;
41-
using GameAction = GameObjectPtr<GameActionRep>;
42+
using GameAction = GameObjectSharedPtr<GameActionRep>;
4243

4344
class GameInfosetRep;
4445
using GameInfoset = GameObjectPtr<GameInfosetRep>;
@@ -148,7 +149,7 @@ class GameOutcomeRep : public GameObject {
148149
};
149150

150151
/// An action at an information set in an extensive game
151-
class GameActionRep : public GameObject {
152+
class GameActionRep : public GameValidObject, public std::enable_shared_from_this<GameActionRep> {
152153
friend class GameTreeRep;
153154
friend class GameInfosetRep;
154155
template <class T> friend class MixedBehaviorProfile;
@@ -157,13 +158,13 @@ class GameActionRep : public GameObject {
157158
std::string m_label;
158159
GameInfosetRep *m_infoset;
159160

161+
public:
160162
GameActionRep(int p_number, const std::string &p_label, GameInfosetRep *p_infoset)
161163
: m_number(p_number), m_label(p_label), m_infoset(p_infoset)
162164
{
163165
}
164-
~GameActionRep() override = default;
166+
~GameActionRep() = default;
165167

166-
public:
167168
int GetNumber() const { return m_number; }
168169
GameInfoset GetInfoset() const;
169170

@@ -184,7 +185,7 @@ class GameInfosetRep : public GameObject {
184185
int m_number;
185186
std::string m_label;
186187
GamePlayerRep *m_player;
187-
Array<GameActionRep *> m_actions;
188+
Array<std::shared_ptr<GameActionRep>> m_actions;
188189
std::vector<GameNodeRep *> m_members;
189190
int flag{0}, whichbranch{0};
190191
Array<Number> m_probs;
@@ -195,7 +196,7 @@ class GameInfosetRep : public GameObject {
195196
void RenumberActions()
196197
{
197198
std::for_each(m_actions.begin(), m_actions.end(),
198-
[act = 1](GameActionRep *a) mutable { a->m_number = act++; });
199+
[act = 1](std::shared_ptr<GameActionRep> a) mutable { a->m_number = act++; });
199200
}
200201

201202
public:

src/games/gameobject.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,18 @@ class GameObject {
8484
//@}
8585
};
8686

87+
class GameValidObject {
88+
protected:
89+
bool m_valid{true};
90+
91+
public:
92+
GameValidObject() = default;
93+
~GameValidObject() = default;
94+
95+
bool IsValid() const { return m_valid; }
96+
void Invalidate() { m_valid = false; }
97+
};
98+
8799
class BaseGameRep {
88100
protected:
89101
int m_refCount{0};
@@ -207,6 +219,43 @@ template <class T> class GameObjectPtr {
207219
bool operator!() const { return !rep; }
208220
};
209221

222+
template <class T> class GameObjectSharedPtr {
223+
private:
224+
std::shared_ptr<T> m_rep;
225+
226+
public:
227+
GameObjectSharedPtr() = default;
228+
GameObjectSharedPtr(std::nullptr_t r) : m_rep(r) {}
229+
GameObjectSharedPtr(std::shared_ptr<T> r) : m_rep(r) {}
230+
GameObjectSharedPtr(const GameObjectSharedPtr<T> &) = default;
231+
~GameObjectSharedPtr() = default;
232+
233+
GameObjectSharedPtr<T> &operator=(const GameObjectSharedPtr<T> &) = default;
234+
235+
std::shared_ptr<T> operator->() const
236+
{
237+
if (!m_rep) {
238+
throw NullException();
239+
}
240+
if (!m_rep->IsValid()) {
241+
throw InvalidObjectException();
242+
}
243+
return m_rep;
244+
}
245+
246+
bool operator==(const GameObjectSharedPtr<T> &r) const { return (m_rep == r.m_rep); }
247+
bool operator==(const std::shared_ptr<T> r) const { return (m_rep == r); }
248+
bool operator==(const std::shared_ptr<const T> r) const { return (m_rep == r); }
249+
bool operator==(const std::nullptr_t) const { return !bool(m_rep); }
250+
bool operator!=(const GameObjectSharedPtr<T> &r) const { return (m_rep != r.m_rep); }
251+
bool operator!=(const std::shared_ptr<T> r) const { return (m_rep != r); }
252+
bool operator!=(const std::shared_ptr<const T> r) const { return (m_rep != r); }
253+
bool operator<(const GameObjectSharedPtr<T> &r) const { return (m_rep < r.m_rep); }
254+
255+
operator bool() const noexcept { return bool(m_rep); }
256+
operator std::shared_ptr<T>() const { return m_rep; }
257+
};
258+
210259
} // end namespace Gambit
211260

212261
#endif // GAMBIT_GAMES_GAMEOBJECT_H

src/games/gametree.cc

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ bool GameActionRep::Precedes(const GameNode &n) const
102102
GameNode node = n;
103103

104104
while (node != node->GetGame()->GetRoot()) {
105-
if (node->GetPriorAction() == GameAction(const_cast<GameActionRep *>(this))) {
105+
if (node->GetPriorAction() == shared_from_this()) {
106106
return true;
107107
}
108108
else {
@@ -114,7 +114,7 @@ bool GameActionRep::Precedes(const GameNode &n) const
114114

115115
void GameTreeRep::DeleteAction(GameAction p_action)
116116
{
117-
GameActionRep *action = p_action;
117+
const std::shared_ptr<GameActionRep> action = p_action;
118118
auto *infoset = action->m_infoset;
119119
if (infoset->m_game != this) {
120120
throw MismatchException();
@@ -155,8 +155,9 @@ GameInfosetRep::GameInfosetRep(GameRep *p_efg, int p_number, GamePlayerRep *p_pl
155155
int p_actions)
156156
: m_game(p_efg), m_number(p_number), m_player(p_player), m_actions(p_actions)
157157
{
158-
std::generate(m_actions.begin(), m_actions.end(),
159-
[this, i = 1]() mutable { return new GameActionRep(i++, "", this); });
158+
std::generate(m_actions.begin(), m_actions.end(), [this, i = 1]() mutable {
159+
return std::make_shared<GameActionRep>(i++, "", this);
160+
});
160161
if (p_player->IsChance()) {
161162
m_probs = Array<Number>(m_actions.size());
162163
std::fill(m_probs.begin(), m_probs.end(), Rational(1, m_actions.size()));
@@ -166,7 +167,8 @@ GameInfosetRep::GameInfosetRep(GameRep *p_efg, int p_number, GamePlayerRep *p_pl
166167

167168
GameInfosetRep::~GameInfosetRep()
168169
{
169-
std::for_each(m_actions.begin(), m_actions.end(), [](GameActionRep *a) { a->Invalidate(); });
170+
std::for_each(m_actions.begin(), m_actions.end(),
171+
[](std::shared_ptr<GameActionRep> a) { a->Invalidate(); });
170172
}
171173

172174
Game GameInfosetRep::GetGame() const { return m_game; }
@@ -225,11 +227,11 @@ GameAction GameTreeRep::InsertAction(GameInfoset p_infoset, GameAction p_action
225227
IncrementVersion();
226228
int where = p_infoset->m_actions.size() + 1;
227229
if (p_action) {
228-
for (where = 1; p_infoset->m_actions[where] != p_action; where++)
230+
for (where = 1; p_action != p_infoset->m_actions[where]; where++)
229231
;
230232
}
231233

232-
auto *action = new GameActionRep(where, "", p_infoset);
234+
auto action = std::make_shared<GameActionRep>(where, "", p_infoset);
233235
p_infoset->m_actions.insert(std::next(p_infoset->m_actions.cbegin(), where - 1), action);
234236
if (p_infoset->m_player->IsChance()) {
235237
p_infoset->m_probs.insert(std::next(p_infoset->m_probs.cbegin(), where - 1), Number());
@@ -627,7 +629,7 @@ GameInfoset GameTreeRep::AppendMove(GameNode p_node, GameInfoset p_infoset)
627629
node->m_infoset = p_infoset;
628630
node->m_infoset->m_members.push_back(node);
629631
std::for_each(node->m_infoset->m_actions.begin(), node->m_infoset->m_actions.end(),
630-
[this, node](const GameActionRep *) {
632+
[this, node](std::shared_ptr<GameActionRep>) {
631633
node->m_children.push_back(new GameNodeRep(this, node));
632634
m_numNodes++;
633635
});
@@ -673,7 +675,8 @@ GameInfoset GameTreeRep::InsertMove(GameNode p_node, GameInfoset p_infoset)
673675
node->m_parent = newNode;
674676
newNode->m_children.push_back(node);
675677
std::for_each(std::next(newNode->m_infoset->m_actions.begin()),
676-
newNode->m_infoset->m_actions.end(), [this, newNode](const GameActionRep *) {
678+
newNode->m_infoset->m_actions.end(),
679+
[this, newNode](std::shared_ptr<GameActionRep>) {
677680
newNode->m_children.push_back(new GameNodeRep(this, newNode));
678681
});
679682

src/pygambit/action.pxi

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
# along with this program; if not, write to the Free Software
2020
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2121
#
22+
import cython
23+
from cython.operator cimport dereference
24+
2225

2326
@cython.cclass
2427
class Action:
@@ -48,14 +51,14 @@ class Action:
4851
)
4952

5053
def __hash__(self) -> int:
51-
return cython.cast(cython.long, self.action.deref())
54+
return cython.cast(cython.long, self.action.deref().get())
5255

5356
@property
5457
def number(self) -> int:
5558
"""Returns the number of the action at its information set.
5659
Actions are numbered starting with 0.
5760
"""
58-
return self.action.deref().GetNumber() - 1
61+
return dereference(self.action.deref()).GetNumber() - 1
5962

6063
def precedes(self, node: Node) -> bool:
6164
"""Returns whether `node` precedes this action in the
@@ -68,21 +71,21 @@ class Action:
6871
"""
6972
if self.infoset.game != node.game:
7073
raise MismatchError("precedes() requires a node from the same game as the action")
71-
return self.action.deref().Precedes(cython.cast(Node, node).node)
74+
return dereference(self.action.deref()).Precedes(cython.cast(Node, node).node)
7275

7376
@property
7477
def label(self) -> str:
7578
"""Get or set the text label of the action."""
76-
return self.action.deref().GetLabel().decode("ascii")
79+
return dereference(self.action.deref()).GetLabel().decode("ascii")
7780

7881
@label.setter
7982
def label(self, value: str) -> None:
80-
self.action.deref().SetLabel(value.encode("ascii"))
83+
dereference(self.action.deref()).SetLabel(value.encode("ascii"))
8184

8285
@property
8386
def infoset(self) -> Infoset:
8487
"""Get the information set to which the action belongs."""
85-
return Infoset.wrap(self.action.deref().GetInfoset())
88+
return Infoset.wrap(dereference(self.action.deref()).GetInfoset())
8689

8790
@property
8891
def prob(self) -> typing.Union[decimal.Decimal, Rational]:
@@ -100,7 +103,7 @@ class Action:
100103
)
101104
py_string = cython.cast(
102105
string,
103-
self.action.deref().GetInfoset().deref().GetActionProb(self.action)
106+
dereference(self.action.deref()).GetInfoset().deref().GetActionProb(self.action)
104107
)
105108
if "." in py_string.decode("ascii"):
106109
return decimal.Decimal(py_string.decode("ascii"))

src/pygambit/gambit.pxd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ cdef extern from "games/game.h":
7171
bool operator !=(c_GameNode) except +
7272
c_GameNodeRep *deref "operator->"() except +RuntimeError
7373

74-
cdef cppclass c_GameAction "GameObjectPtr<GameActionRep>":
74+
cdef cppclass c_GameAction "GameObjectSharedPtr<GameActionRep>":
7575
bool operator !=(c_GameAction) except +
76-
c_GameActionRep *deref "operator->"() except +RuntimeError
76+
shared_ptr[c_GameActionRep] deref "operator->"() except +RuntimeError
7777

7878
cdef cppclass c_GameInfoset "GameObjectPtr<GameInfosetRep>":
7979
bool operator !=(c_GameInfoset) except +

0 commit comments

Comments
 (0)