Initalizing complex array types or some other problem ;/

Prudence via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Tue Sep 15 18:46:07 PDT 2015


On Tuesday, 15 September 2015 at 20:54:49 UTC, anonymous wrote:
> On Tuesday 15 September 2015 22:09, Prudence wrote:
>
>> The code below doesn't work.
>
> Please be more specific in how it doesn't work. Mention the 
> error message if there is one, or say how the code behaves 
> differently from what you'd expect.
>
> Trying to compile the code (after kicking getch out), I get 
> this error:
> core.exception.RangeError at test.d(103): Range violation
>
> Line 103 is: writeln(MyStore.Store[k].length);
>
> I can't find where you set Store[k]. Maybe you forgot that or 
> deleted it accidentally?
>
> I made a guess and added this line in SingleStore.New:
> o.Store[k] ~= tuple(v, o);
>
> It still throws a range error with this. That's because 
> associative arrays are a little weird, unfortunately.
>
> An AA is effectively initialized on the first assignment. So 
> (with my addition) the first SingleStore.New call initializes 
> the AA. But it only initializes o.Store, not the original 
> variable s (i.e. ObjectStore.Store). s is left empty.
>
> Possible solutions/workarounds:
> * Append to s[k], then assign s to o.Store.
> * Initialize ObjectStore.Store in a static constructor:
> ----
>     static this()
>     {
>         Store[TKey.init] = [];
>         Store.remove(TKey.init);
>     }

Maybe. Seems to work without it. The code below should compile on 
your system and work(and prints 1 2 3 4, 4 3 2 1). The getch is 
required on windows if I want to see the output, so I don't know 
why you even bothered mention replacing it.





import std.stdio;
import std.concurrency;



extern (C) int getch();
import std.string;
import std.concurrency;
import core.time;
import core.thread;
import std.container.array;
import std.typecons;







public class SingleStore(TKey, TValue)
{
	public TValue Value;
	public TKey Key;	
	public Tuple!(TValue, SingleStore!(TKey, TValue))[][TKey] Store;


	// Duplicate entries will be removed together as there is no way 
to distinguish them
	public auto Remove()
	{
		import std.algorithm;
		if (Value == null || Key == null) return;
		int count = 0;
		for(int i = 0; i < Store[Key].length; i++)
		{
			if (Store[Key][i][0] == Value && Store[Key][i][1] == this)
			{
					count++;
					Store[Key][i] = tuple(null, null); // Set to null to release 
any references if necessary
					swap(Store[Key][i], Store[Key][max(0, Store[Key].length - 
count)]);
					i = i - 1;
			}

		}
		if (count == 1 && Store[Key].length == 1)
		{
			Store[Key] = null;
			Store.remove(Key);
		} else
			//Store[Key] = Store[Key][0..max(0,Store[Key].length-count)];
			Store[Key].length = Store[Key].length - count;

		Value = null;
		Key = null;

	}

	public static auto New(TKey k, TValue v, ref Tuple!(TValue, 
SingleStore!(TKey, TValue))[][TKey] s)
	{
		auto o = new SingleStore!(TKey, TValue)();	// GC
		o.Key = k;
		o.Value = v;
		s[k] ~= tuple(v, o);
		o.Store = s;

		return o;
	}

}


// Creates a static Associative Array that stores multiple values 
per key. The object returned by New can then be used to remove 
the key/value without having to remember specifically them.
public mixin template ObjectStore(TKey, TValue)
{
	// The object store. It is static. Mixin the template into it's 
different types to create different types of stores. All objects 
of that type are then in the same store.
	public static Tuple!(TValue, SingleStore!(TKey, TValue))[][TKey] 
Store;

	public static auto New(TKey k, TValue v)
	{
		auto r =  SingleStore!(TKey, TValue).New(k, v, Store);
		return r;
	}

}

//alias dg = int delegate(int);
alias dg = string;

public class cMyStore(TKey, TValue)
{
	//mixin ObjectStore!(string, dg);
	mixin ObjectStore!(string, string);	
}



