9

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?

graywolf
  • 7,092
  • 7
  • 53
  • 77
  • 1
    This is totally opinion-based, but I prefer the first one. However I would change it to `int get_value(void * store, char * result, int max_len);` so the caller allocates the memory and memory ownership is not transferred back and forth. – mnistic Jun 04 '18 at 22:36
  • It's a personal preferrence and from what I've encountered the first form seems to be more prevalent. Usually negative ints mean an error, nonnegative success. Some use 0 for fail, and 1 for success, but that's better tied to _Bool's. Pthread and fam use 0 for success and non-0 (generaly positive) for the error code. – Petr Skocik Jun 04 '18 at 22:47
  • 1
    The general consensus is to do it the way your colleagues are doing it. – jxh Jun 04 '18 at 23:10
  • related: https://stackoverflow.com/q/385975/10396 – AShelly Jun 04 '18 at 23:16
  • @AShelly definitelly related, thank you :) – graywolf Jun 04 '18 at 23:26

1 Answers1

12

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.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103