100

For an array with multiple dimensions, we usually need to write a for loop for each of its dimensions. For example:

vector< vector< vector<int> > > A;

for (int k=0; k<A.size(); k++)
{
    for (int i=0; i<A[k].size(); i++)
    {
        for (int j=0; j<A[k][i].size(); j++)
        {
            do_something_on_A(A[k][i][j]);
        }
    }
}

double B[10][8][5];
for (int k=0; k<10; k++)
{
    for (int i=0; i<8; i++)
    {
        for (int j=0; j<5; j++)
        {
            do_something_on_B(B[k][i][j]);
        }
    }
}

You see this kind of for-for-for loops in our code frequently. How do I use macros to define the for-for-for loops so that I don't need to re-write this kind of code every time? Is there a better way to do this?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
C. Wang
  • 2,516
  • 5
  • 29
  • 46
  • Perhaps you could consider using a Template as well – prmottajr Jan 08 '14 at 11:30
  • 63
    The obvious answer is that you don't. You don't create a new language using macros (or any other technique); the person who comes after you will be unable to read the code. – James Kanze Jan 08 '14 at 11:33
  • 17
    When you have a vector of a vector of a vector, that's a sign of bad design. – Maroun Jan 08 '14 at 11:34
  • 3
    @ᴍarounᴍaroun, that's a bit of a generalisation, how do you represent 3 dimensional data otherwise? – Nim Jan 08 '14 at 11:39
  • 5
    @Nim: You may do it with *1* flat array (not sure it is better). – Jarod42 Jan 08 '14 at 11:41
  • 4
    @ᴍarounᴍaroun I tend to agree. The first thing you do when you have such a data structure is to encapsulate it in a class (probably with a flat `std::vector` to hold the data). – James Kanze Jan 08 '14 at 11:48
  • 17
    I would think you wouldn't want to hide potential `O(n) = n^3` code... – poy Jan 08 '14 at 13:12
  • 1
    Looks like an A-bomb to kill a fly to me. – kuroi neko Jan 08 '14 at 16:25
  • 36
    @TC1: And then I will find it harder to read. It's all a question of personal preferences and it actually doesn't help with the question at hand here. – ereOn Jan 08 '14 at 16:37
  • Almost all of the answers here fail on a 2d array of vectors, where you want to pass the _vector_ to the function. – Mooing Duck Jan 08 '14 at 20:01
  • 1
    Write a function to do this. –  Jan 08 '14 at 21:41
  • 1
    Use a function. And if you need it to be more generalised, a templated function. And if you need to be calling different functions on your objects, you could even pass the function to call, depending on your language. And I agree that it seems like you should be encapsulating the data structure in a class. – Simon Trewhella Jan 09 '14 at 01:01

16 Answers16

282

The first thing is that you don't use such a data structure. If you need a three dimensional matrix, you define one:

class Matrix3D
{
    int x;
    int y;
    int z;
    std::vector<int> myData;
public:
    //  ...
    int& operator()( int i, int j, int k )
    {
        return myData[ ((i * y) + j) * z + k ];
    }
};

Or if you want to index using [][][], you need an operator[] which returns a proxy.

Once you've done this, if you find that you constantly have to iterate as you've presented, you expose an iterator which will support it:

class Matrix3D
{
    //  as above...
    typedef std::vector<int>::iterator iterator;
    iterator begin() { return myData.begin(); }
    iterator end()   { return myData.end();   }
};

Then you just write:

for ( Matrix3D::iterator iter = m.begin(); iter != m.end(); ++ iter ) {
    //  ...
}

(or just:

for ( auto& elem: m ) {
}

if you have C++11.)

And if you need the three indexes during such iterations, it's possible to create an iterator which exposes them:

class Matrix3D
{
    //  ...
    class iterator : private std::vector<int>::iterator
    {
        Matrix3D const* owner;
    public:
        iterator( Matrix3D const* owner,
                  std::vector<int>::iterator iter )
            : std::vector<int>::iterator( iter )
            , owner( owner )
        {
        }
        using std::vector<int>::iterator::operator++;
        //  and so on for all of the iterator operations...
        int i() const
        {
            ((*this) -  owner->myData.begin()) / (owner->y * owner->z);
        }
        //  ...
    };
};
hansmaad
  • 18,417
  • 9
  • 53
  • 94
