It is all up to you, if the caller is your code. If the caller is not under your control, you have to either follow their existing convention or develop your own convention together.
For example, on x86 platform when floating-point arithmetic is processed by FPU instructions, the result of a function is returned as the top value on the FPU register stack. (If you know, x86 FPU registers are organized into a "circular stack" of sorts). At that moment it is neither float
nor double
, it is a value stored with internal FPU precision (which could be higher than float
or double
) and it is the caller's responsibility to retrieve that value from the top of FPU stack and convert it to whatever type it desires. In fact, that is how a typical FPU instruction works: it takes its arguments from the top of FPU stack and pushes the result back onto FPU stack. By implementing your function in the same way you essentially emulate a "complex" FPU instruction with your function - a rather natural way to do it.
When floating-point arithmetic is processed by SSE instructions, you can choose some SSE register for the same purpose (use xmm0
just like you use EAX
for integers).
For complex structures (i.e. ones that are larger than a register or a pair of registers), the caller would normally pass a pointer to a reserved buffer to the function. And the function would put the result into the buffer. In other words, under the hood, functions never really "return" large objects, but rather construct them in a caller-provided memory buffer.
Of course, you can use this "memory buffer" method for returning values of any type, but with smaller values, i.e. values of scalar type, it is much more efficient to use registers than a memory location. This applies, BTW, to small structures as well.
Enums are usually just a conceptual wrapper over some integer type. So, there's no difference between returning a enum or an integer.