15

Perhaps it is not good programming practice, but is it possible to define a for loop macro?

For example,

#define loop(n) for(int ii = 0; ii < n; ++ ii)

works perfectly well, but does not give you the ability to change the variable name ii.

It can be used:

loop(5)
{
    cout << "hi" << " " << "the value of ii is:" << " " << ii << endl;
}

But there is no choice of the name/symbol ii.

Is it possible to do something like this?

loop(symbol_name, n)

where the programmer inserts a symbol name into "symbol_name".

Example usage:

loop(x, 10)
{
    cout << x << endl;
}
Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225
  • 2
    **Definitively a BAD practice.** Use C++ range-based loop whenever possible. https://en.cppreference.com/w/cpp/language/range-for. – Phil1970 Mar 09 '19 at 13:53

9 Answers9

41
#define loop(x,n) for(int x = 0; x < n; ++x)
prajmus
  • 3,171
  • 3
  • 31
  • 41
  • If you need an `unsigned` or `long` type, the macro is useless. If `n` is a call to a function, it would be hidden in the macro. – Phil1970 Mar 09 '19 at 13:55
  • this is correct but bad practice, see answer below: https://stackoverflow.com/a/24409355/683918 – atb Dec 10 '19 at 14:39
27

In today's C++ we wouldn't use a macro for this, but we'd use templates and functors (which includes lambda's):

template<typename FUNCTION>
inline void loop(int n, FUNCTION f) {
  for (int i = 0; i < n; ++i) {
    f(i);
  }
}
// ...
loop(5, [](int jj) { std::cout << "This is iteration #" << jj << std::endl; } );

The loop function uses the variable i internally, but the lambda doesn't see that. It's internal to loop. Instead, the lambda defines an argument jj and uses that name.

Instead of the lambda, you could also pass any function as long as it accepts a single integer argument. You could even pass std::to_string<int> - not that loop would do something useful with the resulting strings, but the syntax allows it.

[edit] Via Mathemagician; you can support non-copyable functors using

template<typename FUNCTION>
inline void loop(int n, FUNCTION&& f) {
  for (int i = 0; i < n; ++i) {
    std::forward<FUNCTION>(f)(i);
  }
}

[edit] The 2020 variant, which should give better error messages when passing inappropriate functions.

inline void loop(int n, std::invocable<int> auto&& f) {
  for (int i = 0; i < n; ++i) {
    std::invoke(f,i);
  }
}
MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 2
    Could you extend your answer: Why is this better than a preprocessor define? If the intent is just iterating N times? – Sergio Basurco Oct 05 '15 at 14:22
  • 9
    @chuckleplant: Typesafe, proper parsing, no name leakage. – MSalters Oct 05 '15 at 22:50
  • This has limited use. If your purpose is to initialize an array, then you need access to the array which is outside of this templated function, this is impossible unless you add the array as argument to the function -- this make the templated function less generic/useful. I believe in this case the old C macro wins. – Taozi Feb 05 '16 at 01:03
  • 1
    @Taozi: To initialize an array, you'd normally use `std::fill( )` instead of the `loop`. However, your assumption about needing to pass the array as a parameter is wrong : `loop(5, [&](int jj) { array[jj] = 42 + j; }` – MSalters Feb 05 '16 at 15:31
  • @MSalters Thanks, didn't know the [&] syntax before :) – Taozi Feb 08 '16 at 17:41
  • if we pass F by value, would that affect the performance somehow? Or does std::move kicks in automatically? – Mathemagician Dec 02 '19 at 20:46
  • @Mathemagician: `std::move` doesn't actually move, or kick in automatically. Move constructors do the actual moving. They'll be used when passing a temporary by value to F. You can still use `std::move` yourself. – MSalters Dec 03 '19 at 08:04
  • @MSalters Thanks! Would it be better if we use std::forward<>f()() instead of the way it is presented now? – Mathemagician Dec 03 '19 at 15:56
  • @Mathemagician: There's probably a type on there; `std::forward<>f()()` doesn't compile. And I fail to see what you're trying to achieve. – MSalters Dec 03 '19 at 16:57
  • @MSalters https://stackoverflow.com/questions/31280832/should-i-pass-a-lambda-by-const-reference – Mathemagician Dec 03 '19 at 16:58
  • @Mathemagician: Fair point that, but note the `Function&&` in addition to `std::forward`. – MSalters Dec 03 '19 at 17:01
  • right, I didn't get the grammar right... c++ newbie here:) – Mathemagician Dec 03 '19 at 17:02
  • I think in c++ 20, this question will have received a good solution with the range library. Something like `for (auto i : iota(1, 10))` if I remember correctly.. – Mathemagician Dec 03 '19 at 17:04
8
#define loop(x, n) for(int x = 0; x < n; ++ x)

Something like this?

#include <iostream>
using namespace std;

#define loop(x, n) for(int x = 0; x < n; ++ x)

int main() {

    loop(i, 10)
    {
        cout << i << endl;
    }

    return 0;
}
ArthurChamz
  • 2,039
  • 14
  • 25
3

You can define a variable name as a first parameter for a macro:

#define loop(variable, n) for(int variable = 0; variable < n; ++variable )