James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 21
    This answer should be way more upvoted as it is the only one that deals with the actual source of the problem. – ereOn Jan 08 '14 at 16:39
  • 5
    it may be corret answer but i dont agree it is a good one. lots of cryptic template codes with probably x10 times slow compile time and probably x10 slow debug(may be more) code. For me definitly original code is way more clear to me... – Gorkem Jan 09 '14 at 12:12
  • 10
    @beehorf ...and also much, much slower. Because multi-dimensional arrays in C and C++ are actually nested arrays in the sense that the outer dimensions store pointers to the nested arrays. These nested arrays are then arbitrarily scattered around in memory, effectively defeating any prefetching and caching. I know of examples where somebody wrote a code using `vector > >` to represent a 3-dimensional field. Rewriting the code the equivalent to above solution resulted in a speedup of 10. – Michael Wild Jan 09 '14 at 12:57
  • 5
    @beehorf Where do you see any template code? (In practice, `Matrix3D` probably should be a template, but it's a very straightforward template.) And you only have to debug `Matrix3D`, not every time you need a 3D matrix, so you save an enormous amount of time in debugging. As for clarity: how is `std::vector>>` clearer than `Matrix3D`? Not to mention that `Matrix3D` enforces the fact that you have a matrix, while the nested vectors could be ragged, and that the above is probably significantly faster. – James Kanze Jan 09 '14 at 13:31
  • @MichaelWild That's probably true on most modern machines---at least from personal computers up. It might not be true on some embedded processors, and certainly wasn't true many years back, on the original 8086 (where all memory accesses cost the same, and a multiplication cost something like 20 times more than a memory access). – James Kanze Jan 09 '14 at 13:33
  • 10
    @MichaelWild But of course, the real advantage of my approach is that you can change the representation, depending on what is faster in your environment, without having to modify any of the client code. The key to good performance is proper encapsulation, so that you can make the changes the profiler says you need without having to rewrite the entire application. – James Kanze Jan 09 '14 at 13:36
  • @JamesKanze could you please explain what `i()` is for? thanks . – Koushik Shetty Jan 10 '14 at 07:28
  • @Koushik To get the index `i` from the iterator. Although generally speaking, if you're going to need the index `i`, I'd probably prefer the three nested loops. I'd reserve these special iterators for the cases where you have to visit each element, without concerns of where the element is in the three dimensional structure. – James Kanze Jan 10 '14 at 09:32
  • oh ok. and BTW the return keyword is missing. thanks for the clarification @JamesKanze – Koushik Shetty Jan 10 '14 at 09:45
  • @JamesKanze You probably want to derive Matrix3d::iterator from `std::vector::iterator` and using its `operator++` in your last example. – hansmaad Jan 10 '14 at 10:40
  • @hansmaad Yes. The final example is clearly just an idea; as I said above, I'm not sure it's a good idea. But if someone does want to persue it... Clearly, the iterator should derive from `std::vector::iterator`, and not from `std::vector`. And a good dose of `using` would be in order, since what you want is just to add a few extra functions. – James Kanze Jan 10 '14 at 11:54
  • 1
    Your example further illustrates the initial problem: who said a vector of vector of vector of int ***is*** a 3D matrix in the first place? It could be the number of socks in drawers in cupboards in rooms of the OP's house, stored in vectors to allow easy comparison with millions of similar data collected by the NSA in vector of vector of vector of int form. – kuroi neko Jan 15 '14 at 04:37
44

Using a macro to hide the for loops can be a lot confusing, just to save few characters. I'd use range-for loops instead:

for (auto& k : A)
    for (auto& i : k)
        for (auto& j : i)
            do_something_on_A(j);

Of course you can replace auto& with const auto& if you are, in fact, not modifying the data.

Letharion
  • 4,067
  • 7
  • 31
  • 42
Shoe
  • 74,840
  • 36
  • 166
  • 272
21

Something like this can help:

 template <typename Container, typename Function>
 void for_each3d(const Container &container, Function function)
 {
     for (const auto &i: container)
         for (const auto &j: i)
             for (const auto &k: j)
                 function(k);
 }

 int main()
 {
     vector< vector< vector<int> > > A;     
     for_each3d(A, [](int i){ std::cout << i << std::endl; });

     double B[10][8][5] = { /* ... */ };
     for_each3d(B, [](double i){ std::cout << i << std::endl; });
 }

In order to make it N-ary we need some template magic. First of all we should create SFINAE structure to distinguish whether this value or container. The default implementation for values, and specialisations for arrays and each of the container types. How @Zeta notes, we can determine the standard containers by the nested iterator type (ideally we should check whether the type can be used with range-base for or not).

 template <typename T>
 struct has_iterator
 {
     template <typename C>
     constexpr static std::true_type test(typename C::iterator *);

     template <typename>
     constexpr static std::false_type test(...);

     constexpr static bool value = std::is_same<
         std::true_type, decltype(test<typename std::remove_reference<T>::type>(0))
     >::value;
 };

 template <typename T>
 struct is_container : has_iterator<T> {};

 template <typename T>
 struct is_container<T[]> : std::true_type {};

 template <typename T, std::size_t N>
 struct is_container<T[N]> : std::true_type {}; 

 template <class... Args>
 struct is_container<std::vector<Args...>> : std::true_type {};

Implementation of for_each is straightforward. The default function will call function:

 template <typename Value, typename Function>
 typename std::enable_if<!is_container<Value>::value, void>::type
 rfor_each(const Value &value, Function function)
 {
     function(value);
 }

And the specialisation will call itself recursively:

 template <typename Container, typename Function>
 typename std::enable_if<is_container<Container>::value, void>::type
 rfor_each(const Container &container, Function function)
 {
     for (const auto &i: container)
         rfor_each(i, function);
 }

And voila:

 int main()
 {
     using namespace std;
     vector< vector< vector<int> > > A;
     A.resize(3, vector<vector<int> >(3, vector<int>(3, 5)));
     rfor_each(A, [](int i){ std::cout << i << ", "; });
     // 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,

     std::cout << std::endl;
     double B[3][3] = { { 1. } };
     rfor_each(B, [](double i){ std::cout << i << ", "; });
     // 1, 0, 0, 0, 0, 0, 0, 0, 0,
 }

Also this will not work for pointers (arrays allocated in heap).

fasked
  • 3,555
  • 1
  • 19
  • 36
  • @herohuyongtao with constraints we can implement two specialisation for `Container` and for others. – fasked Jan 08 '14 at 11:58
  • 1
    @herohuyongtao I made an example of K-ary foreach. – fasked Jan 08 '14 at 12:40
  • 1
    @fasked: Use `is_container : has_iterator::value` from my answer and you don't need to write a specialisation for every type, since every container should have an `iterator` typedef. Feel free to completely use anything from my answer, yours is already better. – Zeta Jan 08 '14 at 12:41
  • @Zeta +1 for this. Also as I mentioned `Container` concept will help. – fasked Jan 08 '14 at 12:44
  • `::iterator` does not an iterable range make. `int x[2][3][4]` is perfectly iterable, as is `struct foo { int x[3]; int* begin() { return x; } int* end() { return x+3; } };` I am not sure what `T[]` specialization is supposed to do? – Yakk - Adam Nevraumont Jan 08 '14 at 16:00
  • To help, rather than complain about flaws: `namespace adl_aux { using std::begin; using std::end; template auto adl_begin( C&& c )->decltype( begin(std::forward(c)) ); template auto adl_end( C&& c )->decltype( end(std::forward(c)) ); } using adl_aux::adl_begin; using adl_aux::adl_end;` provides you with functions that closely emulate the iterator lookup used by `for( auto x : y )` loops. – Yakk - Adam Nevraumont Jan 08 '14 at 16:05
17

Most of the answers simply demonstrate how C++ can be twisted into incomprehensible syntactic extensions, IMHO.

By defining whatever templates or macros, you just force other programmers to understand bits of obfuscated code designed to hide other bits of obfuscated code.
You will force every guy who reads your code to have template expertise, just to avoid doing your job of defining objects with clear semantics.

If you decided to use raw data like 3 dimensional arrays, just live with it, or else define a class that gives some understandable meaning to your data.

for (auto& k : A)
for (auto& i : k)
for (auto& current_A : i)
    do_something_on_A(current_A);

is just consistent with the cryptic definition of a vector of vector of vector of int with no explicit semantics.

kuroi neko
  • 8,479
  • 1
  • 19
  • 43
9
#include "stdio.h"

#define FOR(i, from, to)    for(int i = from; i < to; ++i)
#define TRIPLE_FOR(i, j, k, i_from, i_to, j_from, j_to, k_from, k_to)   FOR(i, i_from, i_to) FOR(j, j_from, j_to) FOR(k, k_from, k_to)

int main()
{
    TRIPLE_FOR(i, j, k, 0, 3, 0, 4, 0, 2)
    {
        printf("i: %d, j: %d, k: %d\n", i, j, k);
    }
    return 0;
}

UPDATE: I know, that you asked for it, but you'd better not use that :)

Adithya
  • 183
  • 1
  • 11
FreeNickname
  • 7,398
  • 2
  • 30
  • 60
  • 6
    I know that's what the OP asked for, but seriously... This looks like a marvelous example of obfuscation. Supposing `TRIPLE_FOR` were defined in some header, what am I to think when I see `TRIPLE_FOR here. – James Kanze Jan 08 '14 at 11:44
  • 2
    Yes, I guess, you're right :) I think, I'll leave it here just as an example that this can be done using a macros, but add a note that it's better not to do like that :) I just woke up, and decided to use this question as a small warm-up for the mind. – FreeNickname Jan 08 '14 at 11:48
5

One idea is to write an iterable pseudo-container class that "contains" the set of all multi-index tuples you'll index over. No implementation here because it'll take too long but the idea is that you should be able to write...

multi_index mi (10, 8, 5);
  //  The pseudo-container whose iterators give {0,0,0}, {0,0,1}, ...

for (auto i : mi)
{
  //  In here, use i[0], i[1] and i[2] to access the three index values.
}
4

I see many answers here that work recursively, detecting if the input is a container or not. Instead, why not detect if the current layer is the same type as the function takes? It's far simpler, and allows for more powerful functions:

//This is roughly what we want for values
template<class input_type, class func_type> 
void rfor_each(input_type&& input, func_type&& func) 
{ func(input);}

//This is roughly what we want for containers
template<class input_type, class func_type>
void rfor_each(input_type&& input, func_type&& func) 
{ for(auto&& i : input) rfor_each(i, func);}

However, this (obviously) gives us ambiguity errors. So we use SFINAE to detect if the current input fits in the function or not

//Compiler knows to only use this if it can pass input to func
template<class input_type, class func_type>
auto rfor_each(input_type&& input, func_type&& func) ->decltype(func(input)) 
{ return func(input);}

//Otherwise, it always uses this one
template<class input_type, class func_type>
void rfor_each(input_type&& input, func_type&& func) 
{ for(auto&& i : input) rfor_each(i, func);}

This now handles the containers correctly, but the compiler still considers this ambiguous for input_types that can be passed to the function. So we use a standard C++03 trick to make it prefer the first function over the second, of also passing a zero, and making the one we prefer accept and int, and the other takes ...

template<class input_type, class func_type>
auto rfor_each(input_type&& input, func_type&& func, int) ->decltype(func(input)) 
{ return func(input);}

//passing the zero causes it to look for a function that takes an int
//and only uses ... if it absolutely has to 
template<class input_type, class func_type>
void rfor_each(input_type&& input, func_type&& func, ...) 
{ for(auto&& i : input) rfor_each(i, func, 0);}

That's it. Six, relatively simple lines of code, and you can iterate over values, rows, or any other sub-unit, unlike all of the other answers.

#include <iostream>
int main()
 {

     std::cout << std::endl;
     double B[3][3] = { { 1.2 } };
     rfor_each(B[1], [](double&v){v = 5;}); //iterate over doubles
     auto write = [](double (&i)[3]) //iterate over rows
         {
             std::cout << "{";
             for(double d : i) 
                 std::cout << d << ", ";
             std::cout << "}\n";
         };
     rfor_each(B, write );
 };

Proof of compilation and execution here and here

If you wanted a more convenient syntax in C++11, you could add a macro. (Following is untested)

template<class container>
struct container_unroller {
    container& c;
    container_unroller(container& c_) :c(c_) {}
    template<class lambda>
    void operator <=(lambda&& l) {rfor_each(c, l);}
};
#define FOR_NESTED(type, index, container) container_unroller(container) <= [](type& index) 
//note that this can't handle functions, function pointers, raw arrays, or other complex bits

int main() {
     double B[3][3] = { { 1.2 } };
     FOR_NESTED(double, v, B) {
         std::cout << v << ", ";
     }
}
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
3

I caveat this answer with the following statement: this would only work if you were operating on an actual array - it wouldn't work for your example using std::vector.

If you are performing the same operation on every element of a multi-dimensional array, without caring about the position of each item, then you can take advantage of the fact that arrays are placed in contiguous memory locations, and treat the whole thing as one big one-dimensional array. For example, if we wanted to multiply every element by 2.0 in your second example:

double B[3][3][3];
// ... set the values somehow
double* begin = &B[0][0][0];     // get a pointer to the first element
double* const end = &B[3][0][0]; // get a (const) pointer past the last element
for (; end > begin; ++begin) {
    (*begin) *= 2.0;
}

Note that using the above approach also allows the use of some "proper" C++ techniques:

double do_something(double d) {
    return d * 2.0;
}

...

double B[3][3][3];
// ... set the values somehow
double* begin = &B[0][0][0];  // get a pointer to the first element
double* end = &B[3][0][0];    // get a pointer past the last element

std::transform(begin, end, begin, do_something);

I don't generally advise this approach (preferring something like Jefffrey's answer), as it relies on having defined sizes for your arrays, but in some cases it can be useful.

