Compiler bug? (Heavy template use)

Started by
8 comments, last by l0calh05t 17 years ago
Ok, I wrote the following "little" template today for dynamically allocating contiguous N-dimensional arrays, but for some reason the program crashes when new[] is called in the constructors of the specializations for 1- and 2-dimensional arrays (3+ works fine, dunno about the efficiency, I guess it depends on the compiler). Do you think this is a compiler bug, or am I overlooking something? Of course i could always alternatively use a variadic operator() for item selection, but the usage syntax wouldn't be quite as nice anymore.

/*
	A C++ template for N-dimensional, contiguous, dynamically allocated arrays

	-----------------------------------------------------------------------------

	Copyright (C) 2007, Johannes Sebastian Mueller-Roemer,
	All rights reserved.                          

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions
	are met:

		1. Redistributions of source code must retain the above copyright
		notice, this list of conditions and the following disclaimer.

		2. Redistributions in binary form must reproduce the above copyright
		notice, this list of conditions and the following disclaimer in the
		documentation and/or other materials provided with the distribution.

		3. The names of its contributors may not be used to endorse or promote 
		products derived from this software without specific prior written 
		permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
	CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

	-----------------------------------------------------------------------------

	Usage example:

	// Create a 4-dimensional array with dimension sizes of 10,4,4,3
	// You must provide the correct number of sizes!
	arrayNd<int,4> array(10,4,4,3);

	// Usage is similar to Java arrays
	for(int i = 0; i< array.length(); i++)
		for(int j = 0; j < array.length(); j++)
			for(int k = 0; k < array[j].length(); k++)
				for(int l = 0; l < array[j][k].length(); l++)
				{
					ndarray2[j][k][l] = 1000*i+100*j+10*k+l;
				}

	BUGS/TODO List:
		*	Minimum N is currently 3, as the specializations
			crash when calling new (Reason unknown to me,
			this happens with MSVC 7.1, I have yet to check
			other compilers)
		*	Add operator and selectors for const arrays
		*	Maybe, just maybe, make it conform to the STL
			containers, so that container algorithms can
			be used (requires resizeability though IIRC)

	Feedback is welcome,
	send your comments, suggestions or enhancements to the code to:
	j (dot) s (dot) mueller (hyphen) roemer (at) gmx (dot) net
	(sorry for the "obfuscation", I'm a little paranoid about spambots)

	Version history:
		*	Version 0.1
			The very first version, which is what you are look at right now
			Written and released 03/21/2007
*/

#ifndef JSMR_ARRAYND_H
#define JSMR_ARRAYND_H

#include <cassert>
#include <cstdarg>

template<class T, int N> struct arrayNd
{
	arrayNd() {assert(0);}
	arrayNd(int first,...)
	{
		int itemCount = first;
		dim[0] = first;
		va_list vl;
		va_start(vl,first);
		for(int i = 1; i < N; i++)
		{
			dim = va_arg(vl,int);
			itemCount *= dim;
		}
		va_end(vl);
		data = new T[itemCount];
	}
	arrayNd(const arrayNd<T,N>& o)
	{
		int itemCount = o.dim[0];
		dim[0] = o.dim[0];
		for(int i = 1; i < N; i++)
		{
			dim = dimensions;
			itemCount *= dimensions;
		}
		data = new T[itemCount];
		for(int i = 0; i < itemCount; i++)
		{
			data = o.data;
		}
	}
	~arrayNd() { delete[] data; }

	template<int A, int B> struct selector
	{
		friend selector<A+1,B-1>;

		int length() const {return dimPtr;}

		selector<A-1,B+1> operator[] (int i)
		{
			assert(i>=0&&i<dimPtr);
			return selector<A-1,B+1>(data,dimPtr,i+offset*dimPtr);
		}

	private:
		selector() {assert(0);}
		selector(T* d,int* dim,int o) {data=d;offset=o;dimPtr=dim;}
		T* data;
		int* dimPtr;
		int offset;
	};

	template<> struct selector<N-1,1>
	{
		friend arrayNd<T,N>;

		int length() const {return dimPtr[1];}

		selector<N-2,2> operator[] (int i)
		{
			assert(i>=0&&i<dimPtr[1]);
			return selector<N-2,2>(data,dimPtr,i+offset*dimPtr[1]);
		}

	private:
		selector() {assert(0);}
		selector(T* d,int* dim,int o) {data=d;offset=o;dimPtr=dim;}
		T* data;
		int* dimPtr;
		int offset;
	};

	template<> struct selector<1,N-1>
	{
		friend selector<2,N-2>;

		int length() const {return dimPtr[N-1];}

		T& operator[] (int i)
		{
			assert(i>=0&&i<dimPtr[N-1]);
			return data[i+offset*dimPtr[N-1]];
		}

	private:
		selector() {assert(0);}
		selector(T* d,int* dim,int o) {data=d;offset=o;dimPtr=dim;}
		T* data;
		int* dimPtr;
		int offset;
	};

	selector<N-1,1> operator[] (int i)
	{
		assert(i>=0&&i<dim[0]);
		return selector<N-1,1>(data,dim,i);
	}

	int length() const { return dim[0]; }

private:
	T* data;
	int dim[N];
};

