Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Re: Simple 2D Array

Reply
Thread Tools

Re: Simple 2D Array

 
 
alfps
Guest
Posts: n/a
 
      12-19-2008
On 18 Des, 21:07, "(2b|!2b)==?" <void-s...@ursa-major.com> wrote:
> I quickly hacked this together for some stuff I am writing - I havent
> used the STL for a while (been programming in another language) and I'd
> appreciate any feedback on how I may improve it/any gotchas (an obvious
> one being if constructed with (0,0) :
>
> #ifndef SIMPLE_2D_ARRAY_Header
> #define SIMPLE_2D_ARRAY_Header
>
> #include <vector>


You need a [stddef.h] here for unqualified size_t.


> template <typename T>
> class Array2D
> {
> public:
> * * * * typedef std::vector<T> Row ;


Is this implementation detail intentionally exposed?


> * * * * Array2D(const size_t numrows=1, const size_t numcols=1)
> * * * * :m_rows(numrows)
> * * * * {
> * * * * * * * * if (numrows)
> * * * * * * * * {
> * * * * * * * * * * * * for (size_t i=0; i < numrows; i++)
> * * * * * * * * * * * * * * * * m_rows[i]..resize(numcols);
> * * * * * * * * }
> * * * * }


You don't need that 'if'.



> * * * * Array2D(const Array2D& tbl)
> * * * * {
> * * * * * * * * m_rows = tbl.m_rows ;
> * * * * }


You don't need to define a copy constructor either: the one you get by
default works nicely.

But if you do define a copy constructor, use member initialization
list for initializations wherever possible.

The above constructor first default-constructs m_rows, and then
assigns, instead of copy-constructing it directly.


> * * * * Array2D& operator= (const Array2D& rhs)
> * * * * {
> * * * * * * * * if (this != &rhs)
> * * * * * * * * * * * * m_rows = rhs.m_rows ;
>
> * * * * * * * * return *this ;
> * * * * }


What happens if one of the vector element assignments throw an
exception?

Instead use the 'swap-idiom'.

That means defining a swap method, which you need anyway.


>
> * * * * ~Array2D()
> * * * * {}


This destructor is not needed.


> * * * * void reset(const size_t numrows, const size_t numcols)
> * * * * {
> * * * * * * * * m_rows.clear();
> * * * * * * * * m_rows.resize(numrows);
>
> * * * * * * * * for (size_t i = 0; i < numrows; i++)
> * * * * * * * * {
> * * * * * * * * * * * * m_rows[i].clear();
> * * * * * * * * * * * * m_rows[i].resize(numcols) ;
> * * * * * * * * }
> * * * * }
>
> * * * * size_t numrows() const { return m_rows.size() ; }
> * * * * size_t numcols() const { return m_rows.size() ? m_rows[0]..size() : 0 ; }


The standard library uses size_t, an unsigned type for this, so you're
in good company.

However, it's very impractical (I'd written "plain stupid" except then
narrow-minded conformists would jump on me like a bee-swarm).

For one, the aspect that annoys me the most, it forces use of size_t
on client code in order to avoid silly-warnings about signed/unsigned
comparisions -- and sometimes causes great havoc.

Basic types in C++ do *not* guarantee range checking.

On the contrary, the unsigned types guarantee no range checking. Which
means that they're completely unsuitable for indicating value ranges.
There's no advantage, and many pitfalls.


> * * * * T& value_at(const size_t row, const size_t col)
> * * * * {
> * * * * * * * * if (m_rows.empty() || (row >= m_rows.size()))
> * * * * * * * * * * * * throw std::logic_error("row array bounds exceeded") ;
>
> * * * * * * * * if (m_rows[row].empty() || (col >= m_rows[row].size()))
> * * * * * * * * * * * * throw std::logic_error("col array bounds exceeded") ;
>
> * * * * * * * * return m_rows[row][col] ;
> * * * * }


Why not just use the 'at' method here?


> * * * * const T& value_at(const size_t row, const size_t col) const
> * * * * {
> * * * * * * * * if (m_rows.empty() || (row >= m_rows.size()))
> * * * * * * * * * * * * throw std::logic_error("row array bounds exceeded") ;
>
> * * * * * * * * if (m_rows[row].empty() || (col >= m_rows[row].size()))
> * * * * * * * * * * * * throw std::logic_error("col array bounds exceeded") ;
>
> * * * * * * * * return m_rows[row][col] ;
> * * * * }
>
> * * * * const std::vector<T>& row(const unsigned int index) const
> * * * * {
> * * * * * * * * if (m_rows.empty() || (index >= m_rows.size()))
> * * * * * * * * * * * * throw std::logic_error("row array bounds exceeded") ;
> * * * * * * * * return m_rows[index] ;
> * * * * }
>
> * * * * size_t add_col()
> * * * * {
> * * * * * * * * size_t newsize = m_rows.size() ? m_rows[0].size() + 1 : 1;
>
> * * * * * * * * if (newsize == 1)
> * * * * * * * * {
> * * * * * * * * * * * * Row row ;
> * * * * * * * * * * * * m_rows.push_back(row);
> * * * * * * * * }
> * * * * * * * * else
> * * * * * * * * {
> * * * * * * * * * * * * for (size_t rownum = 0; rownum < m_rows.size(); rownum++)
> * * * * * * * * * * * * * * * * m_rows[rownum].resize(newsize);
> * * * * * * * * }
>
> * * * * * * * * return newsize;
> * * * * }
>
> * * * * size_t add_row()
> * * * * {
> * * * * * * * * if (!m_rows.size())
> * * * * * * * * * * * * return 0;
>
> * * * * * * * * Row row(m_rows[0].size()) ;
> * * * * * * * * m_rows.push_back(row);
>
> * * * * * * * * return m_rows.size();
> * * * * }
>
> private:
> * * * * std::vector<Row> m_rows ;
>
> };
>
> #endif
>
> MTIA


