www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - problem with opIndex

reply Joseph <JE342 gmail.com> writes:
I am trying to have a multi-dimensional array and opIndex has to 
have both an arbitrary number of parameters and allow for slicing.

The problem is if I create opIndex for non-slicing, it looks like

ref auto opIndex(T...)(T index)

and this one catches all templates. But slices do not return the 
same value. I need to somehow have two opIndex's, one for slice 
handling and one for non-slices.

Trying to accomplish

arr[1..4, 5..8, 3, 3..4]

I already have the single index case arr[3, 4, 3, 1]

and the only difference is that for a slice, the indices are 
int[2]'s.

To make this work, I need to somehow know if any of the 
parameters is a slice or not.

Heres what I've got so far:

struct sMArray(F)
{
	import std.traits, std.meta, core.stdc.stdlib;
	size_t[] dimensions;		// The dimensions of the multi-array
	size_t[] offset;			// The offset of the multi-array(for slices)
	void[] data;				// An array of the actual element data in bytes
	void[] parent;				// The parent array or slice, if null, no 
parent.
	
	
	this(T...)(T args)			// Give the dimensions with one 
element(first or last) being a flag array to initialize with
     {			
		static if (isArray!(T[0]))
		{
			auto dat = T[0];
			dimensions = args[1..$];
			enum start = 1;
			enum end = T.length;

		} else
		{
			static if (isArray!(T[$-1]))
			{
				auto dat = T[$-1];
				enum start = 0;
				enum end = T.length-1;
			} else
			{
				int[] dat = null;
				enum start = 0;
				enum end = T.length;
			}				
		}


		foreach(a; args[start..end]) 		
			dimensions ~= a;

		int totalSize = 1;
		foreach(d; dimensions)
			totalSize *= d;

		data = malloc(totalSize*F.sizeof)[0..totalSize*F.sizeof];

		if (dat != null)
			for(int i = 0; i < dat.length; i++)
				(cast(typeof(dat))data)[i] = dat[i];

     }

	ref auto opIndex()
	{
		return data;
	}

	// Index a single element, e.g., data[0, 1] or takes a 
slice/view of the array and returns that as another sMArray
     ref auto opIndex(T...)(T idx)
	{ 		
		if (T.length != dimensions.length) assert(0, "sMArray invalid 
indexing!");
		size_t i = 0;
		static foreach(k, t; T)
		{
			static if (k == 0)
				i = idx[0];
			else
				i += idx[k]*dimensions[k-1];
		}

		debug { if (i >= data.length) assert(0, "sMArray invalid index! 
Too Large."); }
		return (cast(F[])data)[i];
	}

	// Support for `x..y` notation in slicing operator for the given 
dimension.
     size_t opSlice(size_t dim)(size_t start, size_t end)
		in { assert(start >= 0 && end <= this.opDollar!dim); }
     body
     {
         return [start, end];
     }

	size_t GetDim(int i)
	{
		return dimensions[i];
	}

	// Support `$` in slicing notation, e.g., arr[1..$, 0..$-1].
	 property size_t opDollar(size_t dim)() { return 
dimensions[dim]; }





	~this()
	{
		free(data.ptr);
	}



}
Sep 29 2017
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Sep 29, 2017 at 07:31:14PM +0000, Joseph via Digitalmars-d-learn wrote:
 I am trying to have a multi-dimensional array and opIndex has to have
 both an arbitrary number of parameters and allow for slicing.
 
 The problem is if I create opIndex for non-slicing, it looks like
 
 ref auto opIndex(T...)(T index)
 
 and this one catches all templates. But slices do not return the same
 value.  I need to somehow have two opIndex's, one for slice handling
 and one for non-slices.
 
 Trying to accomplish
 
 arr[1..4, 5..8, 3, 3..4]
 
 I already have the single index case arr[3, 4, 3, 1]
 
 and the only difference is that for a slice, the indices are int[2]'s.
 
 To make this work, I need to somehow know if any of the parameters is
 a slice or not.
[...] I've done this before. Basically, there are a few things you need to do: 1) Determine the type of the result. Presumably, you'll want to return the same Matrix / Array type that you started with, only with a different dimensionality, depending on how many slicing arguments you get. To do that, you need to know: a) How many of your arguments are slices b) How many are single indices. You can use a foreach loop with static if, or an analogous template, to count the number of slices. I'd go with the template solution, to ensure you don't incur unnecessary runtime overhead if you have a poor optimizer. Something like this, perhaps: template numSlices(Args...) if (Args.length > 0) { static if (Args.length == 1) { enum numSlices = is(Args[0] == int[2]) ? 1 : 0; else enum numSlices = (is(Args[0] == int[2]) ? 1 : 0) + numSlices!(Args[1 .. $]); } Then you'd construct a type of the same dimensionality as numSlices!Args as the receiving object. 2) Loop over the arguments and compute the stride information for each. (Presumably, you're constructing a slice over the original data; if you're copying the data then of course you'll do something else.) Basically something like this: foreach (arg; args) { static if (is(typeof(arg == int[2]))) { ... // compute stride corresponding to the range } else { ... // compute start offset of subdimensional slice } } T -- Don't drink and derive. Alcohol and algebra don't mix.
Sep 29 2017
prev sibling parent Ilya Yaroshenko <ilyayaroshenko gmail.com> writes:
On Friday, 29 September 2017 at 19:31:14 UTC, Joseph wrote:
 I am trying to have a multi-dimensional array and opIndex has 
 to have both an arbitrary number of parameters and allow for 
 slicing.
You may want to look into ndslice package source code [1] --Ilya [1] https://github.com/libmir/mir-algorithm/blob/master/source/mir/ndslice/slice.d
Sep 29 2017