Peter Dimov wrote:
Why do you want to test w1 without also doing something with it?
But why do you need to compare weak_ptrs for that?
Perhaps this is a little too direct, but maybe my code can speak for itself to answer those questions and others from your reply. I mentioned earlier I was "converting" my network library from straight pointers to shared_ptr. The code here is from a network pong game example. The code made sense with pointers, but your comments suggest I may be misunderstanding the intent of the smart pointers. Previously I wrote this listener using PongClient* (which is a ConnectionListener). Now ConnectionListeners are managed by shared_ptr. I'm still undecided whether I should try to use shared_ptr for EVERYTHING in my game (the network code is but the game logic such as Player is not shared_ptr managed). The CV (ConditionVariable) has semantics that match pthread_cond with an associated mutex. The Lock* objects are RAII-style locks, I think they are most like scoped_lock from boost. The two "on" methods are event handlers, and getNewConnectionParams is an event called when a new connection has arrived but has not been negotiated. waitForPlayer is being called by the "main" thread in the server.
class OurListener : public ServerConnectionListener { public: typedef SmartPtr<OurListener> sptr; typedef WeakPtr<OurListener> wptr;
protected: OurListener(Player* RemotePlayer, Player* LocalPlayer) : remotePlayer(RemotePlayer), localPlayer(LocalPlayer), accept(true) { }
public: /** * This listener takes the params it needs to pass them onto the PongClient * to set up the game. */ static sptr create( Player* RemotePlayer, Player* LocalPlayer ) { sptr ret( new OurListener( RemotePlayer, LocalPlayer ) ); ret->setThisPointer( ret ); return ret; }
virtual ~OurListener() {}
void onListenFailure(const Error& error, const Address& from, const ConnectionListener::sptr& listener) { LockCV lock(sync);
if (listener == connecting.lock()) { //Only display an error for our real player. We don't want to see the //ConnectionRefused errors. LockObject lock( gout ); gout << "Connection error: " << error << endl; gout << " Error received from " << from << endl; connecting.reset(); }
//If waitForPlayer is waiting for the connection, wake it up. sync.broadcast(); }
void onListenSuccess( const ConnectionListener::sptr& listener ) { LockCV lock(sync);
player = connecting; connecting.reset(); accept = false;
//If waitForPlayer is waiting for the connection, wake it up. sync.broadcast(); }
void getNewConnectionParams(ConnectionParams& params) { LockCV lock(sync);
params.setUnrel(false); if (accept && !(connecting.lock()) ) { //If no one is connecting and we are accepting connections PongClient::sptr temp = PongClient::create( remotePlayer, localPlayer ); connecting = temp; params.setListener( temp ); } else { params.setListener( RefuseClient::create() ); } }
//waitForPlayer returns the connected player, or NULL if the connection //process was aborted by the user. PongClient::sptr waitForPlayer() { LockCV lock(sync);
while (!(player.lock()) && !kbhit()) { //We wait for 250ms to recheck kbhit for pressed keys. sync.timedWait(250); } if (!(player.lock())) { //We were woken up by a keypress, so refuse any further connections. accept = false;
//We don't need to wait around if anyone is in the middle of connecting, //because shutting down GNE will close any open connections, and we will //be closing down if we aborted this connection.
//Return that no client connected. return PongClient::sptr(); }
return player.lock(); }
private: Player* remotePlayer; Player* localPlayer;
//This variable will be non-null when there is a player, so we refuse any //other incoming connections. Weak pointer used because we don't want to //keep player alive after connect PongClient::wptr player;
//player will be stored here while he is connecting, then moved to player //when the connection was successful. Weak pointer used because this //variable is only temporary and we don't want to keep anything alive. PongClient::wptr connecting;
//If this is false, then the user canceled the connection process, we we //shouldn't even accept the first player. bool accept;
//We use a CV because getNewConnectionParams might be called from //different threads, and we want to make sure only one client connects at a //time. This protects the state of player. //The CV is notified when a new player arrives. ConditionVariable sync; };
Jason