Uhm, I see no way to get data into such a 2D array?


Cheers & hth.,

- Alf
 
Reply With Quote
 
 
 
 
alfps
Guest
Posts: n/a
 
      12-19-2008
On 19 Des, 05:38, alfps <alf.p.steinb...@gmail.com> wrote:
>
> Uhm, I see no way to get data into such a 2D array?
>


Oh, sorry, Google Groups' interface to Usenet really reverse-blows.

I didn't see that there was a non-const accessor.

Cheers,

- Alf
 
Reply With Quote
 
 
 
 
Juha Nieminen
Guest
Posts: n/a
 
      12-19-2008
alfps wrote:
> The standard library uses size_t, an unsigned type for this, so you're
> in good company.
>
> However, it's very impractical (I'd written "plain stupid" except then
> narrow-minded conformists would jump on me like a bee-swarm).
>
> For one, the aspect that annoys me the most, it forces use of size_t
> on client code in order to avoid silly-warnings about signed/unsigned
> comparisions -- and sometimes causes great havoc.
>
> Basic types in C++ do *not* guarantee range checking.
>
> On the contrary, the unsigned types guarantee no range checking. Which
> means that they're completely unsuitable for indicating value ranges.
> There's no advantage, and many pitfalls.


So if in a 32-bit system you would like to index an array larger than
2GB you would need to use negative values? That could make the program a
bit obfuscated, I would say.

How many elements are there in an array if its size is -1294967296?
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      12-19-2008
On Dec 19, 5:38 am, alfps <alf.p.steinb...@gmail.com> wrote:
> On 18 Des, 21:07, "(2b|!2b)==?" <void-s...@ursa-major.com> wrote:


[...]
> > size_t numrows() const { return m_rows.size() ; }
> > size_t numcols() const { return m_rows.size() ? m_rows[0].size() : 0 ; }


> The standard library uses size_t, an unsigned type for this,
> so you're in good company.


> However, it's very impractical (I'd written "plain stupid"
> except then narrow-minded conformists would jump on me like a
> bee-swarm).


> For one, the aspect that annoys me the most, it forces use of
> size_t on client code in order to avoid silly-warnings about
> signed/unsigned comparisions -- and sometimes causes great
> havoc.