Note, there is a rule that most experienced programmers follow - use uppercase identifiers for macros. In your case, imagine you have a function and macro:

#define loop(variable, n) for(int variable = 0; variable < n; ++variable )

void loop();

Now try to call that function in your code and watch what ugly error messages you are getting. Some of them could be not easy to understand at all. Even worse is having that loop function in a namespace or method in a class and does not help at all.

So at least have it this way:

#define LOOP(variable, n) for(int variable = 0; variable < n; ++variable )

but better not use it at all.

Slava
  • 43,454
  • 1
  • 47
  • 90
2
#define loop(VARIABLE, n) for(int VARIABLE = 0; VARIABLE < n; ++ VARIABLE)

You can try this.

t.c
  • 623
  • 5
  • 19
2

Use:

#include <vector>
#include <cstdio>

using std::printf;

#define FOR(TYPE, IDENT, BEGIN, END) for(TYPE IDENT = BEGIN, IDENT##_end = static_cast<decltype(IDENT)>(END); IDENT < IDENT##_end; ++IDENT)
#define FOR_STEP(TYPE, IDENT, BEGIN, END, STEP) for(TYPE IDENT = (TYPE)(BEGIN), IDENT##_end = static_cast<decltype(IDENT)>(END); IDENT < IDENT##_end; IDENT += STEP )
#define FOR_ITER(IDENT, BEGIN, END) for(auto IDENT = BEGIN, IDENT_end = END; IDENT != IDENT_end; ++IDENT)

int main() {
  FOR(int, i, 0, 10) {
    printf("FOR i: %d\n", i);
    printf("we can even access i_end: %d\n", i_end);
  }

  FOR(auto, i, 0, 10) {
    printf("FOR auto i: %d\n", i);
  }

  std::vector<int> vec = {4, 5, 7, 2, 3, 1, 4, 9, 8, 6};

  printf("FOR with iterator: {");
  FOR(auto, it, vec.begin(), vec.end()) {
    printf("%d, ", *it);
  }
  printf("}\n");

  printf("FOR with non constant end:\n");
  FOR(long, i, 0, vec.size()) {
    printf("vec[%ld] = %d\n", i, vec[i]);
  }
  printf("\n");


  // You can set a step size
  printf("FOR_STEP(double, d, 0, 20, 2.1): ");
  FOR_STEP(double, d, 0, 20, 2.1) {
    printf(" %f ", d);
  }
  printf("\n");

  // It works with iterators that don't have "<" but only "!="
  // defined, but you probably want to use a range-based 'for' anyway.
  printf("FOR_ITER(auto, it, vec.begin(), vec.end()): ");
  FOR(auto, it, vec.begin(), vec.end()) {
    printf("%d, ", *it);
  }
  printf("\n");
}

This is the best I could come up with for a C++11 target. It makes sure the END argument is only evaluated once and requires that begin and end have the same type, which is almost always the case.

Since you should not test doubles on equality with an == or != operator, the default FOR macros uses an < operator for comparison, this limits the usage of the macro for iterators, because they now need to have the < operator defined, too. I could not come up with one solution that works with double and arbitrary iterators alike; you can choose one macro that suits your needs better.

But you should not introduce all of the macros to you code, because with just one macro definition that suits most needs, the code will be much more readable.

Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
Arne
  • 7,921
  • 9
  • 48
  • 66
1

In competition programming, it is necessary to code as fast as you can. One way to speed up coding is using macros. Competition programmers use macro below to abbreviate "for loops" :

#define For(i,j,n) for(int i=(j);i<((int)n);++i)

For example the code below will print 1 to 10.

#include <stdio.h>
#define For(i,j,n) for(int i=(j);i<((int)n);++i)

int main(void)
{
   For(i, 1, 11)
   {
       printf("%d\t", i);
   }
}

But macros are dangerous, picky, and just not that safe. I really recommend you not to use them in your projects. To show this issue consider the code below.

#include <stdio.h>
#define For(i,j,n) for(int i=(j);i<((int)n);++i)

int main(void)
{
   int end = 10;
   For(i, 1, end++)
   {
       printf("%d\t", i);
   }
}

If you run the code, you'll see that the program never stop working, although you may expect that the output should the same 1 to 10.

1

For two-way for loop which replaces both, for(auto i = 0; i < n; ++i) and for(auto i = n - 1; i >= 0; --i) in one macro.

You could use this,

#define F(i, st, n) for (auto i = st-(st > n); (i < n)^(st > n); i += 1-2*(st > n))

Explanation: If start <= n, i = st; i < n; i += 1 otherwise i = st - 1; i >= n; i += -1

Note that the xor operator (i < n)^(st > n) complements the function appropriately.

Alternatively, you could also use the ternary operator (st > n)?(i >= n):(i < n) instead of the xor operator. Like this,

#define F(i, st, n) for (auto i = st-(st > n); (st > n)?(i >= n):(i < n); i += 1-2*(st > n))

Since time overhead is minimal, it should work equally effectively.

Remember, both F(i, 0, m) and F(i, m, 0) include 0 and exclude m.

0

try like this- #define loop(i,n) for((i) = 0; (i) < (int)(n); (i)++)

Ashish
  • 450
  • 4
  • 5