[Insight-developers] itk::Array solutions

Luis Ibanez ibanez@cs.unc.edu
Thu, 25 Jan 2001 16:57:13 -0500


Hi,


Brad King wrote:

> Unfortunately, this also prevents Point and Vector instances from being
> set through assignment to Array instances.  We might be able to solve this
> by having Point and Vector privately inherit from Array, and duplicate ALL
> of Array's public methods with pass-through calls.  Any thoughts on this?
>

The private derivation looks like a lot of work just for having
the option of using Arrays to initialize Vectors and Points.
I'll rather give up the initialization from Arrays, as long as we
keep the comma list initialization which is very natural and intuitive.


>
> 2.)  Addressing the matlab-style array-fill:
>  I have added two methods to the Array class to address this:
>
> itk::Vector<int, 4> v;
> v.Fill(5);
>

That looks good,
it much clearer that the operator=( T ).

>
> The Array class should be able to handle all ValueType settings, not just
> numeric types, so it does not make sense to provide methods like
> AssignZero() to do numerical assignment to array elements.  Instead, we
> can add itk namespace level functions to get all zeros:
>
> itk::ArrayTools<int, 3>::Zeros()
>
> or something like that could work.  Thoughts?



Well, maybe having the Fill() method is good enough,
Zeros() is just equivalent to Fill( 0  );



>
> 3.)  Ranged indexing.  Jim requested the ability to use ranged indexing on
> arrays (which carries over to points and vectors).  The solution is
> surprisingly clean, even in VC++.  I have added an itk::Range class.  It
> is currently just in itkArray.h, but we should move it to its own file if
> everyone likes its use.  It should probably be renamed itk::StaticRange,
> though, because it is a compile-time range selection class.  Here is an
> example:
>
> itk::Array<int, 5> a;
> itk::Array<int, 3> b;
> a.Fill(0);
> b.Fill(1);
>
> a[itk::Range<1,3>()] = b;
> // "a" will now contain {0,1,1,1,0}
>
> itk::Array<int, 2> c = a[itk::Range<3,4>()];
> // "c" will now contain {1,0}
>
> the [] operator is templated over the beginning and end of the range.  The
> Range class doesn't hold anything at run-time.  It is just there to tell
> the compiler what template arguments to use when instantiating the []
> operator, which then returns an Array::Reference to the portion of the
> indexed array selected by the range.  This results in compile-time range
> size checking (can't assign a 4-component array to a 3-component range of
> another array).
>



That looks very good !


>
> 4.)  Remaining issues...
>
> Bounds checking on comma-separated list assignments:
> I know that we can do compile time upper-bound checking using partial
> specialization (not supported by VC++).  There may be a way to do
> lower-bound checking too, but I haven't looked into it yet.  I can almost
> guarantee there is no way to do either check at compile time in VC++.  We
> could consider using a COMPILER_SUPPORTS_PARTIAL_SPECIALIZATION macro to
> turn the check on and off.  This way we would get the check when testing
> on systems with good enough compilers, but the fancy C++ code won't ruin
> the ability of the code to compile on poor compilers.  I don't think we
> want to introduce such a macro, though, because it could be abused (for
> example, used to turn features on/off).  Thoughts?
>



I'll rather not use the macro option, so far we have avoided to use
selective code and that simplifies a lot the future code maintenance.

Looking into Blitz++ they use an interesting technique for
initialization. Basically the class that goes through the list is 
templated over an int, and as each element is added, it instantiate 
and returns a class with N+1 as template parameter. It's like making 
a for loop using the template parameter as counter.

We could use the a variant of the ArrayCommaListCopier by templating it
over the number of elements already read. And make the comma operator of
ArrayCommaListCopier< N > returns an ArrayCommaListCopier< N+1 >.

Finally, the Array will have a operator=() that only accepts an
ArrayCommaListCopiers<N>.

In this way, if the list contained N-k or N+k elements, the class
returned at the end of the comma list parsing will not fit the 
type expected in Array::operator=().

It could be something like:



template < typename T, unsigned in NDimension >
class Array {
...
  const Array & operator= (  ArrayCommaListCopier< NDimension>  );
  ArrayCommaListCopier< 0 > ListBegin( void ) 
  {
    ArrayCommaListCopier< 0 > copier( Begin()  );
    return copier;
    }
protected:
  template< unsigned int NElem >
  class ArrayCommaListCopier {
  public:

    ArrayCommaListCopier( Iterator iter ) 
    {
      m_Iterator = iter;
    }

    ArrayCommaListCopier( Iterator iter, const T value ) 
    {
      m_Iterator = iter;
     *m_Iterator = value;
    }
    
    ArrayCommaListCopier< NElem + 1 >  operator,(  const T & elem )
    {
      ArrayCommaListCopier<NElem + 1> result( ++m_Iterator , elem );
      return result;
    }

    ArrayCommaListCopier< NElem + 1 >  operator=(  const T & elem )
    {
      *m_Iterator++ = elem;
      ArrayCommaListCopier<NElem + 1> result( m_Iterator );
      return result;
    }

   }

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

The bad part about this is that the intialization will have to look
like:

ArrayType   array;
array = (array.ListBegin()= 1, 3, 4 , 5);

Which is not as natural as the previous notation.

The parenthesis are neded because the comma operator has
the latest priority for evaluation.

In this way,

(ListReader=1,2)  returns a class ArrayCommaListCopier< 2 >
(ListReader=1,2,5,5)  returns a class ArrayCommaListCopier< 4 >

and so on...
only the list of right size will return the right type.

It will alse be very fast because the list is loaded at compile time.
There is no runtime loop.

It could be nice to find a way of hiding more the notation...though


Perhaps usin a Set() method instead of the operator=() could look 
better:

   array.Set( array.ListBegin() = 1, 2, 4, 5 );

...??

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



>
> Get_vnl_vector has been removed from itk::Array:
> Since the Array class could be used to store non-numeric types, such as
> "class X {};", and vnl_vector does not support these types, I had to
> remove the Get_vnl_vector support.  We could add it to itk::Vector
> (probably a better place anyway), or we could create an itk namespace
> level function to construct a vnl_vector_ref<> from an Array with a
> numeric ValueType.  Again, I'm looking for input on this.
>

Maybe having the Get_vnl_vector() method defined only in itkVector()
and itkPoint() could be sufficient for most applications.




- Luis