Git Product home page Git Product logo

outcome's Introduction

master branchdevelop branch

CTest dashboard: https://my.cdash.org/index.php?project=Boost.Outcome

All tests passing source tarballs: https://github.com/ned14/outcome/releases

Documentation: https://ned14.github.io/outcome/

Purpose of this library

Outcome is a C++14 library for reporting and handling function failures. It can be used as a substitute for, or a complement to, the exception handling mechanism.

One use case is for contexts where using C++ exception handling is unsuitable for different reasons:

  • The high relative cost of throwing and catching a C++ exception.
  • Making some or all control paths explicitly detailed to aid code correctness auditing, as opposed to having hidden control paths caused by exceptions potentially thrown from any place.
  • Company policy to compile with exceptions disabled.
  • Maintaining a code base that was never designed with exception-safety in mind.
  • Parts of the programs/frameworks that themselves implement exception handling and cannot afford to use exceptions, like propagating failure reports across threads, tasks, fibers…

Usage as a single header file

Outcome v2 comes in single header file form. This is regenerated per commit. To fetch on Linux:

wget https://github.com/ned14/outcome/raw/master/single-header/outcome.hpp

On BSD:

fetch https://github.com/ned14/outcome/raw/master/single-header/outcome.hpp

If you have curl installed:

curl -O -J -L https://github.com/ned14/outcome/raw/master/single-header/outcome.hpp

Otherwise, simply download the raw file from above and place it wherever it suits you. If you might be debugging using Microsoft Visual Studio, you may find the debugger visualisation file at https://github.com/ned14/outcome/raw/master/include/outcome/outcome.natvis useful to include into your build.

Commits and tags in this git repository can be verified using:

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v2

mDMEVvMacRYJKwYBBAHaRw8BAQdAp+Qn6djfxWQYtAEvDmv4feVmGALEQH/pYpBC
llaXNQe0WE5pYWxsIERvdWdsYXMgKHMgW3VuZGVyc2NvcmVdIHNvdXJjZWZvcmdl
IHthdH0gbmVkcHJvZCBbZG90XSBjb20pIDxzcGFtdHJhcEBuZWRwcm9kLmNvbT6I
eQQTFggAIQUCVvMacQIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRCELDV4
Zvkgx4vwAP9gxeQUsp7ARMFGxfbR0xPf6fRbH+miMUg2e7rYNuHtLQD9EUoR32We
V8SjvX4r/deKniWctvCi5JccgfUwXkVzFAk=
=puFk
-----END PGP PUBLIC KEY BLOCK-----

outcome's People

Contributors

akrzemi1 avatar alandefreitas avatar amerry avatar andoks avatar burningenlightenment avatar catskul avatar cstratopoulos avatar ecatmur avatar gix avatar hazelnusse avatar hyeongyukim avatar jenkins-nedprod avatar johnthagen avatar libbooze avatar menuet avatar ned14 avatar norbertwenzel avatar sdarwin avatar vinipsmaker avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

outcome's Issues

Does lightweight_futures support recursive chaining?

Consider the following use case:

ned::promise<int> p;
auto t = p.get_future();
for (int i = 0; i != 65536; ++i)
    t = t.then([](ned::future<int>&& t) { return t.get() + 1; });
p.set_value(0);
std::cout << "ans: " << t.get();

Every new t depends on its previous result, and since the first t is stalled, a long chain of continuation is constructed. The continuation is invoked when the result is set, as the result, the continuation chain is invoked in a tail recursion manner, and the depth in this example is big enough to cause stackoverflow.

The problem (stackoverflow) could be solved though, and I don't consider this a bug in this library, it's a design decision. It's ok that this library doesn't support such a use case and just document the limitation clearly.

What's the intended decision here?

What happened if the future dies before the promise sets the result?

Here's an inconsistency between the std and your version.

#include <cstdlib>
#include <iostream>
#include <future>
#include <boost/outcome/future.hpp>

namespace lw = boost::outcome::lightweight_futures;

int main(int argc, char *argv[])
{
    try
    {
        lw::promise<int> p;
        //std::promise<int> p;
        p.get_future();
        p.set_value(1);
        std::cout << "done\n";
    }
    catch (std::exception& e)
    {
        std::cout << e.what() << "\n";
    }

    return EXIT_SUCCESS;
}

The std version prints done, but the lw version throws an exception no state.

Is the inconsistency by design or is it a bug?

Outcome fails to compaile on MinGW GCC 6.3

I am trying to compile Boost.Outcome on MinGW distro: a platform for running GCC (6.3.0) on Windows (7). Here are details of my compiler. I think it is "new enough".

Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=c:/_data/cpp/mingw/bin/../libexec/gcc/x86_64-w64-mingw32/6.3.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../src/configure --enable-languages=c,c++ --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --disable-multilib --prefix=/c/temp/gcc/dest --with-sysroot=/c/temp/gcc/dest --disable-libstdcxx-pch --disable-nls --disable-shared --disable-win32-registry --enable-checking=release --with-tune=haswell
Thread model: win32
gcc version 6.3.0 (GCC)

