0

I want to make my own while-loop like this.

    MY_OWN_LOOP(a, b, c) {
        user-statement1;
        user-statement2;
    }

I hope the above loop be expanded as followings.

    predefined-function1(a,b);
    while (1) {
        predefined-function2(c);

        user-statement1;
        user-statement2;

        predefined-function3(a,b,c);
    }

Is there any way to make a c++ macro (or something like that) which does like above?

  • 1
    You can do that in the language without a macro. The body of the loop would be a 4th argument as a lambda. – Eljay May 15 '20 at 16:40
  • I would definitely agree with @Eljay that a lambda would be better; however, if you really must use a macro, and want to define it on more than one line, then you should read this: [Multi line preprocessor macros](https://stackoverflow.com/q/10419530/10871073). – Adrian Mole May 15 '20 at 16:47
  • 2
    Macros are a *last resort*. Not something you should *ever* reach for, unless there is no other option. – Jesper Juhl May 15 '20 at 16:48

1 Answers1

0

An easy way to do it is to pass the block as one of the macro arguments, so something like this:

#include <iostream>

#define MY_OWN_LOOP(A, B, C, ...) do { \
    auto&& a = (A); \
    auto&& b = (B); \
    auto&& c = (C); \
\
    std::cout << "predefined-function1(" << a << ", " << b << ")\n"; \
    while (1) { \
        std::cout << "predefined-function2(" << c << ")\n"; \
        { \
            __VA_ARGS__ \
        } \
        std::cout << "predefined-function3(" << a << ", " << b << ", " << c << ")\n";\
    } \
} while (0)

int main() {
    int counter = 2;
    MY_OWN_LOOP('a', 'b', 'c', {
        // Can also access variables `a`, `b` and `c` here
        if (--counter < 0) break;
        std::cout << "user-statement, counter = " << counter << "\n";
    });
}

(Where std::cout << "predefined-function" is replaced with what you want, and instead of MY_OWN_LOOP(a, b, c) { BLOCK }, MY_OWN_LOOP(a, b, c, { BLOCK }))


You can also use a helper function, but it gets complicated trying to handle continue and break. If you don't need to use control flow, a simple function like this would work:

#include <iostream>

template<class A, class B, class C, class F>
void my_own_loop(A&& a, B&& b, C&& c, F&& f) {
    std::cout << "predefined-function1(" << a << ", " << b << ")\n";
    while (1) {
        std::cout << "predefined-function2(" << c << ")\n";
        f(a, b, c);  // Or don't pass the arguments if you don't need them
        std::cout << "predefined-function3(" << a << ", " << b << ", " << c << ")\n";\
    }
}

int main() {
    int counter = 2;
    my_own_loop('a', 'b', 'c', [&](const auto& a, const auto& b, const auto& c) {
        if (--counter < 0) std::exit(0);
        std::cout << "user-statement, counter = " << counter << "\n";
    });
}

Alternative designs could be certain exceptions thrown or values returned signifying continue or break. For example:

#include <iostream>

enum class control_flow {
    none,
    break_,
    continue_
};

template<class A, class B, class C, class F>
void my_own_loop(A&& a, B&& b, C&& c, F&& f) {
    std::cout << "predefined-function1(" << a << ", " << b << ")\n";
    while (1) {
        std::cout << "predefined-function2(" << c << ")\n";
        control_flow x = f(a, b, c);
        if (x == control_flow::break_) break;
        if (x == control_flow::continue_) continue;
        std::cout << "predefined-function3(" << a << ", " << b << ", " << c << ")\n";\
    }
}

int main() {
    int counter = 2;
    my_own_loop('a', 'b', 'c', [&](const auto& a, const auto& b, const auto& c) {
        if (--counter < 0) return control_flow::break_;
        std::cout << "user-statement, counter = " << counter << "\n";
        return control_flow::none;
    });
}
Artyer
  • 31,034
  • 3
  • 47
  • 75