SkillAgentSearch skills...

Meaculpa

Elegant and efficient error handling and reporting patterns for C

Install / Use

/learn @petervaro/Meaculpa
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

[![[license: GPLv3]][1]][2] [![[standard: ISO C11]][3]][4]


![meaculpa][5]

What is meaculpa?

meaculpa is a small, lightweight, simple, elegant, efficient, flexible, extensible, type-safe, thread-safe, fully backtraced error handling and reporting library and design pattern.

And it is capable of producing such beautiful outputs as the next one:

colored-output

Why is it called meaculpa?

Because meaculpa is all about errors!

Mea culpa is a Latin phrase that means "through my fault" and is an acknowledgement of having done wrong. [...]

The expression is used also as an admission of having made a mistake that should have been avoided, and may be accompanied by beating the breast as in its use in a religious context.

Wikipedia

The rationale behind it

Error handling in C is explicit, which means, one has to check directly the correctness of the returned value of a function. The returned value can be an error signal or a mixture of valid values and error signals.

Eventhough this kind of explicitness is a very good thing at such a low-level as C can work, it can make proper error-reporting a nightmare. How to tell exactly where the error occured? How to handle some of the errors and recover from them, while passing on the unrecoverable ones and at the same time printing only the unhandled error messages out? Not to mention, that it would also be useful if one could have a full backtraced message stack, when for example an error occured in a deeply nested function call-chain!

It may sounds easy enough to implement an error-handling/reporting library as that, but most of the existing solutions are reinventing the wheel, and they are adding bloated and both process- and memory-expensive runtimes on top of the C runtime. This kind of overhead is unacceptable, not to mention, that it is the horror itself to work with them in a multithreaded environment.

meaculpa tries to solve the above mentioned problems, with an elegant and very efficient way, which remains loyal to the philosophy of C — it is small, still explicit and provides maximum control and flexibility to its users.

The drawbacks

Well, of course there is no such thing as free lunch, one has to pay for such features. The real question is: how much should one pay?

With meaculpa the cost is very low. It returns mc_Error, which is an unsigned integer type, capable of storing 2<sup>64</sup> - 1 values. That is, if 1 byte is 8 bits, than it is 8 bytes in the memory. In most 64-bit systems it is the same size as a pointer. (Note: the size of a pointer is not explicitly defined in the standard, so this is just a rough approximation.) So it will hardly overflow on the stack — as returning a pointer won't do that either — but the downside still remains: it is very likely that it will be larger than a char, bool, int or enum typed values, which are very common error signals in C.

The other place where one has to pay, is the mute-flags. To get full control over the error riporting, one has to pass an mc_Error to a function which is capable of returning an mc_Error. The cost here is not only the size of the new argument, which is the same as in the previous paragraph, but it also means the developer has to pass this value explicitly every time such function is invoked. Of course this could be eliminated by defining variadic macros as function wrappers, which allows the user to pass a flag by default if it is not specified otherwise.

And the last costs are the extra if statements. One has to check wether an error message should be printed or not. Although, this overhead can be removed as well, as the main purpose of meaculpa is debugging, therefore a correct, production-ready, and already tested/debugged code can remove the extra checkings by placing #ifdefs at the right places.

All in all, the cost of meaculpa is very very small, especially compared to other solutions — it is only a fraction of those!

Dependencies

SUPPORTED PLATFORMS: meaculpa is currently supported only on UNIX-like systems (Linux, BSD, OS X, etc.), but Windows support (via mingw-w64) is on its way!

<!-- -->

SUPPORTED STANDARDS: meaculpa requires C99 or later (C11 recommended)

For end-users:

For developers:

Install utility

The next three sections will demonstrate the default installation of meaculpa, that is, using gcc, installing the headers at /usr/local/include and installing libraries at /usr/local/lib. For further information and available settings on how to use the included install.sh utility run:

bash install.sh --help

End-user build and install

$ git clone https://github.com/petervaro/meaculpa.git
$ cd meaculpa
$ bash install.sh

Developer build and install

$ git clone --recursive https://github.com/petervaro/meaculpa.git
$ cd meaculpa
$ bash tuplet/setup.sh
$ tup init
$ tup
$ bash install.sh

Uninstall

$ bash install.sh --remove

Tutorials

First include the necessary header files:

#include <meaculpa/meaculpa.h>

The basic API design pattern of a function should be something like:

<error> <function-name> ( <inputs...>, <outputs...>, <mute-flags> )

that is, its return value will always be an error (mc_Error with meaculpa), and all the outputs are going to be pointers. The last argument should be the mute-flags, which will inform the functioin which errors should be printed and which are not.

IMPORTANT: The mute-flags won't change the return value of a function, they will only effect the output of the function's mc_Error_put calls.

So for example, the implementation of a divider function which divides two ints, would look like this:

mc_Error
divider(int       dividend,
        int       divisor,
        int      *quotient,
        mc_Error  muted)
{
    /* If the 'quotient' argument is NULL */
    if (!quotient)
    {
        /* If 'mc_ARG_IS_NULL' is not muted */
        if (~muted & mc_ARG_IS_NULL)
            /* Print the error message */
            mc_Error_put(mc_ARG_IS_NULL, -1,
                         "3rd argument 'int *quotient' is NULL");
        /* Return the error signal */
        return mc_ARG_IS_NULL;
    }

    /* If the 'divisor' argument is NULL */
    if (!divisor)
    {
        /* If 'mc_ZERO_DIVISION' is not muted */
        if (~muted & mc_ZERO_DIVISION)
            mc_Error_put(mc_ZERO_DIVISION, -1, "divisor is 0");
        /* Return the error signal */
        return mc_ZERO_DIVISION;
    }

    /* Set the output */
    *quotient = dividend/divisor;

    /* Indicate that everything went fine */
    return mc_OKAY;
}

The following things are happening:

  1. The divider checks, if it can write to its output argument quotient, that is, the argument is not NULL.

  2. If the argument quotient is NULL, then first the function checks wether the error it is going to return muted or not. This is the tricky part, as meaculpa is using bit-masking technique for its error-handling/reporting. Therefore, first the function negates the argument muted, and then checks if mc_ARG_IS_NULL is in it. This expression will only be true, if mc_ARG_IS_NULL was not originally in the muted argument. Therefore if it was not in it, the function can print out the error message. After this the function will return the mc_ARG_IS_NULL wether the error was muted or not.

  3. The divider checks if the argument divisor is 0 or not.

  4. If the argument divisor is 0, the function will check wether it should print an error message for mc_ZERO_DIVISION or not. It does the same way as it did before in the 2. point. If the error is not muted it will print the error message, and then return the error signal itself.

  5. If both arguments quotient and divisor are valid, the function will divide the argument dividend with the argument divisor and will write the result to the memory where the argument quotient is pointing to.

  6. Finally it

View on GitHub
GitHub Stars10
CategoryDevelopment
Updated1y ago
Forks0

Languages

C

Security Score

75/100

Audited on Oct 20, 2024

No findings