Search code examples
c++recursiongoto

Is there a way to call a function without adding to the call stack?


I've some goto-laden C++ code that looks like

#include <stdlib.h>
void test0()
{
    int i = 0;
loop:
    i++;
    if (i > 10) goto done;
    goto loop;
done:
    exit(EXIT_SUCCESS);
}

I'd like to get rid of the gotos while (mostly) preserving the appearance of the original code; that more-or-less rules-out for, while etc. (unless they're hidden behind macros) as that changes the appearance of the existing code too much. Imagine wanting to minimize the "diffs" between existing code and changed code.

One idea is to use a class with methods:

class test1 final
{
    int i = 0;
    void goto_loop()
    {
        i++;
        if (i > 10) goto_done();
        goto_loop();
    }
    void goto_done()
    {
        exit(EXIT_SUCCESS);
    }
public:
    test1() { goto_loop(); }
};

This works, but every call to goto_loop() adds to the call stack. Is there some way to do an exec-like function call? That is, call a function "inline" somehow...execute additional code without adding to the call stack? Is there a way to make tail-recursion explicit?

Using C++20 (or even C++23) is acceptable, although a C++17 solution would be "nice."


For all those wondering about "why?" The real original code is BASIC ...


Solution

  • My solution is to write an exec() routine that stops the recursion:

    template<typename Func>
    void exec(const Func& f)
    {
        using function_t = Func;
        static std::map<const function_t*, size_t> functions;
        const auto it = functions.find(&f);
        if (it == functions.end())
        {
            functions[&f] = 1;
            while (functions[&f] > 0)
            {
                f();
                functions[&f]--;
            }
            functions.erase(&f);
        }
        else
        {
            functions[&f]++;
        }
    }
    

    With that utility, I can more-or-less preserve the appearance of the existing code

    class test4 final
    {
        int i = 0;
        void goto_loop_() {
            i++;
            if (i > 10) goto_done(); } 
        void goto_loop() { goto_loop_(); static const auto f = [&]() { goto_loop(); }; exec(f); }
        void goto_done()
        {
            exit(EXIT_SUCCESS);
        }
    public:
        test4() { goto_loop(); }
    };
    

    (Using a lambda avoids hassles with pointers to members functions.)