List iterators and delete flags

Started by
5 comments, last by The Communist Duck 14 years ago
void ProcessManager::Update(const int delta)
{
	ProcessList::iterator i = processList.begin();
	ProcessList::iterator end = processList.end();
	ProcessPtr next;

	while(i != end)
	{
		ProcessPtr process(*i);

		if(process->IsDead())
		{
			next = process->GetNext();
			if(next != NULL)
			{
				ProcessPtr temp((Process*)NULL);
				process->SetNext(temp);
				this->Attach(next);
			}

			this->Detach(process);
		} 
		if(process->IsActive() && !process->IsPaused())
		{
			process->Update(delta);
		}
		i++;
	}
}

I've been looking through my copy of Game Coding Complete 3rd Ed and found the ProcessManager bit interesting. I tried implementing it, but I get the debug assert thing from VS08 saying "list iterator not incrementable".
(ProcessList = std::list<ProcessPtr>, ProcessPtr = boost::shared_ptr<Process>.)
IsDead is a bool flag set when Kill() is called. GetNext() returns a shared pointer to the next process (when it's killed; similar to a singly linked list I think-where each element points to its successor?) Detach removes it from the list. Attach adds it to the list. I've tried stepping through with debugger, and it goes:

Update
Update
Update
Update
Update
Killed
//There should be another call here to the next process.
It has one element in the list(one process I attach) until the IsDead flag is set. Then it attaches another process(list has 2 elements) and detaches the first one(list has one element). However, it then tries to increment the iterator and fails. I'm not sure how to get around this. Any help? I've understood what it's doing in the book, and mine's a pretty close copy. Thanks. :D //Here's my entire source if you need it.\Header

#ifndef PROCESS_H
#define PROCESS_H

#include <list>
#include <iostream>

#include <boost/shared_ptr.hpp>
#include <boost/utility.hpp>

class Process;
typedef boost::shared_ptr<Process> ProcessPtr;

#define WAIT 0
#define BOOM 1

class Process : boost::noncopyable
{
public:

	Process(int processType);

	bool IsDead() const { return isDead; };
	bool IsActive() const { return isActive; };
	void SetActive(const bool active) { isActive = active; };

	void SetAttached(const bool attach) { isAttached = attach; };
	bool IsAttached() const { return isAttached; };

	bool IsPaused() const { return isPaused; };
	bool IHazInit() const { return !isInitTiemNaow; };

	int GetType() const{ return type; };
	void SetType(const int set) { type = set; };

	ProcessPtr GetNext () const { return nextProcess; };
	void SetNext(ProcessPtr process) { nextProcess = process; };

	virtual void Update(const int delta);
	virtual void Init() {};
	virtual void PauseToggle() { isPaused = !isPaused; };
	virtual void Kill() { isDead = true; isActive = false; };

protected:
	int type;
	bool isActive, isDead, isPaused, isAttached, isInitTiemNaow;
	ProcessPtr nextProcess;
};

typedef std::list< ProcessPtr > ProcessList;

class ProcessManager
{
public:
	bool HasAttachments() { return !processList.empty(); };
	void Attach(ProcessPtr process);
	void Update(const int delta);

private:
	void Detach(ProcessPtr process);
	ProcessList processList;

};

class WaitProcess : public Process
{
public:
	WaitProcess(unsigned int time);
	virtual void Update(const int delta);
	virtual void Kill();

protected:
	unsigned int start, stop;
};

class BoomProcess : public Process
{
	public:
		BoomProcess():Process(BOOM) {};
	virtual void Update(const int delta);
	virtual void Kill();
};

#endif

Implementation

#include "Process.h"


Process::Process(int processType)
:type(processType),
	isActive(true), 
	isDead(false), 
	isPaused(false), 
	isAttached(false), 
	isInitTiemNaow(true)
{
}

void Process::Update(const int delta)
{
	if(isInitTiemNaow)
	{
		this->Init();
		this->isInitTiemNaow = false;
	}
}

void ProcessManager::Attach(ProcessPtr process)
{
	process->SetAttached(true);
	this->processList.push_back(process);
}

void ProcessManager::Detach(ProcessPtr process)
{
	process->SetAttached(false);
	this->processList.remove(process);
}

void ProcessManager::Update(const int delta)
{
	ProcessList::iterator i = processList.begin();
	ProcessList::iterator end = processList.end();
	ProcessPtr next;

	while(i != end)
	{
		ProcessPtr process(*i);

		if(process->IsDead())
		{
			next = process->GetNext();
			if(next != NULL)
			{
				ProcessPtr temp((Process*)NULL);
				process->SetNext(temp);
				this->Attach(next);
			}

			this->Detach(process);
		} 
		if(process->IsActive() && !process->IsPaused())
		{
			process->Update(delta);
		}
		//if(i == processList.end())
			std::cout << "foo";
		i++;
	}
}

WaitProcess::WaitProcess(unsigned int time)
:Process(WAIT),
start(0),
stop(time)
{}

void WaitProcess::Update(const int delta)
{
	if(isActive)
	{
		this->start += delta;
		std::cout << "Waiting." << std::endl;
		if(start >= stop)
			this->Kill();
	}
}

void WaitProcess::Kill()
{
	std::cout << "Stopped waiting." << std::endl;
	Process::Kill();
}

void BoomProcess::Update(const int delta)
{
	if(isActive)
	{
			this->Kill();
	}
}

void BoomProcess::Kill()
{
	std::cout << "BOOM." << std::endl;
}

And how I use it(it's an absolute for the delta time out of testing+the cin)

#include "Process.h"

int main(int argc, char* argv[])
{
	ProcessPtr boom(new BoomProcess());
	ProcessPtr wait(new WaitProcess(3000));
	ProcessManager manager;
	wait->SetNext(boom);
	manager.Attach(wait);
	while(manager.HasAttachments())
	{
	manager.Update(300);
	}
	int x;
	std::cin >> x;
	return 0;
}

Advertisement
I haven't carefully examined the code, but it looks like the processes are in two lists at the same time: the std::list, and the linked list you manage yourself. And it looks like the code in Update() removes a process from your custom list, but not from the std::list.

Is this the intended behavior (and if so, why?), or did I misunderstand the code?

Also, what line triggers the assertion?
the assertion comes on the line:
i++;
Where I try to increment the iterator but there's only one element left.

(Sorry if my code is not understandable...should've done a test case)
What is meant to happen is the ProcessManager iterates through the list of all processes.

It checks to see if the IsDead() flag is set to true. If it is, then it first checks to see if there's an object next in the chain. (You could chain a openDoor process which on destruction causes a monsterJumpOut process, etc.).
If there is, then set it to "next", clear it on the original pointer, and attach the "next" process to the list. Then remove the dead one. (the isDead flag is set in the Kill() method).

If it isn't flagged as dead, and it's both active and not paused, then call each process' Update method. These can call Kill(), but it shouldn't immediately destroy until the next iteration.

I think that sums it up...and the purpose was to "move" (in a sense) the next chained process from the custom list to the std::list.


I hope that cleared stuff up.
Thanks. :D

Try this: Change Detach() to:

ProcessList::iterator ProcessManager::Detach(ProcessPtr process){	process->SetAttached(false);	return this->processList.erase(process);}


I don't think you want to use std::list::remove().

Then change Update() like so:

void ProcessManager::Update(const int delta){	ProcessList::iterator i = processList.begin();	ProcessPtr next;	while(i != processList.end())	{		ProcessPtr process(*i);		if(process->IsDead())		{			next = process->GetNext();			if(next != NULL)			{				ProcessPtr temp((Process*)NULL);				process->SetNext(temp);				this->Attach(next);			}			i = this->Detach(process);		}                 else                        i++;		if(process->IsActive() && !process->IsPaused())		{			process->Update(delta);		}	}}


If you remove elements from the list, the return value of end() will change, so you should call it on each loop iteration.
Ok, thanks for the help...however I tried using erase earlier(and now again) but I get the following error:
error C2664: 'std::list<_Ty>::_Iterator<_Secure_validation> std::list<_Ty>::erase(std::list<_Ty>::_Const_iterator<true>)' : cannot convert parameter 1 from 'ProcessPtr' to 'std::list<_Ty>::_Const_iterator<_Secure_validation>'


I've tried google, but I can't find it clearly. Only idea I have is the fact I'm not using const_iterators or something, but I'm not sure.
Right, sorry, erase works with iterators.

So have Detach() take an iterator:

ProcessList::iterator ProcessManager::Detach(ProcessList::iterator it)

and pass i as the argument.
Thank you; all works now (or at least appears to for the simple one process->one chain I've given it).
+Rating.
:D

This topic is closed to new replies.

Advertisement