Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Looping without a loop

Tags:

c++

We have been restricted us to not use a loop in a program as a programming challenge.

Restrictions: You can not use while, for, goto and recursion.

The restrictions are pretty daunting. I couldn't really think of any proper solution. So I opted for this one which is achieved by modifying the return address.

Could this be any better?

#include <unistd.h>
#include <sys/mman.h>

#include <iostream>
#include <cstring>

void the__(){}
void magic__(){}
void loop__(){}
void function__(){}
void here__(){}

template <typename T>
struct for_
{
    bool started = false;
    void* fix = nullptr;
    void(*body)(T&) = nullptr;

    for_(void(*body)(T&))
        : body(body)
    {
        auto do_for__ = uintptr_t(do_for_);
        uint64_t magic[] = {5243466812662057800, 6135086863767628931ull, 10416984888688609608ull, 144};
        mprotect((void*)(do_for__-do_for__%4096), 4096, 7);
        std::memcpy((void*)(do_for__+135), magic, 25);
    }

    static void do_for_(T& ctx)
    {
        void** p = (void**)((char*)&p+16);
        if (!ctx.started)
        {
            if (!ctx) return;
            ctx.started = true;
            ctx.fix = *p;
            *p = (void*)do_for_;
        }

        ctx.body(ctx);
        ctx.next();

        if (ctx)
        {
            the__();
            magic__();
            loop__();
            function__();
            here__();
        }
        else
        {
            *p = ctx.fix;
        }
    }
};

struct For0ToN : for_<For0ToN>
{
    For0ToN(int N, void(*f)(For0ToN&))
        : for_<For0ToN>(f)
        , N(N)
    {
        do_for_(*this);
    }

    operator bool() {return i < N;}
    operator int() {return i;}
    void next() {i++;}
    int count() {return i;}
    int i = 0, N = 0;
};

int main()
{
    For0ToN(10, +[](For0ToN& i)
    {
        std::cout << int(i) << ": ";
        For0ToN(i.count(), +[](For0ToN& i)
        {
            std::cout << int(i) << ". ";
        });
        std::cout << "\n";
    });
    std::cout << "done\n";
    return 0;
}

The code is demonstrated here: https://coliru.stacked-crooked.com/a/3dd77ade501ac748

like image 732
Khal Buyo Avatar asked Dec 31 '22 06:12

Khal Buyo


1 Answers

You could use longjmp. Here's an example from cppreference:

#include <csetjmp>
#include <iostream>

std::jmp_buf jump_buffer;

[[noreturn]] void a(int count) 
{
    std::cout << "a(" << count << ") called\n";
    std::longjmp(jump_buffer, count+1);  // setjmp() will return count+1
}

int main() { 
    // loop from 0-9

    volatile int count = 0; // local variables must be volatile for setjmp
    if (setjmp(jump_buffer) != 10) {
        a(count++);  // This will cause setjmp() to exit
    }
}
like image 141
Ted Lyngmo Avatar answered Jan 14 '23 12:01

Ted Lyngmo