From a46884009ad581007e9f85a191652b5421845c91 Mon Sep 17 00:00:00 2001 From: vvish Date: Tue, 10 Nov 2020 19:08:46 +0100 Subject: [PATCH] Added basic usage example (#5) --- Makefile | 12 ++++- examples/example.cpp | 115 +++++++++++++++++++++++++++++++++++++++++++ include/result.hpp | 21 +++++++- test/result_test.cpp | 10 ++++ 4 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 examples/example.cpp diff --git a/Makefile b/Makefile index 9084d2d..c96650e 100644 --- a/Makefile +++ b/Makefile @@ -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)/$< diff --git a/examples/example.cpp b/examples/example.cpp new file mode 100644 index 0000000..2cf963f --- /dev/null +++ b/examples/example.cpp @@ -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(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 + } + } + } +} diff --git a/include/result.hpp b/include/result.hpp index e6f5726..e23eacc 100644 --- a/include/result.hpp +++ b/include/result.hpp @@ -120,7 +120,8 @@ struct category_t { template constexpr bool operator==( - category_t const &lhs, category_t const &rhs) + category_t const &lhs, + category_t const &rhs) { return lhs.value == rhs.value; } @@ -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 { @@ -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 @@ -369,6 +378,13 @@ constexpr bool is_success(result_t result) return result_t::success == result; } +template +constexpr bool is_success( + aggregate_result_t result) +{ + return aggregate_result_t::success == result; +} + } // namespace respp #define MAKE_RESULT_CATEGORY(name, w) \ @@ -377,3 +393,6 @@ constexpr bool is_success(result_t result) #define MAKE_RESULT_TYPE(name, ut, ...) \ using name = ::respp::result_t + +#define MAKE_AGGREGATE_RESULT_TYPE(name, ut, single_result) \ + using name = ::respp::aggregate_result_t; diff --git a/test/result_test.cpp b/test/result_test.cpp index 2998444..d795505 100644 --- a/test/result_test.cpp +++ b/test/result_test.cpp @@ -87,6 +87,16 @@ constexpr auto backendAccessError using aggregate_result = respp::aggregate_result_t; +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);