template<class T> struct arrayNd<T,2>
{
	arrayNd() {assert(0);}
	arrayNd(int x, int y)
	{
		dim[0] = x;
		dim[1] = y;
		data = new T[x*y];
	}
	arrayNd(const arrayNd<T,2>& o)
	{
		int itemCount = o.dim[0]*o.dim[1];
		dim[0] = o.dim[0];
		dim[1] = o.dim[1];
		data = new T[itemCount];
		for(int i = 0; i < itemCount; i++)
		{
			data = o.data;
		}
	}
	~arrayNd() { delete[] data; }

	struct selector
	{
		friend arrayNd<T,2>;

		int length() {return len;}

		T& operator[] (int i)
		{
			assert(i>=0&&i<len);
			return data[i+offset];
		}

	private:
		selector() {assert(0);}
		selector(T* d,int l,int o) {data=d;offset=o;len=l;}
		T* data;
		int len;
		int offset;
	};

	selector operator[] (int i)
	{
		assert(i>=0&&i<dim[0]);
		return selector(data,dim[1],i*dim[0]);
	}

	int length() const { return dim[0]; }

private:
	T* data;
	int dim[2];
};

template<class T> struct arrayNd<T,1>
{
	arrayNd() {assert(0);}
	arrayNd(int x)
	{
		len = x;
		data = new T[x];
	}
	arrayNd(const arrayNd<T,1>& o)
	{
		len = o.len;
		data = new T[o.len];
		for(int i = 0; i < o.len; i++)
		{
			data = o.data;
		}
	}
	~arrayNd() { delete[] data; }

	T& operator[] (int i)
	{
		assert(i>=0&&i<len);
		return data;
	}

	int length() const { return len; }

private:
	T* data;
	int len;
};

#endif



[Edited by - l0calh05t on March 24, 2007 12:09:18 PM]
Advertisement
  • Please reformat your post using [source] tags.

  • You have not provided a test case showing the behaviour you are reporting. I was unable to replicate your problem with a simple test case of my own.

  • You are missing copy-assignment operators.

  • friend struct declarations must include the struct keyword.

  • I do not believe it is valid to specialise a member class template within the enclosing class definition. Certainly two of my three compilers complain about it.

  • C++ Coding Standards by Herb Sutter and Andrei Alexandrescu. Item 98: Don't use varargs (ellipsis).

  • Boost.MultiArray.
Σnigma
I knew there had to be a better tag than code :-D thanks for pointing that out

my testcase was simply the following:

#include <iostream>#include "arraynd.h"int main{    arrayNd<int,2> ndarray(5,5);    for(int i = 0; i < ndarray.length(); i++)        for(int j = 0; j < ndarray.length(); j++)            ndarray[j] = 10*i+j;    for(int i = 0; i < ndarray.length(); i++)        for(int j = 0; j < ndarray.length(); j++)            std::cout << ndarray[j] << std::endl;    return 0;}


... result: crash in the constructor (MSVC7.1, the only compiler i currently have)

About the copy assignment operators: shouldn't these be implicitly defined by the compiler when not provided? (by using the copy constructor which i did provide)

Thanks for pointing out the missing "struct" after friend. Wierd that the compiler didn't even give a warning because of that...