When I try to compile this program:

#include <boost/outcome.hpp>

int main() {}
g++ -std=c++14 -IC:\_data\cpp\boost.outcome-master\include  -Wall test.cpp -o test.exe
In file included from C:\_data\cpp\boost.outcome-master\include/boost/outcome/outcome.hpp:26:0,
                 from C:\_data\cpp\boost.outcome-master\include/boost/outcome.hpp:1,
                 from test.cpp:1:
C:\_data\cpp\boost.outcome-master\include/boost/outcome/outcome_v1.0.hpp: In function 'char** backtrace_symbols(void* const*, size_t)':
C:\_data\cpp\boost.outcome-master\include/boost/outcome/outcome_v1.0.hpp:1356:34: error: 'memset' was not declared in this scope
       memset(&ihl, 0, sizeof(ihl));
                                  ^
C:\_data\cpp\boost.outcome-master\include/boost/outcome/outcome_v1.0.hpp:1409:52: error: '_ui64toa_s' was not declared in this scope
           _ui64toa_s((size_t) bt[n], p, end - p, 16);
                                                    ^
C:\_data\cpp\boost.outcome-master\include/boost/outcome/outcome_v1.0.hpp:1410:26: error: 'strchr' was not declared in this scope
           p = strchr(p, 0);
                          ^
C:\_data\cpp\boost.outcome-master\include/boost/outcome/outcome_v1.0.hpp:1418:47: error: '_itoa_s' was not declared in this scope
         _itoa_s(ihl.LineNumber, p, end - p, 10);
                                               ^
C:\_data\cpp\boost.outcome-master\include/boost/outcome/outcome_v1.0.hpp:1419:24: error: 'strchr' was not declared in this scope
         p = strchr(p, 0) + 1;
                        ^
make: *** [Makefile:2: main] Error 1

When I compile this program (with manually added headers):

#include <string.h>
#include <stdlib.h>
#include <boost/outcome.hpp>

int main() {}

I get this compiler output:

g++ -std=c++14 -IC:\_data\cpp\boost.outcome-master\include  -Wall test.cpp -o test.exe
In file included from C:\_data\cpp\boost.outcome-master\include/boost/outcome/outcome.hpp:26:0,
                 from C:\_data\cpp\boost.outcome-master\include/boost/outcome.hpp:1,
                 from test.cpp:3:
C:\_data\cpp\boost.outcome-master\include/boost/outcome/outcome_v1.0.hpp: In function 'char** backtrace_symbols(void* const*, size_t)':
C:\_data\cpp\boost.outcome-master\include/boost/outcome/outcome_v1.0.hpp:1409:52: error: '_ui64toa_s' was not declared in this scope
           _ui64toa_s((size_t) bt[n], p, end - p, 16);
                                                    ^
C:\_data\cpp\boost.outcome-master\include/boost/outcome/outcome_v1.0.hpp:1418:47: error: '_itoa_s' was not declared in this scope
         _itoa_s(ihl.LineNumber, p, end - p, 10);
                                               ^
make: *** [Makefile:2: main] Error 1

Is it me doing something wrong, or is it a bug?

Look into cmake ExternalProject

Motivating use case:

include(ExternalProject)
ExternalProject_Add(
    outcome URL https://dedi4.nedprod.com/static/files/boost.outcome-v1.0-source-latest.tar.xz
    CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND ""
)