The problem is that those warnings aren't silly. Mixing signed
and unsigned in comparisons can result in some very unexpected
results. The problem is that mixing signed and unsigned does
cause problems, so we are more or less forced into using
unsigned. Which isn't very natural; unsigned in C++ is a
strange beast, which should normally be avoided except where its
particular properties are needed (which isn't here).

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
 
Reply With Quote
 
Juha Nieminen
Guest
Posts: n/a
 
      12-20-2008
James Kanze wrote:
> Which isn't very natural; unsigned in C++ is a
> strange beast, which should normally be avoided except where its
> particular properties are needed (which isn't here).


So you really would want to index a vector with a signed value?

I suppose it might work even beyond 2GB (in 32-bit systems), but it
may get messy with all those negative values.

It also gets messy when comparing vector sizes. If std::vector::size()
returned a signed value, something like this wouldn't work properly:

if(vector1.size() < vector2.size()) ...

For example, assume that vector1 has 3 000 000 000 elements and
vector2 has 1 000 000 000 elements (and we are compiling to a 32-bit
system).

Of course one could argue that the size of vectors (and other such
data containers) could be limited to the maximum value of int, but that
would be a rather artificial limitation given that there's no technical
reason for it (other than "we want to use signed integers for dogmatic
reasons").
 
Reply With Quote
 
Kai-Uwe Bux
Guest
Posts: n/a
 
      12-20-2008
Juha Nieminen wrote:

> James Kanze wrote:
>> Which isn't very natural; unsigned in C++ is a
>> strange beast, which should normally be avoided except where its
>> particular properties are needed (which isn't here).

>
> So you really would want to index a vector with a signed value?
>
> I suppose it might work even beyond 2GB (in 32-bit systems), but it
> may get messy with all those negative values.
>
> It also gets messy when comparing vector sizes. If std::vector::size()
> returned a signed value, something like this wouldn't work properly:
>
> if(vector1.size() < vector2.size()) ...
>
> For example, assume that vector1 has 3 000 000 000 elements and
> vector2 has 1 000 000 000 elements (and we are compiling to a 32-bit
> system).


Note that vector::difference_size is signed _and_ supposed to be able to
represent end() - begin() as well as begin() - end(). In particular, it
also can represent the size of the vector without going into negative
values.

> Of course one could argue that the size of vectors (and other such
> data containers) could be limited to the maximum value of int, but that
> would be a rather artificial limitation given that there's no technical
> reason for it (other than "we want to use signed integers for dogmatic
> reasons").


It _is_ limited by the maximum value of vector::difference_type. Of course,
this just means that in order to support larger vectors, an implementation
has to supply signed types of the appropriate size.


Best

Kai-Uwe Bux
 
Reply With Quote
 
Rolf Magnus
Guest
Posts: n/a
 
      12-20-2008
Kai-Uwe Bux wrote:

> Juha Nieminen wrote:
>
>> James Kanze wrote:
>>> Which isn't very natural; unsigned in C++ is a
>>> strange beast, which should normally be avoided except where its
>>> particular properties are needed (which isn't here).

>>
>> So you really would want to index a vector with a signed value?
>>
>> I suppose it might work even beyond 2GB (in 32-bit systems), but it
>> may get messy with all those negative values.


It works up to 2G _elements_.

>> It also gets messy when comparing vector sizes. If std::vector::size()
>> returned a signed value, something like this wouldn't work properly:
>>
>> if(vector1.size() < vector2.size()) ...
>>
>> For example, assume that vector1 has 3 000 000 000 elements and
>> vector2 has 1 000 000 000 elements (and we are compiling to a 32-bit
>> system).

>
> Note that vector::difference_size is signed _and_ supposed to be able to
> represent end() - begin() as well as begin() - end(). In particular, it
> also can represent the size of the vector without going into negative
> values.


That's my main concern about unsigned types for sizes. You can't represent
an offset between to arbitrary elements anymore. To do this at all, you need
to start mixing signed and unsigned, and even then, it doesn't work if your
container is too big.

 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      12-20-2008
On Dec 20, 5:12 am, Juha Nieminen <nos...@thanks.invalid> wrote:
> James Kanze wrote:
> > Which isn't very natural; unsigned in C++ is a strange
> > beast, which should normally be avoided except where its
> > particular properties are needed (which isn't here).


> So you really would want to index a vector with a signed
> value?


Ideally, it would be a ranged type, but C++ doesn't have those.
So I'm stuck with accepting an integral type. The most natural
integral type is int, so unless there are definite reasons for
using anything else, that's what you should use.

> I suppose it might work even beyond 2GB (in 32-bit systems),
> but it may get messy with all those negative values.


Aren't you being a bit silly. On a 32-bit machine, you're not
going to have indexes which overflow a 32 bit signed int.

> It also gets messy when comparing vector sizes. If
> std::vector::size() returned a signed value, something like
> this wouldn't work properly:


> if(vector1.size() < vector2.size()) ...


Why not? What doesn't work today is something like:

if ( vector1.size() > some expression )

where some expression can return a negative value. It would
work if std::vector::size() returned an int.

> For example, assume that vector1 has 3 000 000 000 elements


If you're on a 32-bit machine, vector1 can't have 3000000000
elements, so assuming it does is absurd.

> and vector2 has 1 000 000 000 elements (and we are compiling
> to a 32-bit system).


> Of course one could argue that the size of vectors (and other
> such data containers) could be limited to the maximum value of
> int, but that would be a rather artificial limitation given
> that there's no technical reason for it (other than "we want
> to use signed integers for dogmatic reasons").


The size of vectors is limited by the maximum value of the type
which contains the size. Whatever that type is. Signedness has
nothing to do with it.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
 
Reply With Quote
 
Juha Nieminen
Guest
Posts: n/a
 
      12-21-2008
James Kanze wrote:
>> For example, assume that vector1 has 3 000 000 000 elements

>
> If you're on a 32-bit machine, vector1 can't have 3000000000
> elements, so assuming it does is absurd.


Why not? You can certainly index 3 000 000 000 elements in a 32-bit
system (at least if the size of the elements is 1 byte).
 
Reply With Quote
 
alfps
Guest
Posts: n/a
 
      12-22-2008
On 21 Des, 22:01, Juha Nieminen <nos...@thanks.invalid> wrote:
> James Kanze wrote:
> >> For example, assume that vector1 has 3 000 000 000 elements

>
> > If you're on a 32-bit machine, vector1 can't have 3000000000
> > elements, so assuming it does is absurd.

>
> * Why not? You can certainly index 3 000 000 000 elements in a 32-bit
> system (at least if the size of the elements is 1 byte).


I'm not sure about that "can't", because it's easy to envision e.g. a
64-bit ptrdiff_t on a 32-bit machine.

But I think your argument is that if the address space size is 2^n,
and a vector has more than 2^(n-1) elements, then with n-bits signed
size type it gets rather messy, whereas unsigned type can handle it.

However, as noted earlier by Kai-Uwe Bux, that problem is already the
case for end()-begin(), so there already is an in-practice restriction
to 2^(n-1) elements if, with n-bits signed difference type, one wants
to avoid that mess.

In addition to the problem being already present, for practical
considerations, how often does one want > 2^(n-1) vector size?

And which OSes support it (Windows doesn't support it by default)?


Cheers,

- Alf
(Note: I've argued the opposite way at one time. Now I'm older and
less stupid. )
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
const and array of array (of array ...) Mara Guida C Programming 3 09-03-2009 07:54 AM
simple, simple array question Peter Bailey Ruby 7 04-08-2008 01:54 PM
length of an array in a struct in an array of structs in a struct in an array of structs Tuan Bui Perl Misc 14 07-29-2005 02:39 PM
Length of Array of Array of Array Tom Perl Misc 3 12-20-2004 05:23 PM
Re: Simple Simple question!!! ashelley@inlandkwpp.com ASP .Net 0 06-25-2004 04:18 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57