icabod
  • 6,992
  • 25
  • 41
  • This is illegal: http://stackoverflow.com/questions/6015080/c-c-is-this-undefined-behavior-2d-arrays – ecatmur Jan 08 '14 at 17:59
  • @ecatmur: Interesting - I've only just got in to work, so I'll check this out and update/delete the answer accordingly. Thanks. – icabod Jan 09 '14 at 09:30
  • @ecatmur: I've looked at the C++11 standard (section 8.3.4), and what I've written should work, and doesn't look illegal (to me). The link you provided relates to accessing members outside of the defined array size. While it's true that I get the address of just past the array, it's not accessing the data - this is in order to provide an "end", in the same way that you can use pointers as iterators, with "end" being one past the last element. – icabod Jan 09 '14 at 10:30
  • You're effectively accessing `B[0][0][i]` for `i >= 3`; this is not allowed as it is accessing outside the (inner) array. – ecatmur Jan 09 '14 at 11:40
  • @ecatmur: it's all [contiguous memory](http://stackoverflow.com/a/7784790/218597), so `&B[0][0][3]` is equal to `&B[0][1][0]`. If it wasn't contiguous then yes, I would agree. – icabod Jan 09 '14 at 13:31
  • A little update - I can't spot anything that states with certainty how a multi-D array is stored in memory (one block of memory, or an array of pointers to an array of...), so I guess this is a case of "it works for me on my compiler, YMMV"? – icabod Jan 09 '14 at 14:03
  • The storage is contiguous, but that doesn't mean you're allowed to access it as if it is. Actually, I think this is a point of difference between C++ and C; C++ has the thin-air rule (3.9.2p3), while C is stricter (to allow for more aggressive optimisation). – ecatmur Jan 09 '14 at 15:40
  • 1
    A clearer way IMO of assigning end IF you were to do this is end = start + (xSize * ySize * zSize) – noggin182 Jan 14 '14 at 22:09
  • @noggin182: Yes, that's actually much clearer, and similar to code I've written myself... less chance of trying to access invalid data (by using `[3][3][3]` by mistake). – icabod Jan 15 '14 at 09:17
