I'm writing my first C library and I'm not sure which way to go about. For example a function to retrive string value from some data store can look:
int get_value(void * store, char ** result);
or
char * get_value(void * store, int * error);
I'm having hard time coming with any objective reason to prefer one over another, but than again, I don't write C that much. The return-error-code will look more consistent when multiple output parameters are present, however the return-value could be bit easier to use? Not sure.
Is there general consensus on which style is better and why or is just a personal preference?
There tend not to be good, "hard" answers to style-based questions like this. What follows are my opinions; others will disagree.
Having a function simply return its return value usually makes it easier for the caller -- as long as the caller is interested in getting answers, not necessarily in optimal error handling.
Having all functions return success/failure codes -- returning any other data via "result" parameters -- makes for clean and consistent error handling, but tends to be less convenient for callers. You're always having to declare extra variables (of the proper type) to hold return values. You can't necessarily write things like a = f(g());
.
Returning ordinary values ordinarily, and indicating errors via an "out of band" ordinary return value, is a popular technique -- the canonical example is the Standard C getchar
function -- but it can feel rather ad-hoc and error-prone.
Returning values via the return value, and error codes via a "return" parameter, is unusual. I can see the attraction, but I can't say I've ever used that technique, or would. If there needs to be an error return distinct from the return value, the "C way" (though of course this is generally a pretty bad idea, and now pretty strongly deprecated) is to use some kind of globalish variable, à la errno
.
If you want to pay any heed to the original "spirit of C", it was very much for programmer convenience, and was not too worried about rigid consistency, and was generally okay with healthy dollops of inconsistency and ad-hocciness. So using out-of-band error returns is fine.
If you want to pay heed to modern usage, it seems to be increasingly slanted towards conformity and correctness, meaning that consistent error return schemes are a good thing, even if they're less convenient. So having the return value be a success/failure code, and data returned by a result parameter, is fine.
If you want to have the return value be the return value, for convenience, and if errors are unusual, but for those callers who care you want to give them a way of getting fine-grained error information, a good compromise is sometimes to have a separate function to fetch the details of the most-recent error. This can still lead to the same kinds of race conditions as a global variable, but if your library uses some kind of "descriptors" or "handles", such that you can arrange to have this error-details function return the details of the most recent operation on a particular handle, it can work pretty well.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With