Nulling a reference to a class with opAssign ...

Mike vertex at gmx.at
Fri Dec 7 12:32:23 PST 2007


On Fri, 07 Dec 2007 21:12:44 +0100, Steven Schveighoffer  
<schveiguy at yahoo.com> wrote:

DMD 1.023.

Hmm. I've not tried the example before. Maybe it's because Foo and Bar are  
actually template classes. Here's the actual code (yeah, I'm lazy with  
comments and it's not yet finished).

-----------------------------------------------------------------------
// test code
void datacb(float value, uint id)
{
	Stdout("datacb - val: ")(value)(" id: ")(id).newline;
}
void messagecb(EDBMessage msg, uint id)
{
	Stdout("messagecb - id: ")(id).newline;
}

Stdout("testing databinding").newline;
auto testsource = new CDataSource!(float);
auto testsink1 = new CDataSink!(float)(testsource, &datacb, &messagecb, 1);
	
testsource = 1.;
-----------------------------------------------------------------------
// the actual code
module tools.databinding;

enum EDBMessage
{
	sourceDeleted,
	sourceUpdated,
}

enum EDBForwardMode
{
	push,
	poll,
}

enum EDBAccessMode
{
	read,
	full,
}

class CDataSource(T)
{
	public this(EDBForwardMode forward = EDBForwardMode.push, EDBAccessMode  
access = EDBAccessMode.read)
	{
		myForwarding = forward;
		myAccess = access;
	}
	
	public ~this()
	{
		// send every subscriber the deleted message
		foreach (item; mySubscribers)
		{
			if (item !is null) item.message(EDBMessage.sourceDeleted);
		}
	}
	
	public void subscribe(CDataSink!(T) datasink)
	{
		// search if this sink is already subscribed
		foreach (item; mySubscribers)
		{ if (item !is null && item is datasink) return; }
		
		// search for a deleted subscriber
		foreach (item; mySubscribers)
		{
			if (item is null)
			{
				item = datasink;
				mySubscriberCount++;
				return;
			}
		}
			
		// add at end of list
		mySubscribers ~= datasink;
		mySubscriberCount++;
	}
	
	public void unSubscribe(CDataSink!(T) datasink)
	{
		foreach (item; mySubscribers)
		{
			if (item !is null && item is datasink)
			{
				item = null;
				mySubscriberCount--;
				return;
			}
		}
	}
	
	public T opAssign(T value)
	{
		myValue = value;
		if (mySubscriberCount == 0) return value;
		foreach (item; mySubscribers)
		{
			if (item !is null)
			{
				if (myForwarding == EDBForwardMode.push)
					item.update(value);
				else
					item.message(EDBMessage.sourceUpdated);
			}
		}
		return myValue;
	}
	
	public T opCall()
	{ return myValue; }
	
	public long subscriberCount()
	{ return mySubscriberCount; }
	
	private T myValue;
	private CDataSink!(T)[] mySubscribers;
	private long mySubscriberCount;
	private EDBForwardMode myForwarding;
	private EDBAccessMode myAccess;
}

class CDataSink(T)
{
	public this(CDataSource!(T) source, void delegate(T, uint) valuecb, void  
delegate(EDBMessage, uint) messagecb, uint id)
	{
		myValueCallback = valuecb;
		myMessageCallback = messagecb;
		myId = id;
		subscribe(source);
	}
	
	public ~this()
	{
		if (mySource !is null) mySource.unSubscribe(this);
	}
	
	public void subscribe(CDataSource!(T) source)
	{
		if (mySource !is null) mySource.unSubscribe(this);
		if (source !is null)
		{
			mySource = source;
			mySource.subscribe(this);
		}
	}
	
	public T opCall()
	{ return mySource.myValue; }
	
	public T opAssign(T value)
	{
		if (mySource.myAccess == EDBAccessMode.full) return mySource = value;
		throw new Exception("Databinding: Datasource is write protected.");
	}
	
	public EDBForwardMode forwarding()
	{ return mySource.myForwarding; }
	
	public EDBAccessMode access()
	{ return mySource.myAccess; }
	
	private void update(T value)
	{
		if (myValueCallback !is null) myValueCallback(value, myId);
	}
	
	private void message(EDBMessage message)
	{
		if (message == EDBMessage.sourceDeleted) mySource = null;
		if (myMessageCallback !is null) myMessageCallback(message, myId);
	}
	
	private CDataSource!(T) mySource;
	private void delegate(T, uint) myValueCallback;
	private void delegate(EDBMessage, uint) myMessageCallback;
	private uint myId;
}
----------------------------------------------------------------------

> Your exact code compiles for me (dmd 1.0.23)...  You sure you are using  
> the
> latest dmd?
>
> One observation, your destructor in Foo should not be accessing barRef
> because the garbage collector cannot guarantee that the barRef instance
> hasn't been collected.  If you are in the destructor, there is no reason  
> to
> clean up GC references anyways, as the GC will handle that.
>
> -Steve
>
> "Mike" wrote in message
> Erm ... this totally catched me off guard ...
>
> class Foo()
> {
>      Bar barRef;
>      ~this() { barRef.noMoreFoo(); }
>      void opAssign(float f);
> }
>
> class Bar()
> {
>      Foo fooRef;
>      void setFoo(Foo foo) { fooRef = foo; }
>      void noMoreFoo()     { fooRef = null; } // error: can't cast void*  
> to
> float ...
> }
>
> ?
>
> -Mike
>



-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/


More information about the Digitalmars-d-learn mailing list