I'll try moving the specializations out of the main struct template (again... MSVC didn't complain... sometimes I wish it complained a little more when you do something wrong...)

EDIT:
Overlooked the last two points of your post:
varargs where the only thing that came to my mind for this problem, and anything else would have resulted in even more code. Furthermore, I do know about boost.multiarray, I just wanted to gather a little experience writing this kind of stuff myself as I have just recently discovered the increadible usefulness of templates (I love these moments :-P )
Quote:Original post by l0calh05t
my testcase was simply the following:

*** Source Snippet Removed ***

... result: crash in the constructor (MSVC7.1, the only compiler i currently have)

I'm unable to reproduce this using MSVC7.1. Could you post the compiler command line which is being invoked (should be in project properties)?

Quote:About the copy assignment operators: shouldn't these be implicitly defined by the compiler when not provided? (by using the copy constructor which i did provide)

The implicitly defined copy-assignment operator will not use your copy constructor, it will simply call the copy-assignment operators of each member object. In this case that results in a bitwise copy of your object - not what you want. I recommend implementing the copy-assignment operator using the "create a temporary and swap" idiom for exception-safety goodness.

Σnigma
Quote:Original post by l0calh05t
I knew there had to be a better tag than code :-D thanks for pointing that out


Here's another fun tip:

When you post normal text, the fourm will HTML escape < and >, resulting in:

#include &lt;cassert&gt;#include &lt;cstdarg&gt;


Which is displayed as:

#include <cassert>#include <cstdarg>


However, when it is transformed into a source tag, the HTML escapes show:

#include &lt;cassert&gt;#include &lt;cstdarg&gt;


To fix this, simply hit edit, and then click post again. The result will be:

#include <cassert>#include <cstdarg>
Quote:Original post by Enigma
I'm unable to reproduce this using MSVC7.1. Could you post the compiler command line which is being invoked (should be in project properties)?


/Od /I "C:\Boost\include\boost-1_33_1" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Gm /EHsc /RTC1 /MLd /FAs /Fa"Debug/" /Fo"Debug/" /Fd"Debug/vc70.pdb" /W3 /nologo /c /Wp64 /ZI /TP

Quote:
The implicitly defined copy-assignment operator will not use your copy constructor, it will simply call the copy-assignment operators of each member object. In this case that results in a bitwise copy of your object - not what you want. I recommend implementing the copy-assignment operator using the "create a temporary and swap" idiom for exception-safety goodness.

Σnigma


Actually, they were used...

@MaulingMonkey: Thanks, didn't notice that before
Quote:Original post by l0calh05t
/Od /I "C:\Boost\include\boost-1_33_1" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Gm /EHsc /RTC1 /MLd /FAs /Fa"Debug/" /Fo"Debug/" /Fd"Debug/vc70.pdb" /W3 /nologo /c /Wp64 /ZI /TP

Sorry, still not able to reproduce your crash. Not sure what else to suggest.

Quote:
Actually, they were used...

Are you sure you called the copy-assignment operator? The code:
Thing thing1;Thing thing2 = thing1;
Looks like it calls the copy-assignment operator, but actually calls the copy-constructor. You need something like:
Thing thing1;Thing thing2;thing2 = thing1;
To actually call the copy-assignment operator.

Σnigma
Hmm, not sure about that, i'll have to check that out tomorrow, it's kinda late here.
On top of what Enigma said:
If you don't want the default arrayNd constructor to be called, why not simply make it private instead of merely using an assert? Actually why use an assert (as well) in selector?
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
okay, so i put struct in all the friend declarations and it seems to work now (although... if that was wrong... why did it compile in the first place?), i didn't manage to get the inner struct specializations out of the main template. either i've got my syntax wrong, or it just doesn't work that way (hm.. thinking about it... is it possible i need to declare them with two template thingies in front? one for the outer struct one for the inner one?)

@imalc: yeah, you're right about the arrayNd constructor, for some reason i didn't think about making it private... oh well. and about the unneccessary asserts... I'd rather have too many asserts (which disappear in release compiles anyway) than too little, so i pretty much put them everywhere to check code. (and when i'm tired even in really stupid places :-P)

This topic is closed to new replies.

Advertisement