1

I wonder how to translate such C++11 code into Boost+visual studio 2008: multydimensional array creation and iteration thru it in case its part of some collection?

Here it goes:

#include <iostream>
#include <array>
#include <vector>
#include <set>

typedef size_t cell_id; // row * COLS + col

template <typename T> struct area
{
    T value;
    std::vector<cell_id> cells;
};

template <typename T, size_t Rows, size_t Cols>
std::vector<area<T> > getareas(const std::array<std::array<T, Cols>, Rows>& matrix)
{
    std::vector<area<T> > areas;
    return areas;
}


int main(){
    typedef std::array<int, 3> row;
    std::array<row, 4> matrix = { 
        row { 1  , 2, 3, },
        row { 1  , 3, 3, },
        row { 1  , 3, 3, },
        row { 100, 2, 1, },
    };

    auto areas = getareas(matrix);

    std::cout << "areas detected: " << areas.size() << std::endl;
    for (const auto& area : areas)
    {
        std::cout << "area of " << area.value << ": ";
        for (auto pt : area.cells)
        {
            int row = pt / 3, col = pt % 3;
            std::cout << "(" << row << "," << col << "), ";
        }
        std::cout << std::endl;
    }
}

it magicallyappeared that changing all std::array to boost::array is not enough =(

#include <iostream>
#include <array>
#include <vector>
#include <set>
#include <boost/array.hpp>

typedef size_t cell_id; // row * COLS + col

template <typename T> struct area
{
    T value;
    std::vector<cell_id> cells;
};

template <typename T, size_t Rows, size_t Cols>
std::vector<area<T> > getareas(const boost::array<boost::array<T, Cols>, Rows>& matrix)
{
    std::vector<area<T> > areas;
    return areas;
}


int main(){
    typedef boost::array<int, 3> row;
    boost::array<row, 4> matrix = { 
        row { 1  , 2, 3, },
        row { 1  , 3, 3, },
        row { 1  , 3, 3, },
        row { 100, 2, 1, },
    };

    auto areas = getareas(matrix);

    std::cout << "areas detected: " << areas.size() << std::endl;
    for (const auto& area : areas)
    {
        std::cout << "area of " << area.value << ": ";
        for (auto pt : area.cells)
        {
            int row = pt / 3, col = pt % 3;
            std::cout << "(" << row << "," << col << "), ";
        }
        std::cout << std::endl;
    }
}

boost::array<row, 4> matrix = ... part gives like 20 different alike sintax errors...

So I wonder what would be correct translation?

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
Rella
  • 65,003
  • 109
  • 363
  • 636

5 Answers5

2

std::array is an aggregate, so you should be able to use aggregate initialization syntax on the underlying array directly. That is, instead of this:

typedef std::array<int, 4> V4;
typedef std::array<V4, 4> M44;

M44 m { { 1,2,3,4}, {3,4,5,6}, {2,1,3,2}, {1,5,3,2} };

You can just write a naked array:

int[4][4] m = { { 1,2,3,4}, {3,4,5,6}, {2,1,3,2}, {1,5,3,2} };
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • the trouble is, he doesn't have c++0x. I posted the translation, it was my own C++11 code to another question here: http://stackoverflow.com/questions/8039896/having-a-matrix-mxn-of-integers-how-to-group-them-into-polygons-with-boost-geome/8044058#8044058 – sehe Nov 09 '11 at 15:44
  • Mmm. sorry. I thought you explained how to use uniform initialization. The OP specificly asks for MSVC2008 + Boost, though so I guess that's why I thought you'd be steering towards boost::array – sehe Nov 09 '11 at 16:00
  • But he doesn't have an array of `int`, he has an array of strongly-typed objects. Which look like they have a user-defined constructor. – Ben Voigt Nov 09 '11 at 16:07
  • @BenVoigt: As long as we're talking about an array of anything, aggregate initialization still works, though. – Kerrek SB Nov 09 '11 at 16:23
  • @BenVoigt: your argument is deceptive. "Aggregate" means that you can say (and indeed you can) `cd a[] = { cd(1,0), cd(2,1) };`. The definition of "aggregate", and the consequent initialization syntax, is recursive. `std::complex` appears *not* to be of aggregate type. – Kerrek SB Nov 09 '11 at 18:59
  • @KerrekSB: Sorry, I misread the question. I thought `row` was a strongly-typed class with a constructor (not an aggregate). Your version is a reasonable translation. – Ben Voigt Nov 09 '11 at 19:23
2

Done. The code is live on http://ideone.com/ATY4q

Also fixed a glaring bug in the recursion range checking. Can you spot it?

#include <iostream>
#include <fstream>
#include <boost/assign.hpp>
#include <boost/array.hpp>
#include <vector>
#include <set>

namespace mxdetail
{
    typedef size_t cell_id; // row * COLS + col

    template <typename T> struct area
    {
        T value;
        typedef std::vector<cell_id> cells_t;
        cells_t cells;
    };

    template <typename T, size_t Rows, size_t Cols>
        std::vector<area<T> > getareas(const boost::array<boost::array<T, Cols>, Rows>& matrix)
    {
        typedef boost::array<boost::array<T, Cols>, Rows> mtx;
        std::vector<area<T> > areas;

        struct visitor_t
        {
            const mtx& matrix;
            std::set<cell_id> visited;

            visitor_t(const mtx& mtx) : matrix(mtx) { }

            area<T> start(const int row, const int col)
            {
                area<T> result;
                visit(row, col, result);
                return result;
            }

            void visit(const int row, const int col, area<T>& current)
            {
                const cell_id id = row*Cols+col;
                if (visited.end() != visited.find(id))
                    return;

                bool matches = current.cells.empty() || (matrix[row][col] == current.value);

                if (matches)
                {
                    visited.insert(id);
                    current.value = matrix[row][col];
                    current.cells.push_back(id);

                    // process neighbours
                    for (int nrow=std::max(0, row-1); nrow < std::min((int) Rows, row+2); nrow++)
                    for (int ncol=std::max(0, col-1); ncol < std::min((int) Cols, col+2); ncol++)
                        /* if (ncol!=col || nrow!=row) */
                            visit(nrow, ncol, current);
                }
            }
        } visitor(matrix);

        for (int r=0; r < (int) Rows; r++)
            for (int c=0; c < (int) Cols; c++)
            {
                mxdetail::area<int> area = visitor.start(r,c);
                if (!area.cells.empty()) // happens when startpoint already visited
                    areas.push_back(area);
            }

        return areas;
    }
}


template <typename T, size_t N>
   boost::array<T, N> make_array(const T (&a)[N])
{
    boost::array<T, N> result;
    std::copy(a, a+N, result.begin());
    return result;
}

int main()
{
    typedef boost::array<int, 3> row;

    int row0[] = { 1  , 2, 3, };
    int row1[] = { 1  , 3, 3, };
    int row2[] = { 1  , 3, 3, };
    int row3[] = { 100, 2, 1, };

    boost::array<row, 4> matrix;
    matrix[0] = make_array(row0);
    matrix[1] = make_array(row1);
    matrix[2] = make_array(row2);
    matrix[3] = make_array(row3);

    typedef std::vector<mxdetail::area<int> > areas_t;
    typedef areas_t::value_type::cells_t cells_t; 

    areas_t areas = mxdetail::getareas(matrix);
    for (areas_t::const_iterator it=areas.begin(); it!=areas.end(); ++it)
    {
        std::cout << "area of " << it->value << ": ";
        for (cells_t::const_iterator pit=it->cells.begin(); pit!=it->cells.end(); ++pit)
        {
            int row = *pit / 3, col = *pit % 3;
            std::cout << "(" << row << "," << col << "), ";
        }
        std::cout << std::endl;
    }
    std::cout << "areas detected: " << areas.size() << std::endl;

}

Output:

area of 1: (0,0), (1,0), (2,0), 
area of 2: (0,1), 
area of 3: (0,2), (1,1), (1,2), (2,1), (2,2), 
area of 100: (3,0), 
area of 2: (3,1), 
area of 1: (3,2), 
areas detected: 6
sehe
  • 374,641
  • 47
  • 450
  • 633
0

It's going to be somewhat difficult to emulate the generalized initializer syntax. You may have to assign elements individually, post-creation.

You'll also have to replace the auto keyword with explicit types, and switch to the old for loop syntax.

In fact, this whole file looks like a demonstration of the new C++11 features. You'll probably be better off starting over, and making a design that's more friendly to the limitations of C++03.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • I'm with you that, given C++03 I'd not have used the boost::array<>. However, the translation can be made, as you can see from my answer. Yes non-uniform initialization is a bitch (I'm also rather disappointed that there is no constructor like `template array::array(const T (&a)[N])` in boost::array... – sehe Nov 09 '11 at 15:47
0

I believe that your problem is because VS2008 (and most other pre-c++11 compilers) do not support "initializer lists", which are how you initialize user-defined collections in C++11.

For example, you can't say

vector<int> v = { 1,2,3,4,5 };

in C++03, but you can in C++11.

And, of course, you can't use "auto" either.

auto areas = getareas(matrix);

Or the new for loop syntax:

for (auto pt : area.cells)
Marshall Clow
  • 15,972
  • 2
  • 29
  • 45
0

The declaration of matrix is using C++11's initialisation syntax; presumably, your compiler doesn't support that. There's no simple way to initialise a multi-dimensional boost::array; you'll need to assign each element afterwards.

boost::array<row, 4> matrix;
matrix[0][0] = 1;
// ...
matrix[3][2] = 1;

Alternatively, you could write a make_array function that assembles its arguments into an array. However, without variadic templates, you'd need to write an overload for each array size you want to support.

boost::array<row, 4> matrix = make_array(
    make_array(1,2,3),
    make_array(1,3,3),
    make_array(1,3,3),
    make_array(100,2,1));

Another possibility would be to use a plain array, and overload getareas for that:

template <typename T, size_t Rows, size_t Cols>
std::vector<area<T> > getareas(T (&matrix)[Rows][Cols]);

int matrix[4][3] = {{1,2,3}, {1,3,3}, {1,3,3}, {100,2,1}};

auto areas = getareas(matrix); // if "auto" works for you
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644