void main()
{
	alias MyStore = cMyStore!(string, string);
	auto k = "x";

	auto r = &MyStore.Store;

	/*
	dg d1 = (int x) { return x; };
	dg d2 = (int x) { return x; };
	dg d3 = d1;
	dg d4 = (int x) { return 3*x; };
	*/

	dg d1 = "a1";
	dg d2 = "a2";
	dg d3 = "a3";
	dg d4 = "a4";
	

	
	auto s = MyStore.New(k, d1);
	writeln(MyStore.Store[k].length);
	auto s1 = MyStore.New(k, d2);
	writeln(MyStore.Store[k].length);
	auto s2 = MyStore.New(k, d3);
	writeln(MyStore.Store[k].length);
	auto s3 = MyStore.New(k, d4);
	writeln(MyStore.Store[k].length);	
	

	//auto x = MyStore.Store[k][0](3);
	//writeln("-" ~ x);

	s1.Remove();
	writeln(MyStore.Store[k].length);
	s2.Remove();
	writeln(MyStore.Store[k].length);
	s.Remove();
	writeln(MyStore.Store[k].length);
	s3.Remove();
	


	getch();
}

I went and cleaned it up and somehow the issue miraculously 
resolved itself. It must have been from using the static this and 
such. The code I pasted had a partial changes trying to get it to 
work in the first place.

>
> Aside: I have no idea what you're doing there, but it looks 
> pretty complicated, to the point that I'd guess that it's more 
> complicated than necessary.

In any case, Maybe you are not as smart as you think you are if 
you can't understand it? Maybe next time you shouldn't assume you 
are the oracle of all knowledge and if you can't understand it 
then it's bad/wrong.

In fact, it's quite simple to understand for anyone with half a 
brain.

Seriously. Don't guess. It's not that complex and it would take 
you 5 mins to understand if you wanted. No reason to try and 
through your ego in it.

1. AA's can't have multiple values added to a single key(except 
with tuple, which becomes bulky to use). Hence the ObjectStore is 
a store(a dictionary/AA) that allows one to store multiple values 
per key.

e.g.,

Store["x"] ~= "y";
Store["x"] ~= "z";
Store["x"] ~= "q";


2. Because we might store things like delegates/lambda's in the 
ObjectStore, or other elements(pointers), the values in the store 
under some key may not be unique.

alias d = {int x} { return x; };
Store["x"] ~= d;
Store["x"] ~= d;
Store["x"] ~= d;

Here we have 3 copies of the same element, but the Store allows 
non-unique values.

Also, storing a delegate(or any value) requires us to "remember" 
it if we ever want to remove it from the store(else how could we 
know which one to remove?).

3. Hence, SingleStore wraps the value and in a Singleton(more or 
less). When a SingleStore is created(should never be by a user), 
it keeps the key, the value inside it(so we don't have to). The 
user then keeps around the SingeStore(much easier than a 
delegate) and can remove it from the ObjectStore 
easily(s.remove()). It is sort of like a momento.

4. But because we have to worry about uniqueness, we store the 
values as tuples, the actual value and the SingleStore the value 
is associated with. Since there should be a one to one 
correspondence. If the value is not unique the SingleStore will 
be, which we use as a way to disambiguate values that look the 
same.

e.g., what's kinda going on is

Store["x"] ~= tuple(d, uniqueId1);
Store["x"] ~= tuple(d, uniqueId2);
Store["x"] ~= tuple(d, uniqueId3);

Now, regardless if d is unique, the uniqueId's are unique(as they 
are just the ptr's to the different SingleStores that are 
created... and they all have different addresses).

5. So, as you can see, it's pretty easy. ObjectStore is just a 
store of objects by a key. It allows for multiple values and each 
value is unique. It also makes removing the values easy(not as 
easy for simple types but easier for complex types like 
delegates).

6. Sure there maybe a better way. That isn't the point. I've done 
this several times before in .NET and it takes about 5 mins. In D 
it has taken 2 days or so(well, only a few hours of work but 
still way to long). Partly because D's error messages are crap, 
Partly because I'm just getting back into D, and partly because 
Visual D doesn't show those static variables which makes it hard 
to know what's going on when debugging(since the ObjectStore is 
static, VD won't display whats going on with it).

I'm still unsure if the code is safe(I'll have to add checks and 
all that) as I do not allocate the value array(every value in the 
AA is really an [] of tuples), yet I don't allocate anything(not 
the AA, not the [], or the tuple). Seems to work, maybe cause I'm 
doing s[k] ~= tuple(v, o); and ~ does the allocation for [] just 
like it does for the AA array?

The reason for the mixin should be obvious. It allows one to 
create different ObjectStores(since static AA's are used, we have 
to use them in different classes. In heritence would just create 
one global store, which is probably not what one wants. Obviously 
there are other ways but I like the one line code to create a new 
store(e.g., how MyStore looks).









More information about the Digitalmars-d-learn mailing list