I recently had a job interveiw where I was asked how to write a function that will return from the function it is called from. It will will, in essence, perform `return` twice.

I answered that I would use macros. They were not satisfied, and tried to lead me to the answer they wanted. I didn't get to it, and I'm not ashamed of not knowing this. This is weird!

But when I thought about it, it occured to me that I actually knew of a place where I would have use for such a thing. So I asked #C++ on Rizon.

< FlyingJester > How would I write a function that is equivalent to the statement `return`?
< FlyingJester > As in, calling it returns you from the current function.
< Subv > you can't, you'd have to use a macro

< solarpanelist > goto

< Subv > you can't goto outside of a function

< FlyingJester > This was an interview question. I said I should use a macro. That was not what they were looking for.

< Subv > #define ret() return

< FlyingJester > That wasn't the answer, apparently.

< FlyingJester > And now I can't find a way to do it.

< Subv > i suppose you could also use __asm { retn } and let the stack all messed up

< FlyingJester > Yeah...I kind of think they wanted some insane inline assembly answer...

< Palitroque-kun > { cout < < "noreturn!"; __asm("movq $0, %rax; leave; ret"); }

< walnut > Output > noreturn!

< Palitroque-kun > there you go

< FlyingJester > And so if I put that in a function, and the call stack was, say, three high, it would take my up two levels.

< Palitroque-kun > i don't think so

< FlyingJester > That was a the question. Write a function that, when called, returns from the function it is called from.

< FlyingJester > Basically an actual function that behaves like #define ret(x) return

< Palitroque-kun > walnut: inline void ret(void) { __asm("movq $0, %rax; leave; ret"); } int main(void) { cout < < "noreturn!"; ret(); cout < < "test"; }

< walnut > Output > noreturn!test

< Palitroque-kun > it's not inlining it...

< Palitroque-kun > walnut: static inline void ret(void) { __asm volatile("movq $0, %rax; leave; leave; ret"); } int main(void) { cout < < "noreturn!"; ret(); cout < < "test"; }

< walnut > Output > noreturn!

< Palitroque-kun > BAM!

< Palitroque-kun > there you go

< Palitroque-kun > walnut: void ret(void) { __asm volatile("movq $0, %rax; leave; leave; ret"); } int main(void) { cout < < "noreturn!"; ret(); cout < < "test"; }

< walnut > Output > noreturn!

< DeepBlueSea > how do you know this didnt crash?

< Palitroque-kun > as i said

< DeepBlueSea > the problem is Palitroque-kun, in cdecl the caller cleans up, not the callee

< Palitroque-kun > the leave instruction takes care of that for you

< FlyingJester > It doesn't matter.

< Palitroque-kun > it restores the stack for you

< Palitroque-kun > i leave twice, one for the ret() function and one for main()

< Palitroque-kun > then i call ret

< FlyingJester > As long as someone cleans up, and only one side cleans up, you can violate the calling convention all you want. So long as when you are done, it looks the same as it would if you played nice.

< Palitroque-kun > since i no longer have the return address for ret()

< Palitroque-kun > i return from main() instead

< Palitroque-kun > walnut: void ret(void) { __asm volatile("movq $0, %rax; leave; leave; ret"); } void foo(int i) { cout < < i; ret(); cout < < " " < < i; } int main(void) { foo(10); cout < < "noreturn!"; ret(); cout < < "test"; }

< walnut > Output > 10noreturn!

< Palitroque-kun > further proving my point

< FlyingJester > walnut: void ret(void) { __asm volatile("movq $0, %rax; leave; leave; ret"); } void wrap(){cout < < "first test"; ret(); cout < < "skip this test"; } int main(void) { cout < < "noreturn!"; wrap(); cout < < "second test"; }

< walnut > Output > noreturn!first testsecond test

< FlyingJester > Yes.

< DeepBlueSea > it depends, on cdecl you only have to restore stack and basepointer...leave works there. but for other calling conventions you need to unwind stack prior to return

< FlyingJester > Well, then we can just specify the functions to use cdecl.

< FlyingJester > Or simply say this only works in certain situations.

< Palitroque-kun > walnut: void ret(void) { __asm volatile("movq $0, %rax; leave; leave; ret"); } void foo(int i, int b, int c, int d, int e, int f, int g) { cout < < (i+b+c+d+e+f+g); ret(); cout < < " " < < i; } int main(void) { foo(10, 10, 10, 10, 10, 10, 10); cout < < "noreturn!"; ret(); cout < < "test"; }

< walnut > Output > 70noreturn!

< DeepBlueSea > it works for all situations where the caller cleans up the stack and not the callee
< DeepBlueSea > or where everything is passed in registers

So, for future reference, there is an example:

void ret(void) { __asm volatile("movq $0, %rax; leave; leave; ret"); }