tags:

views:

471

answers:

13

Hi all, in a current project I dared to do away with the old 0 rule, i.e. returning 0 on success of a function. How is this seen in the community? The logic that I am imposing on the code (and therefore on the co-workers and all subsequent maintenance programmers) is:

.>0: for any kind of success/fulfillment, that is, a positive outcome

==0: for signalling no progress or busy or unfinished, which is zero information about the outcome

<0: for any kind of error/infeasibility, that is, a negative outcome

Sitting in between a lot of hardware units with unpredictable response times in a realtime system, many of the functions need to convey exactly this ternary logic so I decided it being legitimate to throw the minimalistic standard return logic away, at the cost of a few WTF's on the programmers side.

Opininons?

PS: on a side note, the Roman empire collapsed because the Romans with their number system lacking the 0, never knew when their C functions succeeded!

+4  A: 

It is fine as long as you document it well.

Paulo Scardine
+10  A: 

"Your program should follow an existing convention if an existing convention makes sense for it."

Source: The GNU C Library

iano
NICE ANSWER IANO.
Shalni
+9  A: 

By deviating from such a widely known convention, you are creating a high level of technical debt. Every single programmer that works on the code will have to ask the same questions, every consumer of a function will need to be aware of the deviation from the standard.

http://en.wikipedia.org/wiki/Exit_status

Sohnee
A: 

It is a convention only. I have worked with many api that abandon the principle when they want to convey more information to the caller. As long as your consistent with this approach any experienced programmer will quickly pick up the standard. What is hard is when each function uses a different approach IE with win32 api.

rerun
+2  A: 

I think it ultimately depends on the customers of your code.

In my last system we used more or less the same coding system as yours, with "0" meaning "I did nothing at all" (e.g. calling Init() twice on an object). This worked perfectly well and everybody who worked on that system knew this was the convention.

However, if you are writing an API that can be sold to external customers, or writing a module that will be plugged into an existing, "standard-RC" system, I would advise you to stick to the 0-on-success rule, in order to avoid future confusion and possible pitfalls for other developers.

And as per your PS, when in Rome, do like the romans do :-)

Eldad Mor
+1 for turning "when in Rome" on the OP :-)
R..
+7  A: 

I think you're overstating the status of this mythical "rule". Much more often, it's that a function returns a nonnegative value on success indicating a result of some sort (number of bytes written/read/converted, current position, size, next character value, etc.), and that negative values, which otherwise would make no sense for the interface, are reserved for signalling error conditions. On the other hand, some functions need to return unsigned results, but zero never makes sense as a valid result, and then zero is used to signal errors.

In short, do whatever makes sense in the application or library you are developing, but aim for consistency. And I mean consistency with external code too, not just your own code. If you're using third-party or library code that follows a particular convention and your code is designed to be closely coupled to that third-party code, it might make sense to follow that code's conventions so that other programmers working on the project don't get unwanted surprises.

And finally, as others have said, whatever your convention, document it!

R..
A: 

In my opinion (and that's the opinion of someone who tends to do out-of-band error messaging thanks to working in Java), I'd say it is acceptable if your functions are of a kind that require strict return-value processing anyway.

So if the return value of your method has to be inspected at all points where it's called, then such a non-standard solution might be acceptable.

If, however, the return value might be ignored or just checked for success at some points, then the non-standard solution produces quite some problem (for example you can no longer use the if(!myFunction()) ohNoesError(); idiom.

Joachim Sauer
A: 

What is your problem? It is just a convention, not a law. If your logic makes more sense for your application, then it is fine, as long as it is well documented and consistent.

codymanix
A: 

On Unix, exit status is unsigned, so this approach won't work if you ever have to run your program there, and this will confuse all your Unix programmers to no end. (I looked it up just now to make sure, and discovered to my surprised that Windows uses a signed exit status.) So I guess it will probably only mostly confuse your Windows programmers. :-)

I'd find another method to pass status between processes. There are many to choose from, some quite simple. You say "at the cost of a few WTF's on the programmers side" as if that's a small cost, but it sounds like a huge cost to me. Re-using an int in C is a miniscule benefit to be gained from confusing other programmers.

Ken
I believe he's talking about returning from a function, not the process itself.
Dusty
Ah, I'm an idiot.
Ken
+1  A: 

I think you should follow the Principle Of Least Astonishment

The POLA states that, when two elements of an interface conflict, or are ambiguous, the behaviour should be that which will least surprise the user; in particular a programmer should try to think of the behavior that will least surprise someone who uses the program, rather than that behavior that is natural from knowing the inner workings of the program.

If your code is for internal consumption only, you may get away with it, though. So it really depends on the people your code will impact :)

samy
A: 

You need to go on a case by case basis. Think about the API and what you need to return. If your function only needs to return success or failure, I'd say give it an explicit type of bool (C99 has a bool type now) and return true for success and false for failure. That way things like:

if (!doSomething())
{
    // failure processing
}

read naturally.

In many cases, however, you want to return some data value, in which case some specific unused or unlikely to be used value must be used as the failure case. For example the Unix system call open() has to return a file descriptor. 0 is a valid file descriptor as is theoretically any positive number (up to the maximum a process is allowed), so -1 is chosen as the failure case.

In other cases, you need to return a pointer. NULL is an obvious choice for failure of pointer returning functions. This is because it is highly unlikely to be valid and on most systems can't even be dereferenced.

JeremyP
I suppose "highly unlikely" is an accurate description of "probability zero", but it's misleadingly non-specific. A null pointer is required by the C language to compare not equal to any valid pointer to an object.
R..
I'm thinking of possible embedded systems with operating systems that might allow access to address zero and C compilers that allow that.
JeremyP
A: 

One of the most important considerations is whether the caller and the called function or program will be updated by the same person at any given time. If you are maintaining an API where a function will return the value to a caller written by someone who may not even have access to your source code, or when it is the return code from a program that will be called from a script, only violate conventions for very strong reasons.

You are talking about passing information across a boundary between different layers of abstraction. Violating the convention ties both the caller and the callee to a different protocol increasing the coupling between them. If the different convention is fundamental to what you are communicating, you can do it. If, on the other hand, it is exposing the internals of the callee to the caller, consider whether you can hide the information.

Dale Gulledge
+1  A: 

There is nothing wrong with doing it that way, assuming you document it in a way that ensures others know what you're doing.

However, as an alternative, if might be worth exploring the option to return an enumerated type defining the codes. Something like:

enum returnCode {
    SUCCESS, FAILURE, NO_CHANGE
}

That way, it's much more obvious what your code is doing, self-documenting even. But might not be an option, depending on your code base.

Roadrunner-EX