File: error_trace.cpp

package info (click to toggle)
boost1.83 1.83.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 545,632 kB
  • sloc: cpp: 3,857,086; xml: 125,552; ansic: 34,414; python: 25,887; asm: 5,276; sh: 4,799; ada: 1,681; makefile: 1,629; perl: 1,212; pascal: 1,139; sql: 810; yacc: 478; ruby: 102; lisp: 24; csh: 6
file content (149 lines) | stat: -rw-r--r-- 4,031 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc.

// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// This program demonstrates the use of leaf::on_error to capture the path an
// error takes as is bubbles up the call stack. The error path-capturing code
// only runs if:
// - An error occurs, and
// - A handler that takes e_error_trace argument is present. Otherwise none of
//   the error trace machinery will be invoked by LEAF.

// This example is similar to error_log, except the path the error takes is
// recorded in a std::deque, rather than just printed in-place.

#include <boost/leaf.hpp>
#include <iostream>
#include <deque>
#include <cstdlib>

#define ENABLE_ERROR_TRACE 1

namespace leaf = boost::leaf;

// The error trace is activated only if an error handling scope provides a
// handler for e_error_trace.
struct e_error_trace
{
    struct rec
    {
        char const * file;
        int line;
        friend std::ostream & operator<<( std::ostream & os, rec const & x )
        {
            return os << x.file << '(' << x.line << ')' << std::endl;
        }
    };

    std::deque<rec> value;

    friend std::ostream & operator<<( std::ostream & os, e_error_trace const & tr )
    {
        for( auto & i : tr.value )
            os << i;
        return os;
    }
};

// The ERROR_TRACE macro is designed for use in functions that detect or forward
// errors up the call stack. If an error occurs, and if an error handling scope
// provides a handler for e_error_trace, the supplied lambda is executed as the
// error bubbles up.
#define ERROR_TRACE auto _trace = leaf::on_error( []( e_error_trace & tr ) { tr.value.emplace_front(e_error_trace::rec{__FILE__, __LINE__}); } )

// Each function in the sequence below calls the previous function, and each
// function has failure_percent chance of failing. If a failure occurs, the
// ERROR_TRACE macro will cause the path the error takes to be captured in an
// e_error_trace.
int const failure_percent = 25;

leaf::result<void> f1()
{
    ERROR_TRACE;
    if( (std::rand()%100) > failure_percent )
        return { };
    else
        return leaf::new_error();
}

leaf::result<void> f2()
{
    ERROR_TRACE;
    if( (std::rand()%100) > failure_percent )
        return f1();
    else
        return leaf::new_error();
}

leaf::result<void> f3()
{
    ERROR_TRACE;
    if( (std::rand()%100) > failure_percent )
        return f2();
    else
        return leaf::new_error();
}

leaf::result<void> f4()
{
    ERROR_TRACE;
    if( (std::rand()%100) > failure_percent )
        return f3();
    else
        return leaf::new_error();
}

leaf::result<void> f5()
{
    ERROR_TRACE;
    if( (std::rand()%100) > failure_percent )
        return f4();
    else
        return leaf::new_error();
}

int main()
{
    for( int i=0; i!=10; ++i )
        leaf::try_handle_all(
            [&]() -> leaf::result<void>
            {
                std::cout << "Run # " << i << ": ";
                BOOST_LEAF_CHECK(f5());
                std::cout << "Success!" << std::endl;
                return { };
            },
#if ENABLE_ERROR_TRACE // This single #if enables or disables the capturing of the error trace.
            []( e_error_trace const & tr )
            {
                std::cerr << "Error! Trace:" << std::endl << tr;
            },
#endif
            []
            {
                std::cerr << "Error!" << std::endl;
            } );
    return 0;
}

////////////////////////////////////////

#ifdef BOOST_LEAF_NO_EXCEPTIONS

namespace boost
{
    [[noreturn]] void throw_exception( std::exception const & e )
    {
        std::cerr << "Terminating due to a C++ exception under BOOST_LEAF_NO_EXCEPTIONS: " << e.what();
        std::terminate();
    }

    struct source_location;
    [[noreturn]] void throw_exception( std::exception const & e, boost::source_location const & )
    {
        throw_exception(e);
    }
}

#endif