diff --git a/README.md b/README.md index a617f20..3ad0e19 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ - [Etl](#etl) - [Integration](#integration) - [Usage](#usage) -- [API](#api) - [Maintainers](#maintainers) - [Contributing](#contributing) - [License](#license) @@ -39,8 +38,8 @@ its a single header file, at roughly 500 lines of code. A nice and easy read wit the Result attempts to be as close to the Rust langauges implementation as possible, and may be just what your looking for to ditch those try/catch error handling blocks. -- There is also a [template specialiazation for std::unique_ptr](https://github.com/thebashpotato/extra-template-library/blob/f1dcd42141c26f4826283d84ec39f87d364be621/etl/include/etl.hpp#L457) that can be used as a starting point for you own specialization if -the generic Result does not work for your type. This will most likely only be the case for move only types. +- One current catch with the Result type is if you have a **move only type** you will need to hi-jack the etl namespace + and create a template specialization for it, but don't worry it's easy. I have provided an example [here](https://github.com/thebashpotato/extra-template-library/blob/main/etl/examples/moveonly) which you can copy and paste, (Just replace the name of the the class with your own). 2. [etl::EnumerationIterator](https://github.com/thebashpotato/extra-template-library/blob/main/etl/tests/enum_iterable_test.cpp) @@ -157,16 +156,13 @@ pkg-config etl --clfags ## Usage [Please see the unit tests](extra-template-library/etl/tests) for bite size examples for each class. -[Please see](extra-template-library/etl/examples/blackjack.cpp) for an example blackjack program utilizing etl to solve real world problems. +[Please see](extra-template-library/etl/examples/blackjack) for an example blackjack program utilizing etl to solve real world problems. +[Please see](extra-template-library/etl/examples/moveonly) for an example for a Result move only class template specialization. ```cpp #include ``` -## API - -TODO - ## Maintainers [@thebashpotato](https://github.com/thebashpotato) diff --git a/dev-scripts/settings.py b/dev-scripts/settings.py index bebd9d8..5090afd 100644 --- a/dev-scripts/settings.py +++ b/dev-scripts/settings.py @@ -36,12 +36,12 @@ # Build in release mode, modify the flags accordingly. "CMAKE_RELEASE": { "name": "cmake", - "flags": "-D ETL_DEV_MODE=OFF -D CMAKE_CXX_COMPILER=clang++", + "flags": "-D ETL_DEV_MODE=OFF -D CMAKE_CXX_COMPILER=clang++-14", }, # Build in develop mode, modify the flags accordingly. "CMAKE_DEVELOP": { "name": "cmake", - "flags": "-D ETL_DEV_MODE=ON -D CMAKE_CXX_COMPILER=clang++", + "flags": "-D ETL_DEV_MODE=ON -D CMAKE_CXX_COMPILER=clang++-14", }, # customize formatting and clang tidy through the flags "CLANG_FORMATTER": {"name": "clang-format", "flags": "-i"}, diff --git a/etl/examples/CMakeLists.txt b/etl/examples/CMakeLists.txt index bbd6ff6..ec87a6b 100644 --- a/etl/examples/CMakeLists.txt +++ b/etl/examples/CMakeLists.txt @@ -3,12 +3,16 @@ # set(ScratchFile "${PROJECT_NAME}-scratch") set(Blackjack "${PROJECT_NAME}-blackjack") +set(MoveOnly "${PROJECT_NAME}-moveonly") add_executable(${ScratchFile} "${APP_EXAMPLES_SOURCE_DIR}/scratch.cpp" ${UTILS_SOURCE_FILES}) -add_executable(${Blackjack} "${APP_EXAMPLES_SOURCE_DIR}/blackjack.cpp" ${UTILS_SOURCE_FILES}) +add_executable(${Blackjack} "${APP_EXAMPLES_SOURCE_DIR}/blackjack/main.cpp" ${UTILS_SOURCE_FILES}) +add_executable(${MoveOnly} "${APP_EXAMPLES_SOURCE_DIR}/moveonly/main.cpp" ${UTILS_SOURCE_FILES}) target_include_directories(${ScratchFile} PUBLIC ${APP_INCLUDE_DIR}) target_include_directories(${Blackjack} PUBLIC ${APP_INCLUDE_DIR}) +target_include_directories(${MoveOnly} PUBLIC ${APP_INCLUDE_DIR}) target_link_libraries(${ScratchFile} PRIVATE etl_project_options etl_project_warnings) target_link_libraries(${Blackjack} PRIVATE etl_project_options etl_project_warnings) +target_link_libraries(${MoveOnly} PRIVATE etl_project_options etl_project_warnings) diff --git a/etl/examples/blackjack.cpp b/etl/examples/blackjack.cpp deleted file mode 100644 index b6a1dcb..0000000 --- a/etl/examples/blackjack.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/// The black jack code is from the youtube channel Dave's Garage. -/// Give him a like and subscribe, he makes great content. -/// -/// @link Link to the video: https://youtu.be/b8V-WIjlScA -/// @link Original code: https://github.com/davepl/blackjack -/// -/// In the video Dave does some manual iteration over a C style enum. -/// At the time stamp 8:30 he says "One unfortunate part about C++ -/// is that it doesn't provide a way to get the lowest and highest values in an enumeration". -/// Which he is correct. Since this was a problem I had already solved for myself I though his -/// viewers might like to see a solution to this problem. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace etl; - -namespace blackjack { - enum class Rank : std::uint16_t { - ACE = 1, - TWO, - THREE, - FOUR, - FIVE, - SIX, - SEVEN, - EIGHT, - NINE, - TEN, - JACK, - QUEEN, - KING - }; - - enum class Suit : std::uint16_t { - HEARTS, - DIAMONDS, - CLUBS, - SPADES, - }; - - class Card { - private: - Rank rank_; - Suit suit_; - - public: - Card(Rank rank, Suit suit) noexcept : rank_(rank), suit_(suit) {} - - [[nodiscard]] auto getRank() const noexcept -> Rank { return rank_; } - - [[nodiscard]] auto getSuite() const noexcept -> Suit { return suit_; } - }; - - - class Deck { - public: - using UniqueCard = std::unique_ptr; - - private: - /// @brief Here is where we use the etl generic enum iterator class - using RankIterator = - EnumerationIterator; - using SuitIterator = - EnumerationIterator; - - private: - std::vector cards_; - - public: - /// @brief Builds a 52 card deck of 13 ranks with 4 suits, - /// - /// @details Uses the custom EnumerationIterator template class to showcase - /// the C++ ability to iterate over enums. - Deck() { - for (const auto &suit: SuitIterator()) { - for (const auto &rank: RankIterator()) { - cards_.emplace_back(std::make_unique(rank, suit)); - } - } - } - - /// @brief How many cards are in the deck - [[nodiscard]] auto size() -> std::size_t { return cards_.size(); } - - /// @brief Uses a random number and The classic Mersenne Twister, - /// random number generator to shuffle the deck. - auto shuffleDeck() { - std::random_device random_number; - std::mt19937 generator(random_number()); - std::shuffle(cards_.begin(), cards_.end(), generator); - } - - /// @brief Draws a single card from the deck if it isn't emptpy. - /// - /// @returns Result - [[nodiscard]] auto drawCard() -> Result { - if (cards_.empty()) { - return Result(Error::create("Deck is empty", RUNTIME_INFO)); - } - auto card = std::move(cards_.back()); - cards_.pop_back(); - return Result(std::move(card)); - } - }; - - - class Player { - private: - std::vector hand_; - - public: - auto addCard(Deck::UniqueCard &&card) noexcept { - hand_.emplace_back(std::move(card)); - } - - [[nodiscard]] auto getHandValue() -> uint16_t { - constexpr auto best_hand_value = 21; - constexpr auto remove_value = 10; - constexpr auto hightest_ace_value = 11; - - uint16_t value = 0; - uint16_t aces = 0; - - for (const auto &card: this->hand_) { - auto cardValue = card->getRank(); - if (cardValue >= Rank::TEN) { - cardValue = Rank::TEN; - } else if (cardValue == Rank::ACE) { - aces++; - cardValue = static_cast(hightest_ace_value); - } - value += static_cast(cardValue); - } - - while (value > best_hand_value && aces > 0) { - value -= remove_value; - aces--; - } - return value; - } - }; - -}// namespace blackjack - - -auto draw_two_cards(blackjack::Deck &deck, blackjack::Player &entity) { - for (int i = 0; i < 2; ++i) { - if (auto result = deck.drawCard(); result.isOk()) { - entity.addCard(std::move(result.ok().value())); - } else { - std::cerr << result.err().value().info() << '\n'; - } - } -} - -auto main() -> int { - blackjack::Deck deck; - deck.shuffleDeck(); - - blackjack::Player player; - blackjack::Player dealer; - std::cout << "Deck has: " << deck.size() << " cards\n"; - - draw_two_cards(deck, player); - draw_two_cards(deck, dealer); - - std::cout << "Player hand value: " << player.getHandValue() << '\n'; - std::cout << "Dealer hand value: " << dealer.getHandValue() << '\n'; - std::cout << "Deck has: " << deck.size() << " cards\n"; - - return EXIT_SUCCESS; -} diff --git a/etl/examples/blackjack/blackjack.hpp b/etl/examples/blackjack/blackjack.hpp new file mode 100644 index 0000000..336fa00 --- /dev/null +++ b/etl/examples/blackjack/blackjack.hpp @@ -0,0 +1,109 @@ +/// The black jack code is from the youtube channel Dave's Garage. +/// Give him a like and subscribe, he makes great content. +/// +/// @link Link to the video: https://youtu.be/b8V-WIjlScA +/// @link Original code: https://github.com/davepl/blackjack +/// +/// In the video Dave does some manual iteration over a C style enum. +/// At the time stamp 8:30 he says "One unfortunate part about C++ +/// is that it doesn't provide a way to get the lowest and highest values in an enumeration". +/// Which he is correct. Since this was a problem I had already solved for myself I thought +/// I would share my solution with him and the world (if anyone ever sees this). + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace etl; + +namespace blackjack { + enum class Rank : std::uint16_t { + ACE = 1, + TWO, + THREE, + FOUR, + FIVE, + SIX, + SEVEN, + EIGHT, + NINE, + TEN, + JACK, + QUEEN, + KING + }; + + enum class Suit : std::uint16_t { + HEARTS, + DIAMONDS, + CLUBS, + SPADES, + }; + + class Card { + private: + Rank _rank; + Suit _suit; + + public: + Card(Rank rank, Suit suit) noexcept; + + [[nodiscard]] auto getRank() const noexcept -> Rank; + + [[nodiscard]] auto getSuite() const noexcept -> Suit; + }; + + + class Deck { + public: + using UniqueCard = std::unique_ptr; + + private: + /// @brief Here is where we use the etl generic enum iterator class + using RankIterator = + EnumerationIterator; + using SuitIterator = + EnumerationIterator; + + private: + std::vector _cards; + + public: + /// @brief Builds a 52 card deck of 13 ranks with 4 suits, + /// + /// @details Uses the custom EnumerationIterator template class to showcase + /// the C++ ability to iterate over enums. + Deck() noexcept; + + /// @brief How many cards are in the deck + [[nodiscard]] auto size() -> std::size_t; + + /// @brief Uses a random number and The classic Mersenne Twister, + /// random number generator to shuffle the deck. + auto shuffleDeck() -> void; + + /// @brief Draws a single card from the deck if it isn't emptpy. + /// + /// @returns Result + [[nodiscard]] auto drawCard() -> Result; + }; + + + class Player { + private: + std::vector _hand; + + public: + auto addCard(Deck::UniqueCard &&card) noexcept; + + [[nodiscard]] auto getHandValue() -> uint16_t; + }; + +}// namespace blackjack diff --git a/etl/examples/blackjack/main.cpp b/etl/examples/blackjack/main.cpp new file mode 100644 index 0000000..b855a42 --- /dev/null +++ b/etl/examples/blackjack/main.cpp @@ -0,0 +1,101 @@ +#include "blackjack.hpp" + +namespace blackjack { + + Card::Card(Rank rank, Suit suit) noexcept : _rank(rank), _suit(suit) {} + + auto Card::getRank() const noexcept -> Rank { return _rank; } + + auto Card::getSuite() const noexcept -> Suit { return _suit; } + + + Deck::Deck() noexcept { + for (auto const &suit: SuitIterator()) { + for (auto const &rank: RankIterator()) { + _cards.emplace_back(std::make_unique(rank, suit)); + } + } + } + + + auto Deck::size() -> std::size_t { return _cards.size(); } + + + auto Deck::shuffleDeck() -> void { + std::random_device random_number; + std::mt19937 generator(random_number()); + std::shuffle(_cards.begin(), _cards.end(), generator); + } + + + auto Deck::drawCard() -> Result { + if (_cards.empty()) { + return Result(Error::create("Deck is empty", RUNTIME_INFO)); + } + auto card = std::move(_cards.back()); + _cards.pop_back(); + return Result(std::move(card)); + } + + + auto Player::addCard(Deck::UniqueCard &&card) noexcept { + _hand.emplace_back(std::move(card)); + } + + + auto Player::getHandValue() -> uint16_t { + constexpr auto best_hand_value = 21; + constexpr auto remove_value = 10; + constexpr auto hightest_ace_value = 11; + + uint16_t value = 0; + uint16_t aces = 0; + + for (const auto &card: this->_hand) { + auto cardValue = card->getRank(); + if (cardValue >= Rank::TEN) { + cardValue = Rank::TEN; + } else if (cardValue == Rank::ACE) { + aces++; + cardValue = static_cast(hightest_ace_value); + } + value += static_cast(cardValue); + } + + while (value > best_hand_value && aces > 0) { + value -= remove_value; + aces--; + } + return value; + } + +}// namespace blackjack + + +auto draw_two_cards(blackjack::Deck &deck, blackjack::Player &entity) { + for (int i = 0; i < 2; ++i) { + if (auto result = deck.drawCard(); result.isOk()) { + entity.addCard(std::move(result.ok().value())); + } else { + std::cerr << result.err().value().info() << '\n'; + } + } +} + +auto main() -> int { + blackjack::Deck deck; + deck.shuffleDeck(); + + blackjack::Player player; + blackjack::Player dealer; + std::cout << "Deck has: " << deck.size() << " cards\n"; + + draw_two_cards(deck, player); + draw_two_cards(deck, dealer); + + std::cout << "Player hand value: " << player.getHandValue() << '\n'; + std::cout << "Dealer hand value: " << dealer.getHandValue() << '\n'; + std::cout << "Deck has: " << deck.size() << " cards\n"; + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/etl/examples/moveonly/main.cpp b/etl/examples/moveonly/main.cpp new file mode 100644 index 0000000..e6e83ff --- /dev/null +++ b/etl/examples/moveonly/main.cpp @@ -0,0 +1,28 @@ +#include "moveonly.hpp" +#include + +using namespace etl; +using namespace example; + +/// @brief Build a move only type from an integer, to show that we can return a move only type from a function +/// inside a Result template specialization. +auto buildMoveOnlyType(int val) -> Result { + if (val < 0) { + return Result(std::string("Value must be positive")); + } + return Result(MoveOnlyType(val)); +} + +auto main() -> int { + constexpr auto val = 42; + auto result = buildMoveOnlyType(val); + if (result.isOk()) { + auto moveOnlyType = std::move(result.ok().value()); + std::cout << "Result is ok" << std::endl; + std::cout << "Value is " << moveOnlyType.getValue() << std::endl; + } else { + std::cout << "Result is not ok" << std::endl; + std::cout << "Error is " << result.err().value() << std::endl; + } + return 0; +} \ No newline at end of file diff --git a/etl/examples/moveonly/moveonly.hpp b/etl/examples/moveonly/moveonly.hpp new file mode 100644 index 0000000..37936bd --- /dev/null +++ b/etl/examples/moveonly/moveonly.hpp @@ -0,0 +1,100 @@ +#pragma once + +#include + + +namespace example { + + /// @brief Move only object + /// + /// @details This showcases how to make a Result template specialization for a move only type. + class MoveOnlyType { + private: + // Private member variables + int _value{}; + + public: + // Ctor/Dtor + MoveOnlyType() noexcept = default; + explicit MoveOnlyType(int value) noexcept : _value(value) {} + virtual ~MoveOnlyType() = default; + + // Copy constructor and assignment operator delete + MoveOnlyType(MoveOnlyType const &other) = delete; + auto operator=(MoveOnlyType const &other) -> MoveOnlyType & = delete; + + // Move constructor and assignment operator + MoveOnlyType(MoveOnlyType &&other) = default; + auto operator=(MoveOnlyType &&other) -> MoveOnlyType & = default; + + public: + // Public member functions + [[nodiscard]] auto getValue() const noexcept -> int { + return _value; + } + }; +}// namespace example + + +/// @brief Hi-jack the etl namespace to add a custom template specialization for example::MoveOnlyType +namespace etl { + + template + class Result { + private: + std::variant _result; + bool _isOk{}; + + public: + Result() noexcept = default; + explicit Result(example::MoveOnlyType &&value) noexcept : _result(std::move(value)), _isOk(true) {} + explicit Result(const ErrType &error) noexcept : _result(error) {} + explicit Result(ErrType &&error) noexcept : _result(std::move(error)) {} + + public: + /// @brief Check if the union value is of the ok type + [[nodiscard]] inline auto isOk() const noexcept -> bool { + return _isOk; + } + + + /// @brief Check if the union value is of the error type + [[nodiscard]] inline auto isErr() const noexcept -> bool { + return !_isOk; + } + + + /// @brief Check if the union value is of the error type + /// + /// @details The use should always use isOk() before using ok() + /// + /// @return std::optinal for safety, incase the user did not call + /// isOk() before using this method. + [[nodiscard]] inline auto ok() noexcept -> std::optional { + std::optional opt{std::nullopt}; + if (_isOk) { + if (auto *value = std::get_if(&_result)) { + opt.emplace(std::move(*value)); + return opt; + } + } + return opt; + } + + + /// @brief Check if the union value is of the error type + /// + /// @details The use should always use isErr() before using err() + /// + /// @return std::optinal for safety, incase the user did not call + /// isErr() before using this method. + [[nodiscard]] inline auto err() const noexcept -> std::optional { + if (!_isOk) { + if (auto *err = std::get_if(&_result)) { + return *err; + } + } + return std::nullopt; + } + }; +}// namespace etl \ No newline at end of file diff --git a/etl/include/etl.hpp b/etl/include/etl.hpp index da4df38..37937db 100644 --- a/etl/include/etl.hpp +++ b/etl/include/etl.hpp @@ -1,6 +1,7 @@ /// MIT License /// -/// Copyright (c) 2023 Matt Williams +/// Copyright (c) 2023 Matt Williams (matt.k.williams@protonmail.com) +/// Original Source: https://github.com/thebashpotato/extra-template-library /// /// Permission is hereby granted, free of charge, to any person obtaining a copy /// of this software and associated documentation files (the "Software"), to deal @@ -47,21 +48,21 @@ namespace etl { /// /// @link https://en.cppreference.com/w/cpp/types/underlying_type using value_t = typename std::underlying_type::type; - std::int64_t value_; + std::int64_t _value; public: /// @brief Default constructor builds an instance to the first value /// in the enumeration. /// /// @details Used in the begin() method. - EnumerationIterator() noexcept : value_(static_cast(beginValue)) {} + EnumerationIterator() noexcept : _value(static_cast(beginValue)) {} /// @brief Constructs an instance to a specified value. /// /// @details Used in the end() method. - explicit EnumerationIterator(const EnumIterable &iter) noexcept - : value_(static_cast(iter)) {} + explicit EnumerationIterator(EnumIterable const &iter) noexcept + : _value(static_cast(iter)) {} /// @brief Default Destructor Move/Copy constructor and assignment @@ -78,7 +79,7 @@ namespace etl { /// an instance of itself. this++ not implemented, as it is /// ineffecient and usually not needed. [[maybe_unused]] auto operator++() noexcept -> EnumerationIterator { - ++this->value_; + ++this->_value; return *this; } @@ -88,14 +89,14 @@ namespace etl { /// @details Gets an instance to the current underlying value /// after casting it the type EnumIterable. [[nodiscard]] auto operator*() noexcept -> EnumIterable { - return static_cast(value_); + return static_cast(_value); } /// @brief Is equal overload [[nodiscard]] auto operator==(EnumerationIterator const &other_iterator) const noexcept -> bool { - return value_ == other_iterator.value_; + return _value == other_iterator._value; } @@ -132,7 +133,7 @@ namespace etl { template class TaggedFundamental { private: - FundamentalType value_{}; + FundamentalType _value{}; public: /// @brief All the constructors needed to build a fundamental wrapped type @@ -141,12 +142,12 @@ namespace etl { }; - explicit TaggedFundamental(FundamentalType const &value) : value_(value) { + explicit TaggedFundamental(FundamentalType const &value) : _value(value) { static_assert(std::is_fundamental::value); } - explicit TaggedFundamental(FundamentalType &&value) : value_(value) { + explicit TaggedFundamental(FundamentalType &&value) : _value(value) { static_assert(std::is_fundamental::value); } @@ -160,11 +161,11 @@ namespace etl { public: /// @brief Get the underlying type as a const ref - [[nodiscard]] inline auto get() const noexcept -> FundamentalType const & { return value_; } + [[nodiscard]] inline auto get() const noexcept -> FundamentalType const & { return _value; } /// @brief Get the underlying type as immutable - [[nodiscard]] inline auto getReadOnly() const noexcept -> FundamentalType { return value_; } + [[nodiscard]] inline auto getReadOnly() const noexcept -> FundamentalType { return _value; } }; @@ -174,9 +175,9 @@ namespace etl { /// macro invocation. class SourceCodeLocation { private: - std::string file_{}; - uint32_t line_{}; - std::string func_{}; + std::string _file{}; + uint32_t _line{}; + std::string _func{}; public: SourceCodeLocation() = delete; @@ -188,7 +189,7 @@ namespace etl { /// @param `func` name of the function where the error occurred SourceCodeLocation(std::string_view const &file, uint32_t line, std::string_view const &func) noexcept - : file_(file), line_(line), func_(func) {} + : _file(file), _line(line), _func(func) {} /// @brief Default Destructor, Move/Copy constructor and assignment @@ -201,18 +202,18 @@ namespace etl { public: /// @brief Get the file name in which the error occured [[nodiscard]] inline auto file() const noexcept -> std::string_view { - return file_; + return _file; } /// @brief Get the line number in which the error occured [[nodiscard]] inline auto line() const noexcept -> uint32_t { - return line_; + return _line; } /// @brief Get the name of the function in which the error occured [[nodiscard]] inline auto function() const noexcept -> std::string_view { - return func_; + return _func; } }; @@ -250,14 +251,14 @@ namespace etl { /// SourceCodeLocation RUNTIME_INFO macro. class Error : public IError { private: - std::string msg_{}; - std::string info_{}; + std::string _msg{}; + std::string _info{}; private: /// @brief Constructs the error with only a message /// /// @details This constructor is private to prevent the user from circumventing the create() method - explicit Error(std::string_view const &msg) noexcept : msg_(msg) {} + explicit Error(std::string_view const &msg) noexcept : _msg(msg) {} /// @brief Constructs the error with the message and source location @@ -267,8 +268,8 @@ namespace etl { /// /// @param `msg` the error message /// @param `slc` the source code location object - Error(std::string_view const &msg, SourceCodeLocation &&slc) noexcept : msg_(msg) { - info_.append("Error: ") + Error(std::string_view const &msg, SourceCodeLocation &&slc) noexcept : _msg(msg) { + _info.append("Error: ") .append(msg) .append("\nFunction: ") .append(slc.function()) @@ -304,19 +305,19 @@ namespace etl { public: /// @brief Get just the error message [[nodiscard]] inline auto msg() const noexcept -> std::string override { - return msg_; + return _msg; } /// @brief Override the current error message, useful when using the Result.mapErr method. [[nodiscard]] inline auto set(std::string_view &&msg) noexcept { - msg_ = msg; + _msg = msg; } /// @brief Override the current error message, useful when using the Result.mapErr method. [[nodiscard]] inline auto set(std::string_view const &msg) noexcept { - msg_ = msg; + _msg = msg; } @@ -325,10 +326,10 @@ namespace etl { /// @details If Error was not created with the RUNTIME_INFO macro info_ will be empty, /// in which case the msg_ will be returned instead. [[nodiscard]] inline auto info() const noexcept -> std::string override { - if (!info_.empty()) { - return info_; + if (!_info.empty()) { + return _info; } - return msg_; + return _msg; } }; @@ -345,15 +346,16 @@ namespace etl { template class Result { private: - std::variant result_; - bool isOk_; + std::variant _result; + bool _isOk{false}; public: /// @brief All the constructors needed to build an OkType or ErrType - explicit Result(OkType const &value) : result_(value), isOk_(true) {} - explicit Result(OkType &&value) : result_(std::move(value)), isOk_(true) {} - explicit Result(ErrType const &error) : result_(error), isOk_(false) {} - explicit Result(ErrType &&error) : result_(std::move(error)), isOk_(false) {} + Result() noexcept = default; + explicit Result(OkType const &value) noexcept : _result(value), _isOk(true) {} + explicit Result(OkType &&value) noexcept : _result(std::move(value)), _isOk(true) {} + explicit Result(ErrType const &error) noexcept : _result(error) {} + explicit Result(ErrType &&error) noexcept : _result(std::move(error)) {} /// @brief Default Destructor, Move/Copy constructor and assignment @@ -366,13 +368,13 @@ namespace etl { public: /// @brief Check if the variant is of the [OkType] [[nodiscard]] inline auto isOk() const noexcept -> bool { - return isOk_; + return _isOk; } /// @brief Check if the variant is of the [ErrType] [[nodiscard]] inline auto isErr() const noexcept -> bool { - return !isOk_; + return !_isOk; } @@ -383,8 +385,8 @@ namespace etl { /// @return std::optinal for safety, incase the user did not call /// isOk() before using this method. [[nodiscard]] inline auto ok() const noexcept -> std::optional { - if (isOk_) { - if (auto *value = std::get_if(&result_)) { + if (_isOk) { + if (auto *value = std::get_if(&_result)) { return *value; } } @@ -399,8 +401,8 @@ namespace etl { /// @return std::optinal for safety, incase the user did not call /// isErr() before using this method. [[nodiscard]] inline auto err() const noexcept -> std::optional { - if (!isOk_) { - if (auto *err = std::get_if(&result_)) { + if (!_isOk) { + if (auto *err = std::get_if(&_result)) { return *err; } } @@ -416,14 +418,14 @@ namespace etl { /// if isOK() is false. template [[nodiscard]] inline auto map(Function &&func) const noexcept -> Result { - if (isOk_) { + if (_isOk) { if constexpr (std::is_invocable_r_v) { - return Result(std::invoke(std::forward(func), *std::get_if(&result_))); + return Result(std::invoke(std::forward(func), *std::get_if(&_result))); } else { - return Result(*std::get_if(&result_)); + return Result(*std::get_if(&_result)); } } - return Result(std::move(*std::get_if(&result_))); + return Result(std::move(*std::get_if(&_result))); } @@ -435,14 +437,14 @@ namespace etl { /// if isErr() is false. template [[nodiscard]] inline auto mapErr(Function &&func) const noexcept -> Result { - if (!isOk_) { + if (!_isOk) { if constexpr (std::is_invocable_r_v) { - return Result(std::invoke(std::forward(func), *std::get_if(&result_))); + return Result(std::invoke(std::forward(func), *std::get_if(&_result))); } else { - return Result(*std::get_if(&result_)); + return Result(*std::get_if(&_result)); } } - return Result(std::move(*std::get_if(&result_))); + return Result(std::move(*std::get_if(&_result))); } }; @@ -456,25 +458,26 @@ namespace etl { template class Result, ErrType> { private: - std::variant, ErrType> result_; - bool isOk_; + std::variant, ErrType> _result; + bool _isOk{false}; public: /// @brief All the constructors needed to build an OkType or ErrType for a move only type - explicit Result(std::unique_ptr &&value) : result_(std::move(value)), isOk_(true) {} - explicit Result(const ErrType &error) : result_(error), isOk_(false) {} - explicit Result(ErrType &&error) : result_(std::move(error)), isOk_(false) {} + Result() noexcept = default; + explicit Result(std::unique_ptr &&value) noexcept : _result(std::move(value)), _isOk(true) {} + explicit Result(ErrType const &error) noexcept : _result(error) {} + explicit Result(ErrType &&error) noexcept : _result(std::move(error)) {} public: /// @brief Check if the variant value is of the [OkType] [[nodiscard]] inline auto isOk() const noexcept -> bool { - return isOk_; + return _isOk; } /// @brief Check if the variant value is of the [ErrType] [[nodiscard]] inline auto isErr() const noexcept -> bool { - return !isOk_; + return !_isOk; } @@ -485,8 +488,8 @@ namespace etl { /// @return std::optinal for safety, incase the user did not call /// isOk() before using this method. [[nodiscard]] inline auto ok() const noexcept -> std::optional> { - if (isOk_) { - if (auto *value = std::get_if>(&result_)) { + if (_isOk) { + if (auto *value = std::get_if>(&_result)) { return std::make_unique(**value); } } @@ -501,8 +504,8 @@ namespace etl { /// @return std::optinal for safety, incase the user did not call /// isErr() before using this method. [[nodiscard]] inline auto err() const noexcept -> std::optional { - if (!isOk_) { - if (auto *err = std::get_if(&result_)) { + if (!_isOk) { + if (auto *err = std::get_if(&_result)) { return *err; } } diff --git a/etl/tests/result_test.cpp b/etl/tests/result_test.cpp index 6b1d340..9ab859a 100644 --- a/etl/tests/result_test.cpp +++ b/etl/tests/result_test.cpp @@ -20,6 +20,18 @@ TEST(EtlResult, ResultOkTypeTest) { ASSERT_EQ(result.ok().value(), 2); } +TEST(EtlResult, ResultOkTypeTestDefaultConstructor) { + constexpr auto numerator = 10; + constexpr auto denominator = 5; + + Result result{}; + + result = divide(numerator, denominator); + ASSERT_TRUE(result.isOk()); + ASSERT_FALSE(result.isErr()); + ASSERT_EQ(result.ok().value(), 2); +} + TEST(EtlResult, ResultOkTypeMapTest) { constexpr auto numerator = 10; constexpr auto denominator = 5;