Invoking return through a function?

Chris Angelico rosuav at gmail.com
Tue Oct 31 02:59:08 EDT 2017


On Tue, Oct 31, 2017 at 4:32 PM, Steve D'Aprano
<steve+python at pearwood.info> wrote:
> On Tue, 31 Oct 2017 02:34 pm, Chris Angelico wrote:
>
>> On Tue, Oct 31, 2017 at 2:00 PM, Steve D'Aprano
>> <steve+python at pearwood.info> wrote:
>>> Python has no GOTO, fortunately, but C has at least two, GOTO and LONGJMP.
>>> A C macro could, if I understand correctly, jump into the middle of another
>>> function. (Yay for spaghetti code!)
>>
>> No, I don't think you do understand them correctly - or at least, I
>> don't know of any way for a C macro to jump into the middle of a
>> function.
>
> I presume a macro could contain a call to longjmp, yes? Since longjmp can jump
> into another function (albeit only one which has prepared for it in advance),
> so can the macro.

I'd still consider longjmp to be jumping OUT, not IN - you can't skip
the beginning of a function and jump half way into it. For instance:

void foo() {
    printf("Start of function foo\n");
    SOME_EVIL_MACRO
    printf("End of function foo\n");
}

void bar() {
    SOME_OTHER_EVIL_MACRO
}

int main() {
    bar();
    return 0;
}

To my knowledge, there's no way to have bar() run just the second half
of foo(). The only way to have bar "jump into" foo is to first call
foo, which would look like this:

jmp_buf buf;
void foo() {
    printf("Start of function foo\n");
    if (!setjmp(buf)) {
        printf("Middle of function foo\n");
        bar();
        printf("This won't happen");
    } else {
        printf("After longjmp\n");
    }
    printf("End of function foo\n");
}

void bar() {
    printf("Start of function bar\n");
    longjmp(buf, 1);
    printf("This won't happen\n");
}

The Python equivalent would be:

def foo():
    print("Start of function foo")
    try:
        print("Middle of function foo")
        bar()
        print("This won't happen")
    except Exception:
        print("After longjmp/exception")
    print("End of function foo")

def bar():
    print("Start of function bar")
    raise Exception
    print("This won't happen")

So if longjmp can jump into a function, so can Python's raise
statement. Yes, it's nonlocal flow control; but it's not an
uncontrolled goto that lets you jump into functions in any normal
sense of that expression.

> https://stackoverflow.com/questions/21355110/how-to-goto-into-different-function-in-c
>
> And what about assembly? Couldn't you jump into a function from assembly? Of
> course the stack will be all wrong, but if you're using assembly you have to
> manage that yourself.

Oh sure, but that's like saying "I can change the value of a Python
string using ctypes, ergo Python strings aren't immutable". When you
peel away an abstraction layer, you get to see the underlying code.
What you do with that is your responsibility.

>> There are three quite different things mentioned here.
>>
>> 1) The 'goto' statement, which unconditionally jumps you to another
>> location *in the same function*
>
> Yes. And a large enough function can contain everything. If you wanted to
> write BASIC-style unstructured code, you could dump everything into one
> function and use GOTO.

Well sure. C certainly won't  stop you from being an idiot :) And
Python (or at least CPython) would let you alter the bytecode and
actually insert jump opcodes. And, of course, you can hide the abuse
behind a decorator, which is as much "action at a distance" as CPP
macros.

The trouble with trying to make yourself stupider than you really are
is that you very often succeed. CS Lewis wasn't talking about software
design, but he was pretty much spot on... :D

ChrisA



More information about the Python-list mailing list