2

I was kind of shocked that no one proposed some arithmetic-magic based loop to do the work. Since C. Wang is looking for a solution with no nested loops, I'll propose one:

double B[10][8][5];
int index = 0;

while (index < (10 * 8 * 5))
{
    const int x = index % 10,
              y = (index / 10) % 10,
              z = index / 100;

    do_something_on_B(B[x][y][z]);
    ++index;
}

Well, this approach isn't elegant and flexible, so we could pack all the process into a template function:

template <typename F, typename T, int X, int Y, int Z>
void iterate_all(T (&xyz)[X][Y][Z], F func)
{
    const int limit = X * Y * Z;
    int index = 0;

    while (index < limit)
    {
        const int x = index % X,
                  y = (index / X) % Y,
                  z = index / (X * Y);

        func(xyz[x][y][z]);
        ++index;
    }
}

This template function can be expressed in the form of nested loops as well:

template <typename F, typename T, int X, int Y, int Z>
void iterate_all(T (&xyz)[X][Y][Z], F func)
{
    for (auto &yz : xyz)
    {
        for (auto &z : yz)
        {
            for (auto &v : z)
            {
                func(v);
            }
        }
    }
}

And can be used providing a 3D array of arbitrary size plus the function name, letting the parameter deduction do the hard work of counting the size of each dimension:

int main()
{
    int A[10][8][5] = {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};
    int B[7][99][8] = {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};

    iterate_all(A, do_something_on_A);
    iterate_all(B, do_something_on_B);

    return 0;
}

Towards more generic

But once again, it lacks of flexibility 'cause it only works for 3D arrays, but using SFINAE we can do the work for arrays of an arbitrary dimension, first we need a template function which iterates arrays of rank 1:

template<typename F, typename A>
typename std::enable_if< std::rank<A>::value == 1 >::type
iterate_all(A &xyz, F func)
{
    for (auto &v : xyz)
    {
        func(v);
    }
}

And another one which iterates arrays of any rank, doing the recursion:

template<typename F, typename A>
typename std::enable_if< std::rank<A>::value != 1 >::type
iterate_all(A &xyz, F func)
{
    for (auto &v : xyz)
    {
        iterate_all(v, func);
    }
}

This allows us to iterate all the elements in all the dimensions of a arbitrary-dimensions arbitrary-sized array.


Working with std::vector

For the multiple nested vector, the solution ressembles the one of arbitrary-dimensions arbitrary-sized array, but without SFINAE: First we will need a template function that iterates std::vectors and calls the desired function:

template <typename F, typename T, template<typename, typename> class V>
void iterate_all(V<T, std::allocator<T>> &xyz, F func)
{
    for (auto &v : xyz)
    {
        func(v);
    }
}

And another template function that iterates any kind of vector of vectors and calls himself:

template <typename F, typename T, template<typename, typename> class V> 
void iterate_all(V<V<T, std::allocator<T>>, std::allocator<V<T, std::allocator<T>>>> &xyz, F func)
{
    for (auto &v : xyz)
    {
        iterate_all(v, func);
    }
}

Regardless of the nesting level, iterate_all will call the vector-of-vectors version unless the vector-of-values version is a better match thus ending the recursivity.

int main()
{
    using V0 = std::vector< std::vector< std::vector<int> > >;
    using V1 = std::vector< std::vector< std::vector< std::vector< std::vector<int> > > > >;

    V0 A0 =   {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};
    V1 A1 = {{{{{9, 8}, {7, 6}}, {{5, 4}, {3, 2}}}}};

    iterate_all(A0, do_something_on_A);
    iterate_all(A1, do_something_on_A);

    return 0;
}

I think that the function body is pretty simple and straight-forward... I wonder if the compiler could unroll this loops (I'm almost sure that most compilers could unroll the first example).

See live demo here.

Hope it helps.

Community
  • 1
  • 1
PaperBirdMaster
  • 12,806
  • 9
  • 48
  • 94
1

Use something along these lines (its pseudo-code, but the idea stays the same). You extract the pattern to loop once, and apply a different function each time.

doOn( structure A, operator o)
{
    for (int k=0; k<A.size(); k++)
    {
            for (int i=0; i<A[k].size(); i++)
            {
                for (int j=0; j<A[k][i].size(); j++)
                {
                        o.actOn(A[k][i][j]);
                }
            }
    }
}

doOn(a, function12)
doOn(a, function13)
Rob Audenaerde
  • 19,195
  • 10
  • 76
  • 121
1

Stick with the nested for loops!

All the methods suggested here have disadvantages in terms of either readability or flexibility.

What happens if you need to use the results of an inner loop for the processing in the outer loop? What happens if you need a value from the outer loop within your inner loop? Most of the "encapsulation" methods fail here.

Trust me I have seen several attempts to "clean up" nested for loops and in the end it turns out that the nested loop is actually the cleanest and most flexible solution.

James Anderson
  • 27,109
  • 7
  • 50
  • 78
0

One technique I've used is templates. E.g.:

template<typename T> void do_something_on_A(std::vector<T> &vec) {
    for (auto& i : vec) { // can use a simple for loop in C++03
        do_something_on_A(i);
    }
}

void do_something_on_A(int &val) {
    // this is where your `do_something_on_A` method goes
}

Then you simply call do_something_on_A(A) in your main code. The template function gets created once for each dimension, the first time with T = std::vector<std::vector<int>>, the second time with with T = std::vector<int>.

You could make this more generic using std::function (or function-like objects in C++03) as a second argument if you want:

template<typename T> void do_something_on_vec(std::vector<T> &vec, std::function &func) {
    for (auto& i : vec) { // can use a simple for loop in C++03
        do_something_on_vec(i, func);
    }
}

template<typename T> void do_something_on_vec(T &val, std::function &func) {
    func(val);
}

Then call it like:

do_something_on_vec(A, std::function(do_something_on_A));

This works even though the functions have the same signature because the first function is a better match for anything with std::vector in the type.

JoshG79
  • 1,685
  • 11
  • 14
0

Here is a C++11 implementation that handles everything iterable. Other solutions restrict themselves to containers with ::iterator typedefs or arrays: but a for_each is about iteration, not being a container.

I also isolate the SFINAE to a single spot in the is_iterable trait. The dispatching (between elements and iterables) is done via tag dispatching, which I find is a clearer solution.

The containers and the functions applied to elements are all perfect forwarded, allowing both const and non-const access to the ranges and functors.

#include <utility>
#include <iterator>

The template function I am implementing. Everything else could go into a details namespace:

template<typename C, typename F>
void for_each_flat( C&& c, F&& f );

Tag dispatching is much cleaner than SFINAE. These two are used for iterable objects and non iterable objects respectively. The last iteration of the first could use perfect forwarding, but I am lazy:

template<typename C, typename F>
void for_each_flat_helper( C&& c, F&& f, std::true_type /*is_iterable*/ ) {
  for( auto&& x : std::forward<C>(c) )
    for_each_flat(std::forward<decltype(x)>(x), f);
}
template<typename D, typename F>
void for_each_flat_helper( D&& data, F&& f, std::false_type /*is_iterable*/ ) {
  std::forward<F>(f)(std::forward<D>(data));
}

This is some boilerplate required in order to write is_iterable. I do argument dependent lookup on begin and end in a detail namespace. This emulates what a for( auto x : y ) loop does reasonably well:

namespace adl_aux {
  using std::begin; using std::end;
  template<typename C> decltype( begin( std::declval<C>() ) ) adl_begin(C&&);
  template<typename C> decltype( end( std::declval<C>() ) ) adl_end(C&&);
}
using adl_aux::adl_begin;
using adl_aux::adl_end;

The TypeSink is useful to test if code is valid. You do TypeSink< decltype( code ) > and if the code is valid, the expression is void. If the code is not valid, SFINAE kicks in and the specialization is blocked:

template<typename> struct type_sink {typedef void type;};
template<typename T> using TypeSink = typename type_sink<T>::type;

template<typename T, typename=void>
struct is_iterable:std::false_type{};
template<typename T>
struct is_iterable<T, TypeSink< decltype( adl_begin( std::declval<T>() ) ) >>:std::true_type{};

I only test for begin. An adl_end test could also be done.

The final implementation of for_each_flat ends up being extremely simple:

template<typename C, typename F>
void for_each_flat( C&& c, F&& f ) {
  for_each_flat_helper( std::forward<C>(c), std::forward<F>(f), is_iterable<C>() );
}        

Live example

This is way down at the bottom: feel free to poach for the top answers, which are solid. I just wanted a few better techniques to be used!

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

You could generate indices in one loop like this (A, B, C are dimensions):

int A = 4, B = 3, C = 3;
for(int i=0; i<A*B*C; ++i)
{
    int a = i/(B*C);
    int b = (i-((B*C)*(i/(B*C))))/C;
    int c = i%C;
}
janek
  • 184
  • 1
  • 6
  • I agree with you, it's specifically designed for 3 dimensions ;) – janek Jan 08 '14 at 17:14
  • 1
    Not to mention it is incredibly slow! – noggin182 Jan 15 '14 at 13:27
  • @noggin182: the question wasn't about speed but about avoiding nested for-loops; besides, there are unnecessary divisions there, i/(B*C) can be replaced by a – janek Jan 16 '14 at 10:08
  • Ok, this is an alternative way, probably more efficient (javascript): for(var i = 0, j = 0, k = 0; i < A; i += (j == B-1 && k == C - 1) ? 1 : 0, j = (k == C - 1) ? ((j == B-1) ? 0 : j + 1) : j, k = (k == C - 1) ? 0 : k + 1) { console.log(i + " " + j + " " + k); } – janek Jan 16 '14 at 16:10
0

One thing you may want to try if you only have statements in the inner-most loop - and your concern is more about the overly verbose nature of the code - is to use a different whitespace scheme. This will only work if you can state your for loops compactly enough so that they all fit on one line.

For your first example, I would rewrite it as:

vector< vector< vector<int> > > A;
int i,j,k;
for(k=0;k<A.size();k++) for(i=0;i<A[k].size();i++) for(j=0;j<A[k][i].size();j++) {
    do_something_on_A(A[k][i][j]);
}

This is kinda pushing it because you are calling functions in the outer loops which is equivalent to putting statements in them. I have removed all unnecessary white-space and it may be passible.

The second example is much better:

double B[10][8][5];
int i,j,k;

for(k=0;k<10;k++) for(i=0;i<8;i++) for(j=0;j<5;j++) {
    do_something_on_B(B[k][i][j]);
}

This may be different whitespace convention than you like to use, but it achieves a compact result that nonetheless does not require any knowledge beyond C/C++ (such as macro conventions) and does not require any trickery like macros.

If you really want a macro, you could then take this a step further with something like:

#define FOR3(a,b,c,d,e,f,g,h,i) for(a;b;c) for(d;e;f) for(g;h;i)

which would change the second example to:

double B[10][8][5];
int i,j,k;

FOR3(k=0,k<10,k++,i=0,i<8,i++,j=0,j<5,j++) {
    do_something_on_B(B[k][i][j]);
}

and the first example fares better too:

vector< vector< vector<int> > > A;
int i,j,k;
FOR3(k=0,k<A.size(),k++,i=0,i<A[k].size(),i++,j=0,j<A[k][i].size(),j++) {
    do_something_on_A(A[k][i][j]);
}

Hopefully you can tell fairly easily which statements go with which for statements. Also, beware the commas, now you can't use them in a single clause of any of the fors.

Michael
  • 9,060
  • 14
  • 61
  • 123
  • 1
    The readability of these is horrible. Jamming more than one `for` loop onto a line doesn't make it more readable it makes it *less*. –  Jan 09 '14 at 03:08
-2

Firstly, you shouldn't use a vector of vectors of vectors. Each vector is guaranteed to have contiguous memory, but the "global" memory of a vector of vectors isn't (and probably won't be). You should use the standard library type array instead of C-style arrays as well.

using std::array;

array<array<array<double, 5>, 8>, 10> B;
for (int k=0; k<10; k++)
    for (int i=0; i<8; i++)
        for (int j=0; j<5; j++)
            do_something_on_B(B[k][i][j]);

// or, if you really don't like that, at least do this:

for (int k=0; k<10; k++) {
    for (int i=0; i<8; i++) {
        for (int j=0; j<5; j++) {
            do_something_on_B(B[k][i][j]);
        }
    }
}

Better yet though, you could define a simple 3D matrix class:

#include <stdexcept>
#include <array>

using std::size_t;

template <size_t M, size_t N, size_t P>
class matrix3d {
    static_assert(M > 0 && N > 0 && P > 0,
                  "Dimensions must be greater than 0.");
    std::array<std::array<std::array<double, P>, N>, M> contents;
public:
    double& at(size_t i, size_t j, size_t k)
    { 
        if (i >= M || j >= N || k >= P)
            throw out_of_range("Index out of range.");
        return contents[i][j][k];
    }
    double& operator(size_t i, size_t j, size_t k)
    {
        return contents[i][j][k];
    }
};

int main()
{
    matrix3d<10, 8, 5> B;
        for (int k=0; k<10; k++)
            for (int i=0; i<8; i++)
                for (int j=0; j<5; j++)
                    do_something_on_B(B(i,j,k));
    return 0;
}

You could go further and make it fully const-correct, add matrix multiplication (proper and element-wise), multiplication by vectors, etc. You could even generalise it to different types (I'd make it template if you mainly use doubles).

You could also add proxy objects so you can do B[i] or B[i][j]. They could return vectors (in the mathematical sense) and matrices full of double&, potentially?

Miles Rout
  • 1,204
  • 1
  • 13
  • 26