add_executable(main main.cpp)
set_property(TARGET main PROPERTY CXX_STANDARD 14)
target_include_directories(main PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/outcome-prefix/src/outcome/include")

Make a gentle intro broken up into bitesize piece per page

Notes from review item:

So, I would do something like we have in the documentation for
Boost.Optional.

The initial page is very short. Short description and a really small
example. In fact too small to sell the power of this library:

http://www.boost.org/doc/libs/1_64_0/libs/optional/doc/html/index.html

And then in a number of subsequent pages, we describe feature by feature
with short examples:

http://www.boost.org/doc/libs/1_64_0/libs/optional/doc/html/boost_optional/quick_start.html#boost_optional.quick_start.optional_return_values
http://www.boost.org/doc/libs/1_64_0/libs/optional/doc/html/boost_optional/quick_start/optional_automatic_variables.html
http://www.boost.org/doc/libs/1_64_0/libs/optional/doc/html/boost_optional/quick_start/optional_data_members.html
http://www.boost.org/doc/libs/1_64_0/libs/optional/doc/html/boost_optional/quick_start/bypassing_unnecessary_default_construction.html
http://www.boost.org/doc/libs/1_64_0/libs/optional/doc/html/boost_optional/quick_start/storage_in_containers.html

I would do the same for Outcome:

In the landing page just give this example:

https://github.com/akrzemi1/__sandbox__/blob/master/outcome_intro2.md

It only introduces outcome::expected (lot of users will not have heard of
std::expected) and the TRY operation to attract attention of more
informed users.

Then in the second page I would provide a number of short capters each with
one annotated example, each introducing one feature:

  • one for using payload wit error_code
  • one for all TRY operations
  • one for interaction with exception_ptr
  • one for catching exceptions into outcome
  • one for different ways of accessing the state of outcome

If you like this idea, I would volunteer to write up the initial pages.

Regards,
&rzej;

expected<T> incorrectly requires that T be default-constructible

The following fails to compile:

struct Date              // no default constructor
{                        // (no default date exists)
  explicit Date(int) {}
};

out::expected<Date> fun ()
{
  return Date{1};        // this fails to compile
}

It fires a static_assert:

"value_type must be default constructible (LEWG Expected requirement)"

This is incorrect, as per p0323r1 (see section X.Z.4.1 in the proposed wording) is_default_constructible is only required when someone triggers the default constructor of expected<T>. Otherwise expected would be not usable for any type without default constructor.

Bug in future<void>

This self-contained example failed to compile (MSVC14)

#include <boost/monad/future.hpp>

int main()
{
    boost::monad::lightweight_futures::future<void> f;
    f.get();
    return 0;
}

If you change void to int, it compiles w/o problem. Is future<void> not supported?

Incorrect continuation result for void return type

The following program crashes:

ned::promise<int> p;
auto t = p.get_future();
t.then([](ned::future<int> const&) {});
p.set_value(0);

The reason is: in future.hpp line 771, it checks v.is_ready(), and in monad.hpp line 866, the return value is output_type(), which is incorrectly "not ready", it should return output_type(value) instead.

value_storage needs to gain a trivially copyable storage specialisation

When fixing issue #12 last night, I undid a hack which had trivial copy/move constructors and assignment used when the type is trivially destructible. This allowed basic_monad to work in constexpr with types with trivial copy/move constructors and assignment, but it failed if the trivial types had non-trivial copy/move constructors which caused issue #12.

Right now value_storage uses one of four storage implementations:

  • trivial destructors of T, EC and E mean use value_storage_impl_trivial
  • otherwise use value_storage_impl_nontrivial
  • additionally there is a bool template parameter selecting packed storage or not.

Instead we probably need a hierarchy of storage implementations:

  • trivial destructibility means we make clear() do nothing, no destructors need be invoked during state changes
  • trivial copy constructor needs to be detected (it's much more likely than a trivial move constructor) and we must not use placement new in that situation, indeed the default copy/move constructor is probably fine
  • packed storage should be dropped I think as GCC 7 made constexpr bitfields illegal rather than implementing them like clang or MSVC.

Under this new schema you would get:

  • value_storage_impl_tc_td - trivially copyable, trivially destructible
  • value_storage_impl_ntc_td - non-trivially copyable, trivially destructible
  • value_storage_impl_tc_ntd - trivially copyable, non-trivially destructible (***)
  • value_storage_impl_ntc_ntd - non-trivially copyable, non-trivially destructible

Issues outstanding:

  • (***) is trivially copyable, non-trivially destructible actually possible for a variant?
  • should we really ignore move construction completely if copy construction is trivial?
  • should we not mark anything calling placement new with constexpr so the compiler trips correctly? Thing is, all of the major compilers do let you call placement new in constexpr sometimes even though the standard says they should not. There is no good technical reason to ban it. I can foresee a standards change eventually.
  • we didn't mention trivial assignment above at all. Some stupid types might have non-trivial copy constructors yet somehow a trivial copy assignment, or more likely vice versa. Handling this would double the count above though.

BOOST_OUTCOME_TRYV(expr) in a function whose return monad's type has no default constructor fails to compile

  using namespace BOOST_OUTCOME_V1_NAMESPACE;
  struct udt
  {
    // no default constructor
    udt(int) {}
    udt(const udt &) = default;
    udt(udt &&) = default;
  };
  auto f = []() -> result<udt>
  {
    auto g = [] { return result<int>(5); };
    /* This fails because BOOST_OUTCOME_TRYV() returns a result<void>
    which if it were valued void, would implicitly convert into a
    default constructed udt which is not possible, hence the compile error.
    */
    BOOST_OUTCOME_TRYV(g());
    return udt(5);
  };

Missing make_ready_future()

There's no make_ready_future for future<void>.

Besides, currently make_ready_future(t) is implemented as future<T>(t), however, I'd suggest dropping such ctor for future<T>, consider the case for future<void>, there's no corresponding ctor that makes it a ready future, future<void>()? no, the default constructed one is invalid. Such value-init ctor is confusing and inconsistent.

Make static_checked_outcome<T>/static_checked_result<T> narrow contract, never empty and make new static_checked_optional_outcome<T>/static_checked_optional_result<T> narrow contract with formal empty state

Both:

  • Have narrow "reinterpret_cast" observers
  • Misusage is statically checked via __builtin_unreachable()

For static_checked_optional_outcome/static_checked_optional_result::

  • Default constructs to empty
  • These implicitly convert from static_checked_outcome/static_checked_result but not the other way round

future<void>::get() should not return a value

Currently it returns no_value_type, not void.
Consider the generic use case, for example:

template<class T>
inline T await_resume(future<T>& fut)
{
    return fut.get();
}

Such code will fail to compile with future<void>.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.