Error types
Jump to navigation
Jump to search
The Linux kernel often uses small positive integers as return values to indicate errors. For functions returning pointers, special macros are used for converting these integers into special pointer values (ERR_PTR()) and back again (IS_ERR()/PTR_ERR()).
However, this usage is incredibly error prone. There are several bugs of this type per month, e.g. [1][2][3][4][5][6][7] to give some recent examples.
It is fully possible to create a safe (and equally efficient) alternative in C++, similar to Rust's Result type, which does not allow you to dereference an error value.
Definitions:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <functional>
template<typename T>
class errptr {
unsigned long raw_value;
public:
// Constructor for errors
explicit errptr(int error_code):
raw_value(-error_code)
{
}
// Constructors for (real) pointers
explicit errptr(T *ptr):
raw_value((long) ptr)
{
}
// unwrap pointer/error
template<typename Success, typename Failure>
inline void check(Success success, Failure failure)
{
if ((long) raw_value > 0)
success((T *) raw_value);
else
failure(-(int) raw_value);
}
};
Usage:
struct kthread {
};
errptr<kthread> kthread_create(int x)
{
if (x)
return errptr<kthread>(new kthread());
return errptr<kthread>(EINVAL);
}
int main(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "usage: %s NUM\n", argv[0]);
exit(EXIT_FAILURE);
}
errptr<kthread> e = kthread_create(atoi(argv[1]));
e.check([](kthread *thread){
// success
printf("succeeded with pointer = %p\n", thread);
}, [](int error_code) {
// failure
printf("failed with error code %d\n", error_code);
});
return 0;
}
In practice, the lambdas are inlined into the caller and so do not produce worse code than the equivalent open-coded checks.