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

Time:01-06

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 ...

CodePudding user response:

It sounds like you want to ensure tail call optimization. At present there's no standard way to do this, but if you're using Clang you can use the clang::musttail attribute.

CodePudding user response:

Loop might preserve code structure:

void test2()
{
    int i = 0;
    for (;;) {
        i  ;
        if (i > 10) break;
        continue;
    }
    exit(EXIT_SUCCESS);
}

even if

void test3()
{
    for (int i = 0; i < 11;   i) {
        /*Empty*/
    }
    exit(EXIT_SUCCESS);
}

would be more idiomatic (assuming some useful code as the loop is useless actually).

CodePudding user response:

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.)

  •  Tags:  
  • Related