Skip to content

Commit

Permalink
Added basic usage example (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
vvish committed Nov 11, 2020
1 parent f288754 commit a468840
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 3 deletions.
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
TEST_LIBS = -lgmock -lgtest -lgtest_main -lpthread -L/usr/lib
TEST_CXX_FLAGS = -std=c++17 -Iinclude
TEST_TARGETS = result_test
TEST_DIR = test

EXAMPLE_TARGETS = example
EXAMPLE_DIR = examples

CXX_FLAGS = -std=c++17 -Iinclude
OUT_DIR = bin

$(TEST_TARGETS): % : $(TEST_DIR)/%.cpp
mkdir -p $(OUT_DIR)
g++ $(TEST_CXX_FLAGS) -o $(OUT_DIR)/$@ $< $(TEST_LIBS)
g++ $(CXX_FLAGS) -o $(OUT_DIR)/$@ $< $(TEST_LIBS)

$(EXAMPLE_TARGETS): % : $(EXAMPLE_DIR)/%.cpp
mkdir -p $(OUT_DIR)
g++ $(CXX_FLAGS) -o $(OUT_DIR)/$@ $<

test: $(TEST_TARGETS)
./$(OUT_DIR)/$<
Expand Down
115 changes: 115 additions & 0 deletions examples/example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Code example for the library usage. Demonstates the basic layout for a
// sample project having several sub-components. Apart from this purpose the
// code is meaningless.

#include "result.hpp"

namespace application
{
// 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(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};

// some sample submodules having various responsibilities
namespace backend_access
{
// sub-categories defined for DB, Rpc and more common errors
constexpr auto Db = SubCategory{1};
constexpr auto Rpc = SubCategory{2};
constexpr auto DataAccess = SubCategory{3};

// 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()
{
// RPC failed
return rpcError;
}
};

// shared error codes can be used
constexpr uint8_t backendAccessErrorCode = 2;
constexpr auto backendAccessError
= Result::make(Backend, DataAccess, backendAccessErrorCode);

// 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;
}

} // namespace backend_access

namespace ui
{
constexpr auto DataModel = SubCategory{1};

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

AggregateResult retrieveData()
{
auto result = backend_access::retrieveRemoteData();
if (respp::is_success(result)) {
// positive case
} else {
// append error from upper layer
return result << dataRetrievalError;
}
}

} // namespace ui
} // namespace application

int main()
{
auto const result = application::ui::retrieveData();
if (respp::is_success(result)) {
return 0;
} else {
// if the errors are in the public interfaces of the modules we can
// collect them here and output readable information or just codes
for (const auto it : result.iterate_errors()) {
if (it == application::backend_access::BackendClient::rpcError) {
// some logic or reporting
}
}
}
}
21 changes: 20 additions & 1 deletion include/result.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ struct category_t {

template <typename Token, uint8_t BitWidth>
constexpr bool operator==(
category_t<Token, BitWidth> const &lhs, category_t<Token, BitWidth> const &rhs)
category_t<Token, BitWidth> const &lhs,
category_t<Token, BitWidth> const &rhs)
{
return lhs.value == rhs.value;
}
Expand Down Expand Up @@ -238,6 +239,8 @@ struct aggregate_result_t {
capacity >= 2,
"The aggregate result should have space for at least two errors");

static constexpr aggregate_result_t success{};

underlaying_type container;

class error_iterator_t {
Expand Down Expand Up @@ -338,6 +341,12 @@ struct aggregate_result_t {
r.append(result);
return r;
}

friend constexpr bool operator==(
aggregate_result_t const &lhs, aggregate_result_t const &rhs)
{
return lhs.container == rhs.container;
}
};

template <typename CatToFind, typename Ut, typename... Cs>
Expand Down Expand Up @@ -369,6 +378,13 @@ constexpr bool is_success(result_t<Ut, Cs...> result)
return result_t<Ut, Cs...>::success == result;
}

template <typename Ut, typename Result, typename PlacementStrategy>
constexpr bool is_success(
aggregate_result_t<Ut, Result, PlacementStrategy> result)
{
return aggregate_result_t<Ut, Result, PlacementStrategy>::success == result;
}

} // namespace respp

#define MAKE_RESULT_CATEGORY(name, w) \
Expand All @@ -377,3 +393,6 @@ constexpr bool is_success(result_t<Ut, Cs...> result)

#define MAKE_RESULT_TYPE(name, ut, ...) \
using name = ::respp::result_t<ut, __VA_ARGS__>

#define MAKE_AGGREGATE_RESULT_TYPE(name, ut, single_result) \
using name = ::respp::aggregate_result_t<ut, single_result>;
10 changes: 10 additions & 0 deletions test/result_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ constexpr auto backendAccessError

using aggregate_result = respp::aggregate_result_t<uint32_t, TestResult>;

TEST(AggregateError_4x8bit, Intialized_with_default_value)
{
aggregate_result e;

EXPECT_TRUE(respp::is_success(e));

aggregate_result::error_iterator_t it(e), end;
ASSERT_EQ(it, end);
}

TEST(AggregateError_4x8bit, Intialized_with_one_error)
{
aggregate_result e(test_errors::drivers::ethLinkError);
Expand Down

0 comments on commit a468840

Please sign in to comment.