Is iio_err() supposed to return 0 for NULL pointers?
Hi,
I'm using sth. like the following code snippet to setup IIO devices and channels:
dev = iio_context_find_device(ctx, "iio-device-name");
err = iio_err(dev);
if (err) shutdown();
ch = iio_device_find_channel(dev, "voltage0", false);
In the normal case this works as expected. To test the error paths in my code, I just unloaded the IIO kernel driver being used and was greated with a null-ptr deref on dev in iio_device_find_channel(). The error could be easily fixed with an explicit NULL check:
if (err || !dev) shutdown();
However, I wondered if iio_err() should handle this case implicitly to reduce the error handling boilerplate for users?
Edit: This was observed on 2c5c1f8a3df76852742b274b55a5c77bae73ddc6.
Hi, To answer the question in the title, yes, iio_err() returns 0 for NULL pointers. This can be seen here: https://github.com/analogdevicesinc/libiio/blob/aa867f0a5c9d818156272a7002333316c119be9a/include/iio/iio.h#L360
Note that dev doesn't need to be checked with iio_err(). Only some functions return pointer-encoded errors. You can quickly identify them by searching for the keyword "pointer-encoded error" in the iio.h file.
I think the point is - having a consistent user experience in the API makes things easier to use - having some functions returns nulls on error, and others having pointer-encoded errors - means that you require every end user to check the iio.h file on every function they user to make sure they are checking things properly...
That seems like not a friendly library to me...
Since this hasn't see an update in a couple weeks...
Why Consistent Return Codes Are a Good Idea
- Predictability for Users:
- Developers quickly learn the pattern: e.g., “functions return 0 on success, negative errno-style codes on error.”
- It reduces the learning curve and allows developers to focus on what they’re doing, not how to interpret every individual function’s return value.
- Easier for new users and contributors to pick up and use the library correctly.
- Easier Error Handling:
- When errors follow a common pattern, error handling logic can be abstracted or reused (e.g., a wrapper that logs or converts errors).
- Uniform error checking (e.g., if (ret < 0)) is simpler and safer.
- Reduces boilerplate when wrapping or chaining library calls.
- Improved Debugging and Logging:
- Consistent values make it easier to grep logs or trace failures.
- Errors can be standardized and tied to clear messages (e.g., via strerror() or a custom error string table).
- Avoids needing to write custom formatting logic for each function’s unique return style.
- Better API Documentation and Readability:
- You don’t need to memorize special cases—just one rule (e.g., "NULL on error", or "pointer on success, ERR_PTR() on failure").
- Documentation can be cleaner, and example code more readable.
- Tooling and Static Analysis:
- Linters and static analyzers can more easily flag improper handling if there’s a consistent convention to follow.
- Makes test coverage more meaningful when the error surface is consistent.
What Happens When an API Mixes NULL and Pointer-Encoded Errors
- Developer Confusion:
- Developers waste time digging into source code to figure out what a function actually returns.
- People may incorrectly assume a return value style based on a similar-looking function.
- Raises the barrier to entry for using the API correctly, especially for non-experts.
- Error-Prone Code:
- It's easy to miss error cases or misinterpret a result, especially if one function returns NULL and another returns ERR_PTR(-EINVAL).
- Code that works now may silently break if the underlying function’s return style changes.
- Defensive programming becomes the norm, cluttering client code with multiple conditionals.
- Inconsistent Error Checking:
- Developers may forget to check both NULL and IS_ERR() in APIs where both might be possible.
- Or worse, they might assume one style applies universally and miss subtle bugs.
- Increases the risk of returning partially initialized data structures or invalid pointers.
- Hidden Bugs:
- An ERR_PTR() mistakenly treated as a valid pointer could cause a crash or undefined behavior.
- A NULL return that wasn’t anticipated could lead to null dereferencing.
- Makes testing more difficult since the range of possible return values is broader and less predictable.
- Harder Refactoring:
- If your API’s error conventions are inconsistent, wrapping or chaining functions becomes more complex.
- Wrappers have to inspect each function's quirks rather than handle a uniform contract.
Example
Some Linux subsystems use one, some use both, and some even mix them, leading to defensive and messy code like:
struct foo *f = get_foo();
if (!f) return -ENOMEM;
if (IS_ERR(f)) return PTR_ERR(f);
That kind of boilerplate is not just ugly—it’s an indication that something is wrong with the API contract.
The Takeaway
A clean, consistent API is easier to understand, safer to use, and less frustrating to debug. Whether the convention is:
- 0 for success, < 0 for errno-style errors
- NULL for error, pointer for success
- pointer for success, ERR_PTR() for error
…it doesn’t matter what the rule is as much as that there is a single rule. Mixed return styles are a fast path to subtle bugs and unhappy developers.
I think the point is - having a consistent user experience in the API makes things easier to use - having some functions returns nulls on error, and others having pointer-encoded errors - means that you require every end user to check the iio.h file on every function they user to make sure they are checking things properly...
That seems like not a friendly library to me...
It should already be somewhat consistent - the API functions that can return NULL are the accessors used to retrieve an object (iio_device, etc) from an index or identifier, in the case where the index or identifier specified does not correspond to any object, or to retrieve an object property (e.g. label) in the case where the property is NULL.
Every other API function that returns a pointer should use pointer-encoded errors, if I remember correctly.