Skip to content

Commit

Permalink
Revisited description in README.md (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
vvish committed Nov 13, 2020
1 parent f51c7cf commit 045541d
Showing 1 changed file with 107 additions and 40 deletions.
147 changes: 107 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,64 +1,131 @@
The built-in integral types are used as a low-level result storage that should provide efficient access and manipulation via bit operations.
The storage type can be specified to achieve portability and efficiency for various platforms.
# The minimalist error handling framework

## Motivation

The framework makes an attempt to provide resource efficient ways to define error types and facilities to analyze them.
In a resource constrained environment it would be convenient to have facilities to represent errors and ability to combine several of them
without overhead of memory allocation/deallocation.
The framework makes an attempt to provide resource efficient ways to define
error types and facilities to analyze them. In a resource-constrained
environment it would be convenient to have facilities to represent errors and
ability to combine several of them without overhead of memory
allocation/deallocation and access indirection.

The type is supposed to be project defined and concrete result values can be declared closer to the usage. That will enable clients to use unified methods to analyze and forward
errors up to the call stack while providing some encapsulation.
The built-in integral types are used as a low-level result storage that should
provide efficient access and manipulation via bit operations. The storage type
can be specified to achieve portability and efficiency for various platforms.

The result type is supposed to be project defined and concrete result values
can be declared closer to the usage. That will enable clients to use unified
methods to analyze and forward errors up to the call stack while providing some
encapsulation.

## Introduction
## Build

The framework allows clients to specify result code along with a set of error categories that can be used to describe the possible error in a more precise way. The underlying type can be specified
as a template parameter.
The library requires C++17.
Makefile in the project root can be used to build and execute unit-tests.

In the example the type to store the result code is defined using 8-bit integral type.
The error type is supposed to have the following structure:
To build and execute tests the following make target can be used:

2 bits for domain, 2 bits for sub-domain. 4 bits for error code
```sh
make test
```

And to build the example:

```sh
make example
```

The executables containing unit-tests and examples can then be found in the
`bin` subfolder.

## Getting started

As the library is header-only, to be used in the project it is sufficient to add
the `include` folder to the project include path.

The project-specific layout for result type should be defined.
It can have several layers of categories pointing at project submodules where
error can occur. In the result type declaration they are identified via
types that work as names for the layer.

In the example two levels of categories are defined each having 2 bits of size
(capable of representing 4 values each).
```c++
namespace application
{
//project-wide definition of error types
MAKE_RESULT_CATEGORY(Category, 2);
MAKE_RESULT_CATEGORY(SubCategory, 2);
```
//definition of some categories and their widths in bits
//(here 2 bits allowing maximum of 4 categories on each level)
MAKE_RESULT_CATEGORY(Domain, 2);
MAKE_RESULT_CATEGORY(SubDomain, 2);
The result type declaration should contain name, specification of the
underlying type and a list of categories to incorporate. Here the definition
uses 8-bit integral type as a low-level storage and has 2 bits for category, 2
bits for subcategory and 4 remaining bits for an error code.
The result type is defined with the name `Result`.
// definition of the result type (contains two categories
// defined above and based on 8 bit integer
// the rest 4 bits are used for result code)
MAKE_RESULT_TYPE(TestResult, uint8_t, Domain, SubDomain);
```c++
MAKE_RESULT_TYPE(Result, uint8_t, Category, SubCategory);
```
The aggregate result can be defined to combine several single results.
In the example it is capable of containing 32/8 bit = 4 result objects.

//definition of the error domains and subdomains
```c++
MAKE_AGGREGATE_RESULT_TYPE(AggregateResult, uint32_t, Result);
```
// here the error can occure in the application layer
// domain having value 3 is defined
constexpr auto Application = Domain{3};
Objects of different categories can be created to identify errors that can
occur in different program modules. They can be placed in corresponding
namespaces.
// two subdomains defined
constexpr auto Server = SubDomain{1};
constexpr auto Client = SubDomain{2};
```c++
constexpr auto Ui = Category{1};
constexpr auto DataModel = SubCategory{1};
//error codes for concrete errors
constexpr uint8_t rpcErrorCode = 1;
constexpr uint8_t backendAccessErrorCode = 2;
constexpr auto Backend = Category{2};
constexpr auto Db = SubCategory{1};
constexpr auto Rpc = SubCategory{2};
```

The error definitions can be placed globally, in the corresponding modules
or nested in the class declarations.

```c++
constexpr auto dataRetrievalError = Result::make(Ui, DataModel, 1);

constexpr auto backendAccessErrorCode = 1;
constexpr auto backendAccessError
= Result::make(Backend, DataAccess, backendAccessErrorCode);

class BackendClient {
public:
static constexpr Result rpcError = Result::make(Backend, Rpc, 1);
static constexpr Result wrongQuery = Result::make(Backend, Db, 1);
// ...
};
```
The client code can analyze returned result objects.
//local alias for the positive result
constexpr auto Ok = TestResult::success;
```c++
Result const result = ...;
if (respp::is_success(result)) {
} else if (respp::get_category<SubCategory>(result) == Rpc) {
} else if (respp::get_code(result) == backendAccessErrorCode) {}
```

Several errors can be combined (nested) in aggregate result:

//error definitions consisting of categories (Domain, SubDomain) and error codes
constexpr auto rpcClientError = TestResult::make(Application, Client, rpcErrorCode);
constexpr auto backendAccessError = TestResult::make(Application, Client, backendAccessErrorCode);
```c++
AggregateResult result {backendAccessError, wrongQuery};
return result << dataRetrievalError;
```
} // namespace application
The aggregated errors can be iterated to traverse 'error stack'.
```c++
AggregateResult const result = ...;
for (auto const it : result.iterate_errors()) {
if (it == application::backend_access::BackendClient::rpcError) {
}
}
```

For more complete examples please refer to `examples/example.cpp`
and unit-tests `test/result_test.cpp`.

0 comments on commit 045541d

Please sign in to comment.