Skip to content

Commit

Permalink
README.md revisited (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
vvish committed Nov 11, 2020
1 parent a468840 commit 0a64a28
Showing 1 changed file with 105 additions and 38 deletions.
143 changes: 105 additions & 38 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 way 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
## Getting started

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.
To use the library the one can clone the sources and add `include` folder into
project include paths.

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:

2 bits for domain, 2 bits for sub-domain. 4 bits for error code
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.

Then the project specific error code type layout can be defined.
The error type in the foloowing example uses 8-bit integral type as low-level
storage and has 2 bits for category, 2 bits for sub-category and 4 bits for error code.

```c++
namespace application
{
//project-wide definition of error types

//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);
// project-wide definitions of error types

// definition of error categories and their widths in bits
// (here 2 bits allowing maximum of 4 categories on each level)
MAKE_RESULT_CATEGORY(Category, 2);
MAKE_RESULT_CATEGORY(SubCategory, 2);
// 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);
MAKE_RESULT_TYPE(Result, uint8_t, Category, SubCategory);
// and aggregate result capable of containing 32/8 = 4 errors
MAKE_AGGREGATE_RESULT_TYPE(AggregateResult, uint32_t, Result);
// local alias for the positive result can be defined
constexpr auto Ok = Result::success;
// application error categories
constexpr auto Ui = Category{1};
constexpr auto Backend = Category{2};
} //namespace application
```
After the structure is defined categories specified:
```c++
namespace backend_access
{
// subcategories defined for DB, Rpc and more common errors
constexpr auto Db = SubCategory{1};
constexpr auto Rpc = SubCategory{2};
constexpr auto DataAccess = SubCategory{3};
// ....
} // namespace backend_access
```

The error definitions can then be defined globally, in the corresponding modules, or nested in the class declarations:
```c++
constexpr auto backendAccessError
= Result::make(Backend, DataAccess, 1);
// sample RPC client for backend access
class BackendClient {
public:
// localy defined error code
static constexpr Result rpcError = Result::make(Backend, Rpc, 1);
static constexpr Result wrongQuery = Result::make(Backend, Db, 1);
// ....
// method return single result
Result executeRemoteQuery() {
return rpcError; // RPC failed
}
};
```
//definition of the error domains and subdomains
The client code can then analyze, modify and forward errors:
```c++
// method returns aggregated result
AggregateResult retrieveRemoteData() {
backend_access::BackendClient client;
Result const result = client.executeRemoteQuery();
// handling error
if (!respp::is_success(result)) {
if (respp::get_category<SubCategory>(result) == Rpc) {
// in case of RPC error we return more common error and nest
// the error from lower layer
return AggregateResult{backendAccessError, result};
} else {
// ignore other types of error
// aggregated result can be constructed from single result
return Ok;
}
}
return result;
}
```

// here the error can occure in the application layer
// domain having value 3 is defined
constexpr auto Application = Domain{3};
The aggregated errors can then be iterated

// two subdomains defined
constexpr auto Server = SubDomain{1};
constexpr auto Client = SubDomain{2};
```c++
auto const result = retrieveRemoteData();
for (auto const it : result.iterate_errors()) {
if (it == application::backend_access::BackendClient::rpcError) {
// some logic or reporting
}
}
```
For more complete example please refer to `examples/example.cpp` and unit-tests.

//error codes for concrete errors
constexpr uint8_t rpcErrorCode = 1;
constexpr uint8_t backendAccessErrorCode = 2;
## Build

//local alias for the positive result
constexpr auto Ok = TestResult::success;
The library is header-only and requires C++17.

//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);
Makefile in the project root can be used to build and execute unit-tests.

} // namespace application
To build and execute tests `make test` target can be used (`make` without parameters to build only).

```
The executable containing unit-test can then be found in the `bin` subfolder.

0 comments on commit 0a64a28

Please sign in to comment.