10

I have a 3D multi_array and I would like to make 2D slices using dimensions specified at runtime. I know the index of degenerate dimension and the index of a slice that I want to extract in that degenerate dimension. Currently the ugly workaround looks like that:

if (0 == degenerate_dimension)
{
    Slice slice = input_array[boost::indices[slice_index][range()][range()]];
}
else if (1 == degenerate_dimension)
{
    Slice slice = input_array[boost::indices[range()][slice_index][range()]];
}
else if (2 == degenerate_dimension)
{
    Slice slice = input_array[boost::indices[range()][range()][slice_index]];
}

Is there a more beautiful way to construct index_gen object? Something like that:

var slicer;
for(int i = 0; i < 3; ++i) {
    if (degenerate_dimension == i)
        slicer = boost::indices[slice_index];
    else
        slicer = boost::indices[range()];
}
Slice slice = input_array[slicer];

It seems each subsequent call to boost::indices::operator[] returns a different type depending on the dimensionality (i.e. number of previous calls), so there's no way to use a single variable, that can hold the temporary index_gen object.

Anton Daneyko
  • 6,528
  • 5
  • 31
  • 59
  • +1 for a clear question using a sane multidimensional array, not that C stuff! :) – Xeo Dec 12 '11 at 23:43

2 Answers2

4

Please, try this. Сode has one disadvantage - it refers to ranges_ array variable declared at boost::detail:: multi_array namespace.

#include <boost/multi_array.hpp>                                                                                                                              

typedef boost::multi_array<double, 3> array_type;                                                                                                             
typedef boost::multi_array_types::index_gen::gen_type<2,3>::type index_gen_type;                                                                                   
typedef boost::multi_array_types::index_range range;                                                                                                          

index_gen_type                                                                                                                                                     
func(int degenerate_dimension, int slice_index)                                                                                                               
{                                                                                                                                                             
    index_gen_type slicer;                                                                                                                                         
    int i;                                                                                                                                                    
    for(int i = 0; i < 3; ++i) {                                                                                                                              
        if (degenerate_dimension == i)                                                                                                                        
            slicer.ranges_[i] = range(slice_index);                                                                                                           
        else                                                                                                                                                  
            slicer.ranges_[i] = range();                                                                                                                      
    }                                                                                                                                                         
    return slicer;                                                                                                                                            
}                                                                                                                                                             

int main(int argc, char **argv)                                                                                                                               
{                                                                                                                                                             
    array_type myarray(boost::extents[3][3][3]);                                                                                                              
    array_type::array_view<2>::type myview = myarray[ func(2, 1) ];                                                                                           
    return 0;                                                                                                                                                 
}
alexander
  • 2,703
  • 18
  • 16
  • This is great and it works. I am sorry I missed it when the bounty was still on, so I am going to give you your 100 reputation by voting for all of the 18 answers you've got at your profile page. Although it is kind of hack of the rating system, I do not know of any other way to do that. – Anton Daneyko Feb 29 '12 at 17:15
  • Did you come up with that reading the source code of multi_array or did you pick it up from the docs? – Anton Daneyko Feb 29 '12 at 17:16
  • Thank you very much for the reputation :) Glad to hear that this code has helped you. In order to find a solution, I worked only with source of multi_array and applyed knowledge of C++. – alexander Mar 01 '12 at 06:57
  • @alexander Is it typically "wrong" to use something in `::detail`? As in, should I avoid using this for some reason? – David Doria Feb 29 '16 at 14:05
  • @David Doria, I think what things in ::detail namespace is for multi_array implementation and can be changed without notice between boost versions. While things inside multi_array interface keeps stable between boost versions. So, if you use something from ::detail, be ready what your code could became broken. – alexander Mar 01 '16 at 05:20
-2

What you're trying to do is move a variable from run time to compile time. This can only be done with a chain of if else statements or a switch statement.

A simplified example

// print a compile time int
template< int I >
void printer( void )
{
   std::cout << I << '\n';
}

// print a run time int
void printer( int i )
{
   // translate a runtime int to a compile time int
   switch( i )
   {
      case 1: printer<1>(); break;
      case 2: printer<2>(); break;
      case 3: printer<3>(); break;
      case 4: printer<4>(); break;
      default: throw std::logic_error( "not implemented" );
   }
}

// compile time ints
enum{ enum_i = 2 };
const int const_i = 3;
constexpr i constexper_i( void ) { return 4; }

// run time ints
extern int func_i( void ); // { return 5; }
extern int global_i; // = 6

int main()
{
   int local_i = 7;
   const int local_const_i = 8;

   printer<enum_i>();
   printer<const_i>();
   printer<constexpr_i()>();
   //printer<func_i()>();
   //printer<global_i>();
   //printer<local_i>();
   printer<local_const_i>();

   printer( enum_i );
   printer( const_i );
   printer( constexpr_i() );
   printer( func_i()      ); // throws an exception
   printer( global_i      ); // throws an exception
   printer( local_i       ); // throws an exception
   printer( local_const_i ); // throws an exception
}
deft_code
  • 57,255
  • 29
  • 141
  • 224