.. _module-pw_result: ========= pw_result ========= ``pw::Result`` is a class template for use in returning either a ``pw::Status`` error or an object of type ``T``. .. inclusive-language: disable ``pw::Result``'s implementation is closely based on Abseil's `StatusOr class `_. There are a few differences: .. inclusive-language: enable * ``pw::Result`` uses ``pw::Status``, which is much less sophisticated than ``absl::Status``. * ``pw::Result``'s functions are ``constexpr`` and ``pw::Result`` may be used in ``constexpr`` statements if ``T`` is trivially destructible. ----- Usage ----- Usage of ``pw::Result`` is identical to Abseil's ``absl::StatusOr``. See Abseil's `documentation `_ and `usage tips `_ for guidance. ``pw::Result`` is returned from a function that may return ``pw::OkStatus()`` and a value or an error status and no value. If ``ok()`` is true, the ``pw::Result`` contains a valid ``T``. Otherwise, it does not contain a ``T`` and attempting to access the value is an error. ``pw::Result`` can be used to directly access the contained type: .. code-block:: cpp #include "pw_result/result.h" if (pw::Result foo = TryCreateFoo(); foo.ok()) { foo->DoBar(); } ``pw::Result`` is compatible with ``PW_TRY`` and ``PW_TRY_ASSIGN``, for example: .. code-block:: cpp #include "pw_status/try.h" #include "pw_result/result.h" pw::Result GetAnswer(); // Example function. pw::Status UseAnswer() { const pw::Result answer = GetAnswer(); if (!answer.ok()) { return answer.status(); } if (answer.value() == 42) { WhatWasTheUltimateQuestion(); } return pw::OkStatus(); } pw::Status UseAnswerWithTry() { const pw::Result answer = GetAnswer(); PW_TRY(answer.status()); if (answer.value() == 42) { WhatWasTheUltimateQuestion(); } return pw::OkStatus(); } pw::Status UseAnswerWithTryAssign() { PW_TRY_ASSIGN(const int answer, GetAnswer()); if (answer == 42) { WhatWasTheUltimateQuestion(); } return pw::OkStatus(); } .. warning:: Be careful not to use larger types by value as this can quickly consume unnecessary stack. .. warning:: This module is experimental. Its impact on code size and stack usage has not yet been profiled. Use at your own risk. Monadic Operations ================== ``pw::Result`` also supports monadic operations, similar to the additions made to ``std::optional`` in C++23. These operations allow functions to be applied to a ``pw::Result`` that would perform additional computation. These operations do not incur any additional FLASH or RAM cost compared to a traditional if/else ladder, as can be seen in the `Size report`_. .. code-block:: cpp // Without monads pw::Result GetCuteCat(const Image& img) { pw::Result cropped = CropToCat(img); if (!cropped.ok()) { return cropped.status(); } pw::Result with_tie = AddBowTie(*cropped); if (!with_tie.ok()) { return with_tie.status(); } pw::Result with_sparkles = MakeEyesSparkle(*with_tie); if (!with_sparkles.ok()) { return with_parkes.status(); } return AddRainbow(MakeSmaller(*with_sparkles)); } // With monads pw::Result GetCuteCat(const Image& img) { return CropToCat(img) .and_then(AddBoeTie) .and_then(MakeEyesSparkle) .transform(MakeSmaller) .transform(AddRainbow); } ``pw::Result::and_then`` --------------------------- The ``pw::Result::and_then`` member function will return the result of the invocation of the provided function on the contained value if it exists. Otherwise, returns the contained status in a ``pw::Result``, which is the return type of provided function. .. code-block:: cpp // Expositional prototype of and_then: template class Result { template Result and_then(Function(T)> func); }; Result CreateFoo(); Result CreateBarFromFoo(const Foo& foo); Result bar = CreateFoo().and_then(CreateBarFromFoo); ``pw::Result::or_else`` -------------------------- The ``pw::Result::or_else`` member function will return ``*this`` if it contains a value. Otherwise, it will return the result of the provided function. The function must return a type convertible to a ``pw::Result`` or ``void``. This is particularly useful for handling errors. .. code-block:: cpp // Expositional prototype of or_else: template class Result { template requires std::is_convertible_v> Result or_else(Function func); Result or_else(Function func); }; // Without or_else: Result GetCuteCat(const Image& image) { Result cropped = CropToCat(image); if (!cropped.ok()) { PW_LOG_ERROR("Failed to crop cat: %d", cropped.status().code()); return cropped.status(); } return cropped; } // With or_else: Result GetCuteCat(const Image& image) { return CropToCat(image).or_else( [](Status s) { PW_LOG_ERROR("Failed to crop cat: %d", s.code()); }); } Another useful scenario for ``pw::Result::or_else`` is providing a default value that is expensive to compute. Typically, default values are provided by using ``pw::Result::value_or``, but that requires the default value to be constructed regardless of whether we actually need it. .. code-block:: cpp // With value_or: Image GetCuteCat(const Image& image) { // GenerateCuteCat() must execute regardless of the success of CropToCat return CropToCat(image).value_or(GenerateCuteCat()); } // With or_else: Image GetCuteCat(const Image& image) { // GenerateCuteCat() only executes if CropToCat fails. return *CropToCat(image).or_else([](Status) { return GenerateCuteCat(); }); } ``pw::Result::transform`` ---------------------------- The ``pw::Result::transform`` member method will return a ``pw::Result`` which contains the result of the invocation of the given function if ``*this`` contains a value. Otherwise, it returns a ``pw::Result`` with the same ``pw::Status`` value as ``*this``. The monadic methods for ``and_then`` and ``transform`` are fairly similar. The primary difference is that ``and_then`` requires the provided function to return a ``pw::Result``, whereas ``transform`` functions can return any type. Users should be aware that if they provide a function that returns a ``pw::Result`` to ``transform``, this will return a ``pw::Result>``. .. code-block:: cpp // Expositional prototype of transform: template class Result { template Result transform(Function func); }; Result ConvertStringToInteger(std::string_view); int MultiplyByTwo(int x); Result x = ConvertStringToInteger("42") .transform(MultiplyByTwo); ----------- Size report ----------- The table below showcases the difference in size between functions returning a Status with an output pointer, and functions returning a Result, in various situations. Note that these are simplified examples which do not necessarily reflect the usage of Result in real code. Make sure to always run your own size reports to check if Result is suitable for you. .. include:: result_size ------ Zephyr ------ To enable ``pw_result`` for Zephyr add ``CONFIG_PIGWEED_RESULT=y`` to the project's configuration.