Difference between revisions of "Error types"

From vegard.wiki
Jump to navigation Jump to search
(new page)
 
m (Vegard moved page C++ error types to Error types without leaving a redirect)
 
(No difference)

Latest revision as of 21:07, 18 December 2019

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.