1:last-update-label!:
2:icons: font
3:prewrap!:
4:docinfo: shared
5:stylesheet: zajo-dark.css
6:source-highlighter: rouge
7
8ifdef::backend-pdf[]
9= LEAF
10endif::[]
11ifndef::backend-pdf[]
12= LEAFpass:[<a href="https://ci.appveyor.com/project/zajo/leaf/branch/master"><img style="margin-left:8px; margin-top:21px; float:right; vertical-align: top" src="https://ci.appveyor.com/api/projects/status/u7mq10n8y5ewpre3/branch/master?svg=true"></a> <a href="https://travis-ci.com/boostorg/leaf"><img style="margin-top:21px; float:right; vertical-align: top" src="https://travis-ci.com/boostorg/leaf.svg?branch=master"></a><div style="z-index: 3; bottom:-16px; right:4px; position:fixed"><input width="32" height="32" type="image" alt="Skin" src="./skin.png" onclick="this.blur();switch_style();return false;"/></div>]
13endif::[]
14Lightweight Error Augmentation Framework | Emil Dotchevski
15ifndef::backend-pdf[]
16:toc: left
17:toclevels: 3
18:toc-title:
19
20[.text-right]
21https://github.com/boostorg/leaf[GitHub] | link:./leaf.pdf[PDF]
22endif::[]
23
24[abstract]
25== Abstract
26
27Boost LEAF is a lightweight error handling library for {CPP}11. Features:
28
29====
30* Small single-header format, no dependencies.
31
32* Designed for maximum efficiency ("happy" path and "sad" path).
33
34* No dynamic memory allocations, even with heavy payloads.
35
36* O(1) transport of arbitrary error types (independent of call stack depth).
37
38* Can be used with or without exception handling.
39
40* Support for multi-thread programming.
41====
42
43ifndef::backend-pdf[]
44[grid=none, frame=none]
45|====
46| <<introduction,Introduction>> \| <<tutorial>> \| <<synopsis>> \| https://github.com/boostorg/leaf/blob/master/doc/whitepaper.md[Whitepaper] \| https://github.com/boostorg/leaf/blob/master/benchmark/benchmark.md[Benchmark] >| Reference: <<functions,Functions>> \| <<types,Types>> \| <<predicates,Predicates>> \| <<traits,Traits>> \| <<macros,Macros>>
47|====
48endif::[]
49
50LEAF is designed with a strong bias towards the common use case where callers of functions which may fail check for success and forward errors up the call stack but do not handle them. In this case, only a trivial success-or-failure discriminant is transported. Actual error objects are communicated directly to the error-handling scope, skipping the intermediate check-only frames altogether.
51
52[[support]]
53== Support
54
55* https://Cpplang.slack.com[cpplang on Slack] (use the `#boost` channel)
56* https://lists.boost.org/mailman/listinfo.cgi/boost-users[Boost Users Mailing List]
57* https://lists.boost.org/mailman/listinfo.cgi/boost[Boost Developers Mailing List]
58* https://github.com/boostorg/leaf/issues[Report an issue] on GitHub
59
60[[distribution]]
61== Distribution and Portability
62
63Distributed under the http://www.boost.org/LICENSE_1_0.txt[Boost Software License, Version 1.0].
64
65LEAF requires only {CPP}11 (including support for thread-local storage).
66
67[[introduction]]
68== Five Minute Introduction
69
70We'll implement two versions of the same simple program: one using the LEAF `noexcept` API to handle errors, and another using the exception-handling API.
71
72[[introduction-result]]
73=== `noexcept` API
74
75We'll write a short but complete program that reads a text file in a buffer and prints it to `std::cout`, using LEAF to handle errors without exception handling.
76
77NOTE: LEAF provides an <<introduction-eh>> as well.
78
79We'll skip to the chase and start with the `main` function: it will try several operations as needed and handle all the errors that occur. Did I say *all* the errors? I did, so we'll use `leaf::try_handle_all`. It has the following signature:
80
81[source,c++]
82----
83template <class TryBlock, class... Handler>
84<<deduced>> try_handle_all( TryBlock && try_block, Handler && ... handler );
85----
86
87`TryBlock` is a function type, required to return a `result<T>` -- for example, `leaf::result<T>` -- that holds a value of type `T` or else indicates a failure.
88
89The first thing `try_handle_all` does is invoke the `try_block` function. If the returned object `r` indicates success, `try_handle_all` unwraps it, returning the contained `r.value()`; otherwise it calls the [underline]#first suitable# error handling function from the `handler...` list.
90
91We'll see later just what kind of a `TryBlock` will our `main` function pass to `try_handle_all`, but first, let's look at the juicy error-handling part. In case of an error, LEAF will consider each of the error handlers, [underline]#in order#, and call the first suitable match:
92
93[source,c++]
94----
95int main( int argc, char const * argv[] )
96{
97  return leaf::try_handle_all(
98
99    [&]() -> leaf::result<int>
100    {
101      // The TryBlock goes here, we'll see it later
102    },
103
104    // Error handlers below:
105
106    [](leaf::match<error_code, open_error>, leaf::match_value<leaf::e_errno, ENOENT>, leaf::e_file_name const & fn)
107    { <1>
108      std::cerr << "File not found: " << fn.value << std::endl;
109      return 1;
110    },
111
112    [](leaf::match<error_code, open_error>, leaf::e_errno const & errn, leaf::e_file_name const & fn)
113    { <2>
114      std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl;
115      return 2;
116    },
117
118    [](leaf::match<error_code, size_error, read_error, eof_error>, leaf::e_errno const * errn, leaf::e_file_name const & fn)
119    { <3>
120      std::cerr << "Failed to access " << fn.value;
121      if( errn )
122        std::cerr << ", errno=" << *errn;
123      std::cerr << std::endl;
124      return 3;
125    },
126
127    [](leaf::match<error_code, output_error>, leaf::e_errno const & errn)
128    { <4>
129      std::cerr << "Output error, errno=" << errn << std::endl;
130      return 4;
131    },
132
133    [](leaf::match<error_code, bad_command_line>)
134    { <5>
135      std::cout << "Bad command line argument" << std::endl;
136      return 5;
137    },
138
139    [](leaf::error_info const & unmatched)
140    { <6>
141      std::cerr <<
142        "Unknown failure detected" << std::endl <<
143        "Cryptic diagnostic information follows" << std::endl <<
144        unmatched;
145      return 6;
146    }
147  );
148}
149----
150
151<1> This handler will be called if the detected error includes: +
152pass:[•] an object of type `enum error_code` equal to the value `open_error`, and +
153pass:[•] an object of type `leaf::e_errno` that has `.value` equal to `ENOENT`, and +
154pass:[•] an object of type `leaf::e_file_name`. +
155In short, it handles "file not found" errors.
156
157<2> This handler will be called if the detected error includes: +
158pass:[•] an object of type `enum error_code` equal to `open_error`, and +
159pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), and +
160pass:[•] an object of type `leaf::e_file_name`. +
161In short, it will handle other "file open" errors.
162
163<3> This handler will be called if the detected error includes: +
164pass:[•] an object of type `enum error_code` equal to any of `size_error`, `read_error`, `eof_error`, and +
165pass:[•] an optional object of type `leaf::e_errno` (regardless of its `.value`), and +
166pass:[•] an object of type `leaf::e_file_name`.
167
168<4> This handler will be called if the detected error includes: +
169pass:[•] an object of type `enum error_code` equal to `output_error`, and +
170pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`),
171
172<5> This handler will be called if the detected error includes an object of type `enum error_code` equal to `bad_command_line`.
173
174<6> This last handler is a catch-all for any error, in case no other handler could be selected: it prints diagnostic information to help debug logic errors in the program, since it failed to find an appropriate error handler for the error condition it encountered.
175
176WARNING: It is critical to understand that the error handlers are considered in order, rather than by finding a "best match". No error handler is "better" than the others: LEAF will call the first one for which all of the arguments can be supplied using the available error objects.
177
178Now, reading and printing a file may not seem like a complex job, but let's split it into several functions, each communicating failures using `leaf::result<T>`:
179
180[source,c++]
181----
182leaf::result<char const *> parse_command_line( int argc, char const * argv[] ) noexcept; <1>
183
184leaf::result<std::shared_ptr<FILE>> file_open( char const * file_name ) noexcept; <2>
185
186leaf::result<int> file_size( FILE & f ) noexcept; <3>
187
188leaf::result<void> file_read( FILE & f, void * buf, int size ) noexcept; <4>
189----
190
191<1> Parse the command line, return the file name.
192<2> Open a file for reading.
193<3> Return the size of the file.
194<4> Read size bytes from f into buf.
195
196For example, let's look at `file_open`:
197
198[source,c++]
199----
200leaf::result<std::shared_ptr<FILE>> file_open( char const * file_name ) noexcept
201{
202  if( FILE * f = fopen(file_name,"rb") )
203    return std::shared_ptr<FILE>(f,&fclose);
204  else
205    return leaf::new_error(open_error, leaf::e_errno{errno});
206}
207----
208
209If `fopen` succeeds, we return a `shared_ptr` which will automatically call `fclose` as needed. If `fopen` fails, we report an error by calling `new_error`, which takes any number of error objects to communicate with the error. In this case we pass the system `errno` (LEAF defines `struct e_errno {int value;}`), and our own error code value, `open_error`.
210
211Here is our complete error code `enum`:
212
213[source,c++]
214----
215enum error_code
216{
217  bad_command_line = 1,
218  open_error,
219  read_error,
220  size_error,
221  eof_error,
222  output_error
223};
224----
225
226We're now ready to look at the `TryBlock` we'll pass to `try_handle_all`. It does all the work, bails out if it encounters an error:
227
228[source,c++]
229----
230int main( int argc, char const * argv[] )
231{
232  return leaf::try_handle_all(
233
234    [&]() -> leaf::result<int>
235    {
236      leaf::result<char const *> file_name = parse_command_line(argc,argv);
237      if( !file_name )
238        return file_name.error();
239----
240
241Wait, what's this, if "error" return "error"? There is a better way: we'll use `BOOST_LEAF_AUTO`. It takes a `result<T>` and bails out in case of a failure (control leaves the calling function), otherwise uses the passed variable to access the `T` value stored in the `result` object.
242
243This is what our `TryBlock` really looks like:
244
245[source,c++]
246----
247int main( int argc, char const * argv[] )
248{
249  return leaf::try_handle_all(
250
251    [&]() -> leaf::result<int> <1>
252    {
253      BOOST_LEAF_AUTO(file_name, parse_command_line(argc,argv)); <2>
254
255      auto load = leaf::on_error( leaf::e_file_name{file_name} ); <3>
256
257      BOOST_LEAF_AUTO(f, file_open(file_name)); <4>
258
259      BOOST_LEAF_AUTO(s, file_size(*f)); <4>
260
261      std::string buffer( 1 + s, '\0' );
262      BOOST_LEAF_CHECK(file_read(*f, &buffer[0], buffer.size()-1)); <4>
263
264      std::cout << buffer;
265      std::cout.flush();
266      if( std::cout.fail() )
267        return leaf::new_error(output_error, leaf::e_errno{errno});
268
269      return 0;
270    },
271
272    .... <5>
273
274  ); <6>
275}
276----
277
278<1> Our `TryBlock` returns a `result<int>`. In case of success, it will hold `0`, which will be returned from `main` to the OS.
279<2> If `parse_command_line` returns an error, we forward that error to `try_handle_all` (which invoked us) verbatim. Otherwise, `BOOST_LEAF_AUTO` gets us a variable, `file_name`, to access the parsed string.
280<3> From now on, all errors escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name {std::string value;}`). This is as if every time one of the following functions wants to report an error, `on_error` says "wait, associate this `e_file_name` object with the error, it's important!"
281<4> Call more functions, forward each failure to the caller.
282<5> List of error handlers goes here (we saw this earlier).
283<6> This concludes the `try_handle_all` arguments -- as well as our program!
284
285Nice and simple! Writing the `TryBlock`, we focus on the "happy" path -- if we encounter any error we just return it to `try_handle_all` for processing. Well, that's if we're being good and using RAII for automatic clean-up -- which we are, `shared_ptr` will automatically close the file for us.
286
287TIP: The complete program from this tutorial is available https://github.com/boostorg/leaf/blob/master/examples/print_file/print_file_result.cpp?ts=4[here]. The https://github.com/boostorg/leaf/blob/master/examples/print_file/print_file_eh.cpp?ts=4[other] version of the same program uses exception handling to report errors (see <<introduction-eh,below>>).
288
289'''
290
291[[introduction-eh]]
292=== Exception-Handling API
293
294And now, we'll write the same program that reads a text file in a buffer and prints it to `std::cout`, this time using exceptions to report errors. First, we need to define our exception class hierarchy:
295
296[source,c++]
297----
298struct bad_command_line: std::exception { };
299struct input_error: std::exception { };
300struct open_error: input_error { };
301struct read_error: input_error { };
302struct size_error: input_error { };
303struct eof_error: input_error { };
304struct output_error: std::exception { };
305----
306
307We'll split the job into several functions, communicating failures by throwing exceptions:
308
309[source,c++]
310----
311char const * parse_command_line( int argc, char const * argv[] ); <1>
312
313std::shared_ptr<FILE> file_open( char const * file_name ); <2>
314
315int file_size( FILE & f ); <3>
316
317void file_read( FILE & f, void * buf, int size ); <4>
318----
319<1> Parse the command line, return the file name.
320<2> Open a file for reading.
321<3> Return the size of the file.
322<4> Read size bytes from f into buf.
323
324The `main` function brings everything together and handles all the exceptions that are thrown, but instead of using `try` and `catch`, it will use the function template `leaf::try_catch`, which has the following signature:
325
326[source,c++]
327----
328template <class TryBlock, class... Handler>
329<<deduced>> try_catch( TryBlock && try_block, Handler && ... handler );
330----
331
332`TryBlock` is a function type that takes no arguments; `try_catch` simply returns the value returned by the `try_block`, catching [underline]#any# exception it throws, in which case it calls the [underline]#first# suitable error handling function from the `handler...` list.
333
334Let's first look at the `TryBlock` our `main` function passes to `try_catch`:
335
336[source,c++]
337----
338int main( int argc, char const * argv[] )
339{
340  return leaf::try_catch(
341
342    [&] <1>
343    {
344      char const * file_name = parse_command_line(argc,argv); <2>
345
346      auto load = leaf::on_error( leaf::e_file_name{file_name} ); <3>
347
348      std::shared_ptr<FILE> f = file_open( file_name ); <2>
349
350      std::string buffer( 1+file_size(*f), '\0' ); <2>
351      file_read(*f, &buffer[0], buffer.size()-1); <2>
352
353      std::cout << buffer;
354      std::cout.flush();
355      if( std::cout.fail() )
356        throw leaf::exception(output_error{}, leaf::e_errno{errno});
357
358      return 0;
359    },
360
361    .... <4>
362
363  ); <5>
364}
365----
366
367<1> Except if it throws, our `TryBlock` returns `0`, which will be returned from `main` to the OS.
368<2> If any of the functions we call throws, `try_catch` will find an appropriate handler to invoke (below).
369<3> From now on, all exceptions escaping this scope will automatically communicate the (now successfully parsed from the command line) file name (LEAF defines `struct e_file_name {std::string value;}`). This is as if every time one of the following functions wants to throw an exception, `on_error` says "wait, associate this `e_file_name` object with the exception, it's important!"
370<4> List of error handlers goes here. We'll see that later.
371<5> This concludes the `try_catch` arguments -- as well as our program!
372
373As it is always the case when using exception handling, as long as our `TryBlock` is exception-safe, we can focus on the "happy" path. Of course, our `TryBlock` is exception-safe, since `shared_ptr` will automatically close the file for us in case an exception is thrown.
374
375Now let's look at the second part of the call to `try_catch`, which lists the error handlers:
376
377[source,c++]
378----
379int main( int argc, char const * argv[] )
380{
381  return leaf::try_catch(
382    [&]
383    {
384      // The TryBlock goes here (we saw that earlier)
385    },
386
387    // Error handlers below:
388
389    [](open_error &, leaf::match_value<leaf::e_errno,ENOENT>, leaf::e_file_name const & fn)
390    { <1>
391      std::cerr << "File not found: " << fn.value << std::endl;
392      return 1;
393    },
394
395    [](open_error &, leaf::e_file_name const & fn, leaf::e_errno const & errn)
396    { <2>
397      std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl;
398      return 2;
399    },
400
401    [](input_error &, leaf::e_errno const * errn, leaf::e_file_name const & fn)
402    { <3>
403      std::cerr << "Failed to access " << fn.value;
404      if( errn )
405        std::cerr << ", errno=" << *errn;
406      std::cerr << std::endl;
407      return 3;
408    },
409
410    [](output_error &, leaf::e_errno const & errn)
411    { <4>
412      std::cerr << "Output error, errno=" << errn << std::endl;
413      return 4;
414    },
415
416    [](bad_command_line &)
417    { <5>
418      std::cout << "Bad command line argument" << std::endl;
419      return 5;
420    },
421
422    [](leaf::error_info const & unmatched)
423    { <6>
424      std::cerr <<
425        "Unknown failure detected" << std::endl <<
426        "Cryptic diagnostic information follows" << std::endl <<
427        unmatched;
428      return 6;
429    } );
430}
431----
432
433<1> This handler will be called if: +
434pass:[•] an `open_error` exception was caught, with +
435pass:[•] an object of type `leaf::e_errno` that has `.value` equal to `ENOENT`, and +
436pass:[•] an object of type `leaf::e_file_name`. +
437In short, it handles "file not found" errors.
438
439<2> This handler will be called if: +
440pass:[•] an `open_error` exception was caught, with +
441pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`), and +
442pass:[•] an object of type `leaf::e_file_name`. +
443In short, it handles other "file open" errors.
444
445<3> This handler will be called if: +
446pass:[•] an `input_error` exception was caught (which is a base type), with +
447pass:[•] an optional object of type `leaf::e_errno` (regardless of its `.value`), and +
448pass:[•] an object of type `leaf::e_file_name`.
449
450<4> This handler will be called if: +
451pass:[•] an `output_error` exception was caught, with +
452pass:[•] an object of type `leaf::e_errno` (regardless of its `.value`),
453
454<5> This handler will be called if a `bad_command_line` exception was caught.
455
456<6> If `try_catch` fails to find an appropriate handler, it will re-throw the exception. But this is the `main` function which should handle all exceptions, so this last handler matches any error and prints diagnostic information, to help debug logic errors.
457
458WARNING: It is critical to understand that the error handlers are considered in order, rather than by finding a "best match". No error handler is "better" than the others: LEAF will call the first one for which all of the arguments can be supplied using the available error objects.
459
460To conclude this introduction, let's look at one of the error-reporting functions that our `TryBlock` calls, for example `file_open`:
461
462[source,c++]
463----
464std::shared_ptr<FILE> file_open( char const * file_name )
465{
466  if( FILE * f = fopen(file_name,"rb") )
467    return std::shared_ptr<FILE>(f,&fclose);
468  else
469    throw leaf::exception(open_error{}, leaf::e_errno{errno});
470}
471----
472
473If `fopen` succeeds, it returns a `shared_ptr` which will automatically call `fclose` as needed. If `fopen` fails, we throw the exception object returned by `leaf::exception`, which in this case is of type that derives from `open_error`; the passed `e_errno` object will be associated with the exception.
474
475NOTE: `try_catch` works with any exception, not only exceptions thrown using `leaf::exception`.
476
477TIP: The complete program from this tutorial is available https://github.com/boostorg/leaf/blob/master/examples/print_file/print_file_eh.cpp?ts=4[here]. The https://github.com/boostorg/leaf/blob/master/examples/print_file/print_file_result.cpp?ts=4[other] version of the same program does not use exception handling to report errors (see the <<introduction-result,previous introduction>>).
478
479[[tutorial]]
480== Tutorial
481
482This section assumes the reader has basic understanding of using LEAF to handle errors; see <<introduction>>.
483
484[[tutorial-model]]
485=== Error Communication Model
486
487==== `noexcept` API
488
489The following figure illustrates how error objects are transported when using LEAF without exception handling:
490
491.LEAF noexcept Error Communication Model
492image::LEAF-1.png[]
493
494The arrows pointing down indicate the call stack order for the functions `f1` through `f5`: higher level functions calling lower level functions.
495
496Note the call to `on_error` in `f3`: it caches the passed error objects of types `E1` and `E3` in the returned object `load`, where they stay ready to be communicated in case any function downstream from `f3` reports an error. Presumably these objects are relevant to any such failure, but are conveniently accessible only in this scope.
497
498_Figure 1_ depicts the condition where `f5` has detected an error. It calls `leaf::new_error` to create a new, unique `error_id`. The passed error object of type `E2` is immediately loaded in the first active `context` object that provides static storage for it, found in any calling scope (in this case `f1`), and is associated with the newly-generated `error_id` (solid arrow);
499
500The `error_id` itself is returned to the immediate caller `f4`, usually stored in a `result<T>` object `r`. That object takes the path shown by dashed arrows, as each error-neutral function, unable to handle the failure, forwards it to its immediate caller in the returned value -- until an error-handling scope is reached.
501
502When the destructor of the `load` object in `f3` executes, it detects that `new_error` was invoked after its initialization, loads the cached objects of types `E1` and `E3` in the first active `context` object that provides static storage for them, found in any calling scope (in this case `f1`), and associates them with the last generated `error_id` (solid arrow).
503
504When the error-handling scope `f1` is reached, it probes `ctx` for any error objects associated with the `error_id` it received from `f2`, and processes a list of user-provided error handlers, in order, until it finds a handler with arguments that can be supplied using the available (in `ctx`) error objects. That handler is called to deal with the failure.
505
506==== Exception-Handling API
507
508The following figure illustrates the slightly different error communication model used when errors are reported by throwing exceptions:
509
510.LEAF Error Communication Model Using Exception Handling
511image::LEAF-2.png[]
512
513The main difference is that the call to `new_error` is implicit in the call to the function template `leaf::exception`, which in this case takes an exception object of type `Ex`, and returns an exception object of unspecified type that derives publicly from `Ex`.
514
515[[tutorial-interoperability]]
516==== Interoperability
517
518Ideally, when an error is detected, a program using LEAF would always call <<new_error>>, ensuring that each encountered failure is definitely assigned a unique <<error_id>>, which then is reliably delivered, by an exception or by a `result<T>` object, to the appropriate error-handling scope.
519
520Alas, this is not always possible.
521
522For example, the error may need to be communicated through uncooperative 3rd-party interfaces. To facilitate this transmission, a error ID may be encoded in a `std::error_code`. As long as a 3rd-party interface understands `std::error_code`, it should be compatible with LEAF.
523
524Further, it is sometimes necessary to communicate errors through an interface that does not even use `std::error_code`. An example of this is when an external lower-level library throws an exception, which is unlikely to be able to carry an `error_id`.
525
526To support this tricky use case, LEAF provides the function <<current_error>>, which returns the error ID returned by the most recent call (from this thread) to <<new_error>>. One possible approach to solving the problem is to use the following logic (implemented by the <<error_monitor>> type):
527
528. Before calling the uncooperative API, call <<current_error>> and cache the returned value.
529. Call the API, then call `current_error` again:
530.. If this returns the same value as before, pass the error objects to `new_error` to associate them with a new `error_id`;
531.. else, associate the error objects with the `error_id` value returned by the second call to `current_error`.
532
533Note that if the above logic is nested (e.g. one function calling another), `new_error` will be called only by the inner-most function, because that call guarantees that all calling functions will hit the `else` branch.
534
535TIP: To avoid ambiguities, whenever possible, use the <<exception>> function template when throwing exceptions to ensure that the exception object transports a unique `error_id`; better yet, use the <<BOOST_LEAF_THROW_EXCEPTION>> macro, which in addition will capture `pass:[__FILE__]` and `pass:[__LINE__]`.
536
537'''
538
539[[tutorial-loading]]
540=== Loading of Error Objects
541
542To load an error object is to move it into an active <<context>>, usually local to a <<try_handle_some>>, a <<try_handle_all>> or a <<try_catch>> scope in the calling thread, where it becomes uniquely associated with a specific <<error_id>> -- or discarded if storage is not available.
543
544Various LEAF functions take a list of error objects to load. As an example, if a function `copy_file` that takes the name of the input file and the name of the output file as its arguments detects a failure, it could communicate an error code `ec`, plus the two relevant file names using <<new_error>>:
545
546[source,c++]
547----
548return leaf::new_error(ec, e_input_name{n1}, e_output_name{n2});
549----
550
551Alternatively, error objects may be loaded using a `result<T>` that is already communicating an error. This way they become associated with that error, rather than with a new error:
552
553[source,c++]
554----
555leaf::result<int> f() noexcept;
556
557leaf::result<void> g( char const * fn ) noexcept
558{
559  if( leaf::result<int> r = f() )
560  { <1>
561    ....;
562    return { };
563  }
564  else
565  {
566    return r.load( e_file_name{fn} ); <2>
567  }
568}
569----
570[.text-right]
571<<result>> | <<result::load>>
572
573<1> Success! Use `r.value()`.
574<2> `f()` has failed; here we associate an additional `e_file_name` with the error. However, this association occurs iff in the call stack leading to `g` there are error handlers that take an `e_file_name` argument. Otherwise, the object passed to `load` is discarded. In other words, the passed objects are loaded iff the program actually uses them to handle errors.
575
576Besides error objects, `load` can take function arguments:
577
578* If we pass a function that takes no arguments, it is invoked, and the returned error object is loaded.
579+
580Consider that if we pass to `load` an error object that is not needed by any error handler, it will be discarded. If the object is expensive to compute, it would be better if the computation can be skipped as well. Passing a function with no arguments to `load` is an excellent way to achieve this behavior:
581+
582[source,c++]
583----
584struct info { .... };
585
586info compute_info() noexcept;
587
588leaf::result<void> operation( char const * file_name ) noexcept
589{
590  if( leaf::result<int> r = try_something() )
591  { <1>
592    ....
593    return { };
594  }
595  else
596  {
597    return r.load( <2>
598      [&]
599      {
600        return compute_info();
601      } );
602  }
603}
604----
605[.text-right]
606<<result>> | <<result::load>>
607+
608<1> Success! Use `r.value()`.
609<2> `try_something` has failed; `compute_info` will only be called if an error handler exists which takes a `info` argument.
610+
611* If we pass a function that takes a single argument of type `E &`, LEAF calls the function with the object of type `E` currently loaded in an active `context`, associated with the error. If no such object is available, a new one is default-initialized and then passed to the function.
612+
613For example, if an operation that involves many different files fails, a program may provide for collecting all relevant file names in a `e_relevant_file_names` object:
614+
615[source,c++]
616----
617struct e_relevant_file_names
618{
619  std::vector<std::string> value;
620};
621
622leaf::result<void> operation( char const * file_name ) noexcept
623{
624  if( leaf::result<int> r = try_something() )
625  { <1>
626    ....
627    return { };
628  }
629  else
630  {
631    return r.load( <2>
632      [&](e_relevant_file_names & e)
633      {
634        e.value.push_back(file_name);
635      } );
636  }
637}
638----
639[.text-right]
640<<result>> | <<result::load>>
641+
642<1> Success! Use `r.value()`.
643<2> `try_something` has failed -- add `file_name` to the `e_relevant_file_names` object, associated with the `error_id` communicated in `r`. Note, however, that the passed function will only be called iff in the call stack there are error handlers that take an `e_relevant_file_names` object.
644
645'''
646
647[[tutorial-on_error]]
648=== Using `on_error`
649
650It is not typical for an error-reporting function to be able to supply all of the data needed by a suitable error-handling function in order to recover from the failure. For example, a function that reports `FILE` operation failures may not have access to the file name, yet an error handling function needs it in order to print a useful error message.
651
652Of course the file name is typically readily available in the call stack leading to the failed `FILE` operation. Below, while `parse_info` can't report the file name, `parse_file` can and does:
653
654[source,c++]
655----
656leaf::result<info> parse_info( FILE * f ) noexcept; <1>
657
658leaf::result<info> parse_file( char const * file_name ) noexcept
659{
660  auto load = leaf::on_error(leaf::e_file_name{file_name}); <2>
661
662  if( FILE * f = fopen(file_name,"r") )
663  {
664    auto r = parse_info(f);
665    fclose(f);
666    return r;
667  }
668  else
669    return leaf::new_error( error_enum::file_open_error );
670}
671----
672
673[.text-right]
674<<result>> | <<on_error>> | <<new_error>>
675
676<1> `parse_info` parses `f`, communicating errors using `result<info>`.
677<2> Using `on_error` ensures that the file name is included with any error reported out of `parse_file`. All we need to do is hold on to the returned object `load`; when it expires, if an error is being reported, the passed `e_file_name` value will be automatically associated with it.
678
679TIP: `on_error` --  like `load` -- can be passed any number of arguments.
680
681When we invoke `on_error`, we can pass three kinds of arguments:
682
683. Actual error objects (like in the example above);
684. Functions that take no arguments and return an error object;
685. Functions that take an error object by mutable reference.
686
687If we want to use `on_error` to capture `errno`, we can't just pass <<e_errno>> to it, because at that time it hasn't been set (yet). Instead, we'd pass a function that returns it:
688
689[source,c++]
690----
691void read_file(FILE * f) {
692
693  auto load = leaf::on_error([]{ return e_errno{errno}; });
694
695  ....
696  size_t nr1=fread(buf1,1,count1,f);
697  if( ferror(f) )
698    throw leaf::exception();
699
700  size_t nr2=fread(buf2,1,count2,f);
701  if( ferror(f) )
702    throw leaf::exception();
703
704  size_t nr3=fread(buf3,1,count3,f);
705  if( ferror(f) )
706    throw leaf::exception();
707  ....
708}
709----
710
711Above, if a `throw` statement is reached, LEAF will invoke the function passed to `on_error` and associate the returned `e_errno` object with the exception.
712
713The final type of arguments that can be passed to `on_error` is a function that takes a single mutable error object reference. In this case, `on_error` uses it similarly to how such functios are used by `load`; see <<tutorial-loading>>.
714
715'''
716
717[[tutorial-predicates]]
718=== Using Predicates to Handle Errors
719
720Usually, LEAF error handlers are selected based on the type of the arguments they take and the type of the available error objects. When an error handler takes a predicate type as an argument, the <<handler_selection_procedure,handler selection procedure>> is able to also take into account the _value_ of the available error objects.
721
722Consider this error code enum:
723
724[source,c++]
725----
726enum class my_error
727{
728  e1=1,
729  e2,
730  e3
731};
732----
733
734We could handle `my_error` errors like so:
735
736[source,c++]
737----
738return leaf::try_handle_some(
739  []
740  {
741    return f(); // returns leaf::result<T>
742  },
743
744  []( my_error e )
745  { <1>
746    switch(e)
747    {
748      case my_error::e1:
749        ....; <2>
750        break;
751      case my_error::e2:
752      case my_error::e3:
753        ....; <3>
754        break;
755      default:
756        ....; <4>
757        break;
758  } );
759----
760<1> This handler will be selected if we've got a `my_error` object.
761<2> Handle `e1` errors.
762<3> Handle `e2` and `e3` errors.
763<4> Handle bad `my_error` values.
764
765If `my_error` object is available, LEAF will call our error handler. If not, the failure will be forwarded to our caller.
766
767This can be rewritten using the <<match>> predicate to organize the different cases in different error handlers. The following is equivalent:
768
769[source,c++]
770----
771return leaf::try_handle_some(
772  []
773  {
774    return f(); // returns leaf::result<T>
775  },
776
777  []( leaf::match<my_error, my_error::e1> m )
778  { <1>
779    assert(m.matched == my_error::e1);
780    ....;
781  },
782
783  []( leaf::match<my_error, my_error::e2, my_error::e3> m )
784  { <2>
785    assert(m.matched == my_error::e2 || m.matched == my_error::e3);
786    ....;
787  },
788
789  []( my_error e )
790  { <3>
791    ....;
792  } );
793----
794<1> We've got a `my_error` object that compares equal to `e1`.
795<2> We`ve got a `my_error` object that compares equal to either `e2` or `e3`.
796<3> Handle bad `my_error` values.
797
798The first argument to the `match` template generally specifies the type `E` of the error object `e` that must be available for the error handler to be considered at all. Typically, the rest of the arguments are values. The error handler to be dropped if `e` does not compare equal to any of them.
799
800In particular, `match` works great with `std::error_code`. The following handler is designed to handle `ENOENT` errors:
801
802[source,c++]
803----
804[]( leaf::match<std::error_code, std::errc::no_such_file_or_directory> )
805{
806}
807----
808
809This, however, requires {CPP}17 or newer, because it is impossible to infer the type of the error enum (in this case, `std::errc`) from the specified type `std::error_code`, and {CPP}11 does not allow `auto` template arguments. LEAF provides the following workaround, compatible with {CPP}11:
810
811[source,c++]
812----
813[]( leaf::match<leaf::condition<std::errc>, std::errc::no_such_file_or_directory> )
814{
815}
816----
817
818In addition, it is possible to select a handler based on `std::error_category`. The following handler will match any `std::error_code` of the `std::generic_category` (requires {CPP}17 or newer):
819
820[source,c++]
821----
822[]( std::error_code, leaf::category<std::errc>> )
823{
824}
825----
826
827TIP: See <<match>> for more examples.
828
829The following predicates are available:
830
831* <<match>>: as described above.
832* <<match_value>>: where `match<E, V...>` compares the object `e` of type `E` with the values `V...`, `match_value<E, V...>` compare `e.value` with the values `V...`.
833* <<match_member>>: similar to `match_value`, but takes a pointer to the data member to compare; that is, `match_member<&E::value, V...>` is equvialent to `match_value<E, V...>`. Note, however, that `match_member` requires {CPP}17 or newer, while `match_value` does not.
834* `<<catch_,catch_>><Ex...>`: Similar to `match`, but checks whether the caught `std::exception` object can be `dynamic_cast` to any of the `Ex` types.
835* <<if_not>> is a special predicate that takes any other predicate `Pred` and requires that an error object of type `E` is available and that `Pred` evaluates to `false`. For example, `if_not<match<E, V...>>` requires that an object `e` of type `E` is available, and that it does not compare equal to any of the specified `V...`.
836
837Finally, the predicate system is easily extensible, see <<predicates,Predicates>>.
838
839NOTE: See also <<tutorial-std_error_code>>.
840
841'''
842
843[[tutorial-binding_handlers]]
844=== Binding Error Handlers in a `std::tuple`
845
846Consider this snippet:
847
848[source,c++]
849----
850leaf::try_handle_all(
851  [&]
852  {
853    return f(); // returns leaf::result<T>
854  },
855
856  [](my_error_enum x)
857  {
858    ...
859  },
860
861  [](read_file_error_enum y, e_file_name const & fn)
862  {
863    ...
864  },
865
866  []
867  {
868    ...
869  });
870----
871
872[.text-right]
873<<try_handle_all>> | <<e_file_name>>
874
875Looks pretty simple, but what if we need to attempt a different set of operations yet use the same handlers? We could repeat the same thing with a different function passed as `TryBlock` for `try_handle_all`:
876
877[source,c++]
878----
879leaf::try_handle_all(
880  [&]
881  {
882    return g(); // returns leaf::result<T>
883  },
884
885  [](my_error_enum x)
886  {
887    ...
888  },
889
890  [](read_file_error_enum y, e_file_name const & fn)
891  {
892    ...
893  },
894
895  []
896  {
897    ...
898  });
899----
900
901That works, but it is better to bind our error handlers in a `std::tuple`:
902
903[source,c++]
904----
905auto error_handlers = std::make_tuple(
906  [](my_error_enum x)
907  {
908    ...
909  },
910
911  [](read_file_error_enum y, e_file_name const & fn)
912  {
913    ...
914  },
915
916  []
917  {
918    ...
919  });
920----
921
922The `error_handlers` tuple can later be used with any error handling function:
923
924[source,c++]
925----
926leaf::try_handle_all(
927  [&]
928  {
929    // Operations which may fail <1>
930  },
931
932  error_handlers );
933
934leaf::try_handle_all(
935  [&]
936  {
937    // Different operations which may fail <2>
938  },
939
940  error_handlers ); <3>
941----
942[.text-right]
943<<try_handle_all>> | <<error_info>>
944
945<1> One set of operations which may fail...
946<2> A different set of operations which may fail...
947<3> ... both using the same `error_handlers`.
948
949Error-handling functions accept a `std::tuple` of error handlers in place of any error handler. The behavior is as if the tuple is unwrapped in-place.
950
951'''
952
953[[tutorial-async]]
954=== Transporting Error Objects Between Threads
955
956Error objects are stored on the stack in an instance of the <<context>> class template in the scope of e.g. <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> functions. When using concurrency, we need a mechanism to collect error objects in one thread, then use them to handle errors in another thread.
957
958LEAF offers two interfaces for this purpose, one using `result<T>`, and another designed for programs that use exception handling.
959
960[[tutorial-async_result]]
961==== Using `result<T>`
962
963Let's assume we have a `task` that we want to launch asynchronously, which produces a `task_result` but could also fail:
964
965[source,c++]
966----
967leaf::result<task_result> task();
968----
969
970Because the task will run asynchronously, in case of a failure we need it to capture the relevant error objects but not handle errors. To this end, in the main thread we bind our error handlers in a `std::tuple`, which we will later use to handle errors from each completed asynchronous task (see <<tutorial-binding_handlers,tutorial>>):
971
972[source,c++]
973----
974auto error_handlers = std::make_tuple(
975  [](E1 e1, E2 e2)
976  {
977    //Deal with E1, E2
978    ....
979    return { };
980  },
981
982  [](E3 e3)
983  {
984    //Deal with E3
985    ....
986    return { };
987  } );
988----
989
990Why did we start with this step? Because we need to create a <<context>> object to collect the error objects we need. We could just instantiate the `context` template with `E1`, `E2` and `E3`, but that would be prone to errors, since it could get out of sync with the handlers we use. Thankfully LEAF can deduce the types we need automatically, we just need to show it our `error_handlers`:
991
992[source,c++]
993----
994std::shared_ptr<leaf::polymorphic_context> ctx = leaf::make_shared_context(error_handlers);
995----
996
997The `polymorphic_context` type is an abstract base class that has the same members as any instance of the `context` class template, allowing us to erase its exact type. In this case what we're holding in `ctx` is a `context<E1, E2, E3>`, where `E1`, `E2` and `E3` were deduced automatically from the `error_handlers` tuple we passed to `make_shared_context`.
998
999We're now ready to launch our asynchronous task:
1000
1001[source,c++]
1002----
1003std::future<leaf::result<task_result>> launch_task() noexcept
1004{
1005  return std::async(
1006    std::launch::async,
1007    [&]
1008    {
1009      std::shared_ptr<leaf::polymorphic_context> ctx = leaf::make_shared_context(error_handlers);
1010      return leaf::capture(ctx, &task);
1011    } );
1012}
1013----
1014
1015[.text-right]
1016<<result>> | <<make_shared_context>> | <<capture>>
1017
1018That's it! Later when we `get` the `std::future`, we can process the returned `result<task_result>` in a call to <<try_handle_some>>, using the `error_handlers` tuple we created earlier:
1019
1020[source,c++]
1021----
1022//std::future<leaf::result<task_result>> fut;
1023fut.wait();
1024
1025return leaf::try_handle_some(
1026
1027  [&]() -> leaf::result<void>
1028  {
1029    BOOST_LEAF_AUTO(r, fut.get());
1030    //Success!
1031    return { }
1032  },
1033
1034  error_handlers );
1035----
1036
1037[.text-right]
1038<<try_handle_some>> | <<result>> | <<BOOST_LEAF_AUTO>>
1039
1040The reason this works is that in case it communicates a failure, `leaf::result<T>` is able to hold a `shared_ptr<polymorphic_context>` object. That is why earlier instead of calling `task()` directly, we called `leaf::capture`: it calls the passed function and, in case that fails, it stores the `shared_ptr<polymorphic_context>` we created in the returned `result<T>`, which now doesn't just communicate the fact that an error has occurred, but also holds the `context` object that `try_handle_some` needs in order to supply a suitable handler with arguments.
1041
1042NOTE: Follow this link to see a complete example program: https://github.com/boostorg/leaf/blob/master/examples/capture_in_result.cpp?ts=4[capture_in_result.cpp].
1043
1044'''
1045
1046[[tutorial-async_eh]]
1047==== Using Exception Handling
1048
1049Let's assume we have an asynchronous `task` which produces a `task_result` but could also throw:
1050
1051[source,c++]
1052----
1053task_result task();
1054----
1055
1056Just like we saw in <<tutorial-async_result>>, first we will bind our error handlers in a `std::tuple`:
1057
1058[source,c++]
1059----
1060auto handle_errors = std::make_tuple(
1061{
1062  [](E1 e1, E2 e2)
1063  {
1064    //Deal with E1, E2
1065    ....
1066    return { };
1067  },
1068
1069  [](E3 e3)
1070  {
1071    //Deal with E3
1072    ....
1073    return { };
1074  } );
1075----
1076
1077Launching the task looks the same as before, except that we don't use `result<T>`:
1078
1079[source,c++]
1080----
1081std::future<task_result> launch_task()
1082{
1083  return std::async(
1084    std::launch::async,
1085    [&]
1086    {
1087      std::shared_ptr<leaf::polymorphic_context> ctx = leaf::make_shared_context(&handle_error);
1088      return leaf::capture(ctx, &task);
1089    } );
1090}
1091----
1092
1093[.text-right]
1094<<make_shared_context>> | <<capture>>
1095
1096That's it! Later when we `get` the `std::future`, we can process the returned `task_result` in a call to <<try_catch>>, using the `error_handlers` we saved earlier, as if it was generated locally:
1097
1098[source,c++]
1099----
1100//std::future<task_result> fut;
1101fut.wait();
1102
1103return leaf::try_catch(
1104
1105  [&]
1106  {
1107    task_result r = fut.get(); // Throws on error
1108    //Success!
1109  },
1110
1111  error_handlers );
1112----
1113
1114[.text-right]
1115<<try_catch>>
1116
1117This works similarly to using `result<T>`, except that the `std::shared_ptr<polymorphic_context>` is transported in an exception object (of unspecified type which <<try_catch>> recognizes and then automatically unwraps the original exception).
1118
1119NOTE: Follow this link to see a complete example program: https://github.com/boostorg/leaf/blob/master/examples/capture_in_exception.cpp?ts=4[capture_in_exception.cpp].
1120
1121'''
1122
1123[[tutorial-classification]]
1124=== Classification of Failures
1125
1126It is common for any given interface to define an `enum` that lists all possible error codes that the API reports. The benefit of this approach is that the list is complete and usually contains comments, so we know where to go for reference.
1127
1128The disadvantage of such flat enums is that they do not support handling a whole class of failures. Consider this error handler from the <<introduction-result,introduction section>>:
1129
1130[source,c++]
1131----
1132....
1133[](leaf::match<error_code, size_error, read_error, eof_error>, leaf::e_errno const * errn, leaf::e_file_name const & fn)
1134{
1135  std::cerr << "Failed to access " << fn.value;
1136  if( errn )
1137    std::cerr << ", errno=" << *errn;
1138  std::cerr << std::endl;
1139  return 3;
1140},
1141....
1142----
1143
1144It will get called if the value of the `error_code` enum communicated with the failure is one of `size_error`, `read_error` or `eof_error`. In short, the idea is to handle any input error.
1145
1146But what if later we add support for detecting and reporting a new type of input error, e.g. `permissions_error`? It is easy to add that to our `error_code` enum; but now our input error handler won't recognize this new input error -- and we have a bug.
1147
1148If we can use exceptions, the situation is better because exception types can be organized in a hierarchy in order to classify failures:
1149
1150[source,c++]
1151----
1152struct input_error: std::exception { };
1153struct read_error: input_error { };
1154struct size_error: input_error { };
1155struct eof_error: input_error { };
1156----
1157
1158In terms of LEAF, our input error exception handler now looks like this:
1159
1160[source,c++]
1161----
1162[](input_error &, leaf::e_errno const * errn, leaf::e_file_name const & fn)
1163{
1164  std::cerr << "Failed to access " << fn.value;
1165  if( errn )
1166    std::cerr << ", errno=" << *errn;
1167  std::cerr << std::endl;
1168  return 3;
1169},
1170----
1171
1172This is future-proof, but still not ideal, because it is not possible to refine the classification of the failure after the exception object has been thrown.
1173
1174LEAF supports a novel style of error handling where the classification of failures does not use error code values or exception type hierarchies. If we go back to the introduction section, instead of defining:
1175
1176[source,c++]
1177----
1178enum error_code
1179{
1180  ....
1181  read_error,
1182  size_error,
1183  eof_error,
1184  ....
1185};
1186----
1187
1188We could define:
1189
1190[source,c++]
1191----
1192....
1193struct input_error { };
1194struct read_error { };
1195struct size_error { };
1196struct eof_error { };
1197....
1198----
1199
1200With this in place, `file_read` from the https://github.com/boostorg/leaf/blob/master/examples/print_file/print_file_result.cpp?ts=4[print_file_result.cpp] example can be rewritten like this:
1201
1202[source,c++]
1203----
1204leaf::result<void> file_read( FILE & f, void * buf, int size )
1205{
1206  int n = fread(buf, 1, size, &f);
1207
1208  if( ferror(&f) )
1209    return leaf::new_error(input_error{}, read_error{}, leaf::e_errno{errno}); <1>
1210
1211  if( n!=size )
1212    return leaf::new_error(input_error{}, eof_error{}); <2>
1213
1214  return { };
1215}
1216----
1217[.text-right]
1218<<result>> | <<new_error>> | <<e_errno>>
1219
1220<1> This error is classified as `input_error` and `read_error`.
1221<2> This error is classified as `input_error` and `eof_error`.
1222
1223Or, even better:
1224
1225[source,c++]
1226----
1227leaf::result<void> file_read( FILE & f, void * buf, int size )
1228{
1229  auto load = leaf::on_error(input_error{}); <1>
1230
1231  int n = fread(buf, 1, size, &f);
1232
1233  if( ferror(&f) )
1234    return leaf::new_error(read_error{}, leaf::e_errno{errno}); <2>
1235
1236  if( n!=size )
1237    return leaf::new_error(eof_error{}); <3>
1238
1239  return { };
1240}
1241----
1242[.text-right]
1243<<result>> | <<on_error>> | <<new_error>> | <<e_errno>>
1244
1245<1> Any error escaping this scope will be classified as `input_error`
1246<2> In addition, this error is classified as `read_error`.
1247<3> In addition, this error is classified as `eof_error`.
1248
1249This technique works just as well if we choose to use exception handling:
1250
1251[source,c++]
1252----
1253void file_read( FILE & f, void * buf, int size )
1254{
1255  auto load = leaf::on_error(input_error{});
1256
1257  int n = fread(buf, 1, size, &f);
1258
1259  if( ferror(&f) )
1260    throw leaf::exception(read_error{}, leaf::e_errno{errno});
1261
1262  if( n!=size )
1263    throw leaf::exception(eof_error{});
1264}
1265----
1266[.text-right]
1267<<on_error>> | <<exception>> | <<e_errno>>
1268
1269NOTE: If the type of the first argument passed to `leaf::exception` derives from `std::exception`, it will be used to initialize the returned exception object taken by `throw`. Here this is not the case, so the function returns a default-initialized `std::exception` object, while the first (and any other) argument is associated with the failure.
1270
1271And now we can write a future-proof handler that can handle any `input_error`:
1272
1273[source,c++]
1274----
1275....
1276[](input_error, leaf::e_errno const * errn, leaf::e_file_name const & fn)
1277{
1278  std::cerr << "Failed to access " << fn.value;
1279  if( errn )
1280    std::cerr << ", errno=" << *errn;
1281  std::cerr << std::endl;
1282  return 3;
1283},
1284....
1285----
1286
1287Remarkably, because the classification of the failure does not depend on error codes or on exception types, this error handler can be used with `try_catch` if we use exception handling, or with `try_handle_some`/`try_handle_all` if we do not. Here is the complete example from the introduction section, rewritten to use this technique:
1288
1289* https://github.com/boostorg/leaf/blob/master/examples/print_file/print_file_result_error_tags.cpp?ts=4[print_file_result_error_tags.cpp] (using `leaf::result<T>`).
1290* https://github.com/boostorg/leaf/blob/master/examples/print_file/print_file_eh_error_tags.cpp?ts=4[print_file_eh_error_tags.cpp] (using exception handling).
1291
1292'''
1293
1294[[tutorial-exception_to_result]]
1295=== Converting Exceptions to `result<T>`
1296
1297It is sometimes necessary to catch exceptions thrown by a lower-level library function, and report the error through different means, to a higher-level library which may not use exception handling.
1298
1299NOTE: Understand that error handlers that take arguments of types that derive from `std::exception` work correctly -- regardless of whether the error object itself is thrown as an exception, or <<tutorial-loading,loaded>> into a <<context>>. The technique described here is only needed when the exception must be communicated through functions which are not exception-safe, or are compiled with exception-handling disabled.
1300
1301Suppose we have an exception type hierarchy and a function `compute_answer_throws`:
1302
1303[source,c++]
1304----
1305class error_base: public std::exception { };
1306class error_a: public error_base { };
1307class error_b: public error_base { };
1308class error_c: public error_base { };
1309
1310int compute_answer_throws()
1311{
1312  switch( rand()%4 )
1313  {
1314    default: return 42;
1315    case 1: throw error_a();
1316    case 2: throw error_b();
1317    case 3: throw error_c();
1318  }
1319}
1320----
1321
1322We can write a simple wrapper using `exception_to_result`, which calls `compute_answer_throws` and switches to `result<int>` for error handling:
1323
1324[source,c++]
1325----
1326leaf::result<int> compute_answer() noexcept
1327{
1328  return leaf::exception_to_result<error_a, error_b>(
1329    []
1330    {
1331      return compute_answer_throws();
1332    } );
1333}
1334----
1335
1336[.text-right]
1337<<result>> | <<exception_to_result>>
1338
1339The `exception_to_result` template takes any number of exception types. All exception types thrown by the passed function are caught, and an attempt is made to convert the exception object to each of the specified types. Each successfully-converted slice of the caught exception object, as well as the return value of `std::current_exception`, are copied and <<tutorial-loading,loaded>>, and in the end the exception is converted to a `<<result,result>><T>` object.
1340
1341(In our example, `error_a` and `error_b` slices as communicated as error objects, but `error_c` exceptions will still be captured by `std::exception_ptr`).
1342
1343Here is a simple function which prints successfully computed answers, forwarding any error (originally reported by throwing an exception) to its caller:
1344
1345[source,c++]
1346----
1347leaf::result<void> print_answer() noexcept
1348{
1349  BOOST_LEAF_AUTO(answer, compute_answer());
1350  std::cout << "Answer: " << answer << std::endl;
1351  return { };
1352}
1353----
1354
1355[.text-right]
1356<<result>> | <<BOOST_LEAF_AUTO>>
1357
1358Finally, here is a scope that handles the errors -- it will work correctly regardless of whether `error_a` and `error_b` objects are thrown as exceptions or not.
1359
1360[source,c++]
1361----
1362leaf::try_handle_all(
1363  []() -> leaf::result<void>
1364  {
1365    BOOST_LEAF_CHECK(print_answer());
1366    return { };
1367  },
1368
1369  [](error_a const & e)
1370  {
1371    std::cerr << "Error A!" << std::endl;
1372  },
1373
1374  [](error_b const & e)
1375  {
1376    std::cerr << "Error B!" << std::endl;
1377  },
1378
1379  []
1380  {
1381    std::cerr << "Unknown error!" << std::endl;
1382  } );
1383----
1384
1385[.text-right]
1386<<try_handle_all>> | <<result>> | <<BOOST_LEAF_CHECK>>
1387
1388NOTE: The complete program illustrating this technique is available https://github.com/boostorg/leaf/blob/master/examples/exception_to_result.cpp?ts=4[here].
1389
1390'''
1391
1392[[tutorial-on_error_in_c_callbacks]]
1393=== Using `error_monitor` to Report Arbitrary Errors from C-callbacks
1394
1395Communicating information pertaining to a failure detected in a C callback is tricky, because C callbacks are limited to a specific static signature, which may not use {CPP} types.
1396
1397LEAF makes this easy. As an example, we'll write a program that uses Lua and reports a failure from a {CPP} function registered as a C callback, called from a Lua program. The failure will be propagated from {CPP}, through the Lua interpreter (written in C), back to the {CPP} function which called it.
1398
1399C/{CPP} functions designed to be invoked from a Lua program must use the following signature:
1400
1401[source,c]
1402----
1403int do_work( lua_State * L ) ;
1404----
1405
1406Arguments are passed on the Lua stack (which is accessible through `L`). Results too are pushed onto the Lua stack.
1407
1408First, let's initialize the Lua interpreter and register `do_work` as a C callback, available for Lua programs to call:
1409
1410[source,c++]
1411----
1412std::shared_ptr<lua_State> init_lua_state() noexcept
1413{
1414  std::shared_ptr<lua_State> L(lua_open(), &lua_close); //<1>
1415
1416  lua_register(&*L, "do_work", &do_work); //<2>
1417
1418  luaL_dostring(&*L, "\ //<3>
1419\n      function call_do_work()\
1420\n          return do_work()\
1421\n      end");
1422
1423  return L;
1424}
1425----
1426<1> Create a new `lua_State`. We'll use `std::shared_ptr` for automatic cleanup.
1427<2> Register the `do_work` {CPP} function as a C callback, under the global name `do_work`. With this, calls from Lua programs to `do_work` will land in the `do_work` {CPP} function.
1428<3> Pass some Lua code as a `C` string literal to Lua. This creates a global Lua function called `call_do_work`, which we will later ask Lua to execute.
1429
1430Next, let's define our `enum` used to communicate `do_work` failures:
1431
1432[source,c++]
1433----
1434enum do_work_error_code
1435{
1436  ec1=1,
1437  ec2
1438};
1439----
1440
1441We're now ready to define the `do_work` callback function:
1442
1443[source,c++]
1444----
1445int do_work( lua_State * L ) noexcept
1446{
1447  bool success = rand()%2; <1>
1448  if( success )
1449  {
1450    lua_pushnumber(L, 42); <2>
1451    return 1;
1452  }
1453  else
1454  {
1455    leaf::new_error(ec1); <3>
1456    return luaL_error(L, "do_work_error"); <4>
1457  }
1458}
1459----
1460[.text-right]
1461<<new_error>> | <<error_id::load>>
1462
1463<1> "Sometimes" `do_work` fails.
1464<2> In case of success, push the result on the Lua stack, return back to Lua.
1465<3> Generate a new `error_id` and associate a `do_work_error_code` with it. Normally, we'd return this in a `leaf::result<T>`, but the `do_work` function signature (required by Lua) does not permit this.
1466<4> Tell the Lua interpreter to abort the Lua program.
1467
1468Now we'll write the function that calls the Lua interpreter to execute the Lua function `call_do_work`, which in turn calls `do_work`. We'll return `<<result,result>><int>`, so that our caller can get the answer in case of success, or an error:
1469
1470[source,c++]
1471----
1472leaf::result<int> call_lua( lua_State * L )
1473{
1474  lua_getfield(L, LUA_GLOBALSINDEX, "call_do_work");
1475
1476  error_monitor cur_err;
1477  if( int err=lua_pcall(L, 0, 1, 0) ) <1>
1478  {
1479    auto load = leaf::on_error(e_lua_error_message{lua_tostring(L,1)}); <2>
1480    lua_pop(L,1);
1481
1482    return cur_err.assigned_error_id().load(e_lua_pcall_error{err}); <3>
1483  }
1484  else
1485  {
1486    int answer = lua_tonumber(L, -1); <4>
1487    lua_pop(L, 1);
1488    return answer;
1489  }
1490}
1491----
1492[.text-right]
1493<<result>> | <<on_error>> | <<error_monitor>>
1494
1495<1> Ask the Lua interpreter to call the global Lua function `call_do_work`.
1496<2> `on_error` works as usual.
1497<3> `load` will use the `error_id` generated in our Lua callback. This is the same `error_id` the `on_error` uses as well.
1498<4> Success! Just return the `int` answer.
1499
1500Finally, here is the `main` function which exercises `call_lua`, each time handling any failure:
1501
1502[source,c++]
1503----
1504int main() noexcept
1505{
1506  std::shared_ptr<lua_State> L=init_lua_state();
1507
1508  for( int i=0; i!=10; ++i )
1509  {
1510    leaf::try_handle_all(
1511
1512      [&]() -> leaf::result<void>
1513      {
1514        BOOST_LEAF_AUTO(answer, call_lua(&*L));
1515        std::cout << "do_work succeeded, answer=" << answer << '\n'; <1>
1516        return { };
1517      },
1518
1519      [](do_work_error_code e) <2>
1520      {
1521        std::cout << "Got do_work_error_code = " << e <<  "!\n";
1522      },
1523
1524      [](e_lua_pcall_error const & err, e_lua_error_message const & msg) <3>
1525      {
1526        std::cout << "Got e_lua_pcall_error, Lua error code = " << err.value << ", " << msg.value << "\n";
1527      },
1528
1529      [](leaf::error_info const & unmatched)
1530      {
1531        std::cerr <<
1532          "Unknown failure detected" << std::endl <<
1533          "Cryptic diagnostic information follows" << std::endl <<
1534          unmatched;
1535      } );
1536  }
1537----
1538[.text-right]
1539<<try_handle_all>> | <<result>> | <<BOOST_LEAF_AUTO>> | <<error_info>>
1540
1541<1> If the call to `call_lua` succeeded, just print the answer.
1542<2> Handle `do_work` failures.
1543<3> Handle all other `lua_pcall` failures.
1544
1545[NOTE]
1546--
1547Follow this link to see the complete program: https://github.com/boostorg/leaf/blob/master/examples/lua_callback_result.cpp?ts=4[lua_callback_result.cpp].
1548
1549Remarkably, the Lua interpreter is {CPP} exception-safe, even though it is written in C. Here is the same program, this time using a {CPP} exception to report failures from `do_work`: https://github.com/boostorg/leaf/blob/master/examples/lua_callback_eh.cpp?ts=4[lua_callback_eh.cpp].
1550--
1551
1552'''
1553
1554[[tutorial-diagnostic_information]]
1555=== Diagnostic Information
1556
1557LEAF is able to automatically generate diagnostic messages that include information about all error objects available to error handlers. For this purpose, it needs to be able to print objects of user-defined error types.
1558
1559To do this, LEAF attempts to bind an unqualified call to `operator<<`, passing a `std::ostream` and the error object. If that fails, it will also attempt to bind `operator<<` that takes the `.value` of the error type. If that also doesn't compile, the error object value will not appear in diagnostic messages, though LEAF will still print its type.
1560
1561Even with error types that define a printable `.value`, the user may still want to overload `operator<<` for the enclosing `struct`, e.g.:
1562
1563[source,c++]
1564----
1565struct e_errno
1566{
1567  int value;
1568
1569  friend std::ostream & operator<<( std::ostream & os, e_errno const & e )
1570  {
1571    return os << "errno = " << e.value << ", \"" << strerror(e.value) << '"';
1572  }
1573};
1574----
1575
1576The `e_errno` type above is designed to hold `errno` values. The defined `operator<<` overload will automatically include the output from `strerror` when `e_errno` values are printed (LEAF defines `e_errno` in `<boost/leaf/common.hpp>`, together with other commonly-used error types).
1577
1578TIP: The automatically-generated diagnostic messages are developer-friendly, but not user-friendly. Therefore, `operator<<` overloads for error types should only print technical information in English, and should not attempt to localize strings or to format a user-friendly message; this should be done in error-handling functions specifically designed for that purpose.
1579
1580'''
1581
1582[[tutorial-std_error_code]]
1583=== Working with `std::error_code`, `std::error_condition`
1584
1585==== Introduction
1586
1587The relationship between `std::error_code` and `std::error_condition` is not easily understood from reading the standard specifications. This section explains how they're supposed to be used, and how LEAF interacts with them.
1588
1589The idea behind `std::error_code` is to encode both an integer value representing an error code, as well as the domain of that value. The domain is represented by a `std::error_category` [underline]#reference#. Conceptually, a `std::error_code` is like a `pair<std::error_category const &, int>`.
1590
1591Let's say we have this `enum`:
1592
1593[source,c++]
1594----
1595enum class libfoo_error
1596{
1597  e1 = 1,
1598  e2,
1599  e3
1600};
1601----
1602
1603We want to be able to transport `libfoo_error` values in `std::error_code` objects. This erases their static type, which enables them to travel freely across API boundaries. To this end, we must define a `std::error_category` that represents our `libfoo_error` type:
1604
1605[source,c++]
1606----
1607std::error_category const & libfoo_error_category()
1608{
1609  struct category: std::error_category
1610  {
1611    char const * name() const noexcept override
1612    {
1613      return "libfoo";
1614    }
1615
1616    std::string message(int code) const override
1617    {
1618      switch( libfoo_error(code) )
1619      {
1620        case libfoo_error::e1: return "e1";
1621        case libfoo_error::e2: return "e2";
1622        case libfoo_error::e3: return "e3";
1623        default: return "error";
1624      }
1625    }
1626  };
1627
1628  static category c;
1629  return c;
1630}
1631----
1632
1633We also need to inform the standard library that `libfoo_error` is compatible with `std::error_code`, and provide a factory function which can be used to make `std::error_code` objects out of `libfoo_error` values:
1634
1635[source,c++]
1636----
1637namespace std
1638{
1639  template <>
1640  struct is_error_code_enum<libfoo_error>: std::true_type
1641  {
1642  };
1643}
1644
1645std::error_code make_error_code(libfoo_error e)
1646{
1647  return std::error_code(int(e), libfoo_error_category());
1648}
1649----
1650
1651With this in place, if we receive a `std::error_code`, we can easily check if it represents some of the `libfoo_error` values we're interested in:
1652
1653[source,c++]
1654----
1655std::error_code f();
1656
1657....
1658auto ec = f();
1659if( ec == libfoo_error::e1 || ec == libfoo_error::e2 )
1660{
1661  // We got either a libfoo_error::e1 or a libfoo_error::e2
1662}
1663----
1664
1665This works because the standard library detects that `std::is_error_code_enum<libfoo_error>::value` is `true`, and then uses `make_error_code` to create a `std::error_code` object it actually uses to compare to `ec`.
1666
1667So far so good, but remember, the standard library defines another type also, `std::error_condition`. The first confusing thing is that in terms of its physical representation, `std::error_condition` is identical to `std::error_code`; that is, it is also like a pair of `std::error_category` reference and an `int`. Why do we need two different types which use identical physical representation?
1668
1669The key to answering this question is to understand that `std::error_code` objects are designed to be returned from functions to indicate failures. In contrast, `std::error_condition` objects are [underline]#never# supposed to be communicated; their purpose is to interpret the `std::error_code` values being communicated. The idea is that in a given program there may be multiple different "physical" (maybe platform-specific) `std::error_code` values which all indicate the same "logical" `std::error_condition`.
1670
1671This leads us to the second confusing thing about `std::error_condition`: it uses the same `std::error_category` type, but for a completely different purpose: to specify what `std::error_code` values are equivalent to what `std::error_condition` values.
1672
1673Let's say that in addition to `libfoo`, our program uses another library, `libbar`, which communicates failures in terms of `std::error_code` with a different error category. Perhaps `libbar_error` looks like this:
1674
1675[source,c++]
1676----
1677enum class libbar_error
1678{
1679  e1 = 1,
1680  e2,
1681  e3,
1682  e4
1683};
1684
1685// Boilerplate omitted:
1686// - libbar_error_category()
1687// - specialization of std::is_error_code_enum
1688// - make_error_code factory function for libbar_error.
1689----
1690
1691We can now use `std::error_condition` to define the _logical_ error conditions represented by the `std::error_code` values communicated by `libfoo` and `libbar`:
1692
1693[source,c++]
1694----
1695enum class my_error_condition <1>
1696{
1697  c1 = 1,
1698  c2
1699};
1700
1701std::error_category const & libfoo_error_category() <2>
1702{
1703  struct category: std::error_category
1704  {
1705    char const * name() const noexcept override
1706    {
1707      return "my_error_condition";
1708    }
1709
1710    std::string message(int cond) const override
1711    {
1712      switch( my_error_condition(code) )
1713      {
1714        case my_error_condition::c1: return "c1";
1715        case my_error_condition::c2: return "c2";
1716        default: return "error";
1717      }
1718    }
1719
1720    bool equivalent(std::error_code const & code, int cond) const noexcept
1721    {
1722      switch( my_error_condition(cond) )
1723      {
1724        case my_error_condition::c1: <3>
1725          return
1726            code == libfoo_error::e1 ||
1727            code == libbar_error::e3 ||
1728            code == libbar_error::e4;
1729        case my_error_condition::c2: <4>
1730          return
1731            code == libfoo_error::e2 ||
1732            code == libbar_error::e1 ||
1733            code == libbar_error::e2;
1734        default:
1735          return false;
1736      }
1737    }
1738  };
1739
1740  static category c;
1741  return c;
1742}
1743
1744namespace std
1745{
1746  template <> <5>
1747  class is_error_condition_enum<my_error_condition>: std::true_type
1748  {
1749  };
1750}
1751
1752std::error_condition make_error_condition(my_error_condition e) <6>
1753{
1754  return std::error_condition(int(e), my_error_condition_error_category());
1755}
1756----
1757<1> Enumeration of the two logical error conditions, `c1` and `c2`.
1758<2> Define the `std::error_category` for `std::error_condition` objects that represent a `my_error_condition`.
1759<3> Here we specify that any of `libfoo:error::e1`, `libbar_error::e3` and `libbar_error::e4` are logically equivalent to `my_error_condition::c1`, and that...
1760<4> ...any of `libfoo:error::e2`, `libbar_error::e1` and `libbar_error::e2` are logically equivalent to `my_error_condition::c2`.
1761<5> This specialization tells the standard library that the `my_error_condition` enum is designed to be used with `std::error_condition`.
1762<6> The factory function to make `std::error_condition` objects out of `my_error_condition` values.
1763
1764Phew!
1765
1766Now, if we have a `std::error_code` object `ec`, we can easily check if it is equivalent to `my_error_condition::c1` like so:
1767
1768[source,c++]
1769----
1770if( ec == my_error_condition::c1 )
1771{
1772  // We have a c1 in our hands
1773}
1774----
1775
1776Again, remember that beyond defining the `std::error_category` for `std::error_condition` objects initialized with a `my_error_condition` value, we don't need to interact with the actual `std::error_condition` instances: they're created when needed to compare to a `std::error_code`, and that's pretty much all they're good for.
1777
1778==== Support in LEAF
1779
1780The following support for `std::error_code` and `std::error_condition` is available:
1781
1782* The <<match>> template can be used as an argument to a LEAF error handler, so it can be considered based on the value of a communicated `std::error_code`.
1783+
1784NOTE: See <<match>> for examples.
1785+
1786* The <<error_id>> type can be converted to a `std::error_code`; see <<error_id::to_error_code>>. The returned object encodes the state of the `error_id` without any loss of information. This is useful if an `error_id` needs to be communicated through interfaces that support `std::error_code` but do not use LEAF.
1787* The `error_id` type can be implicitly initialized with a `std::error_code`. If the `std::error_code` was created using `to_error_code`, the original `error_id` state is restored. Otherwise, the `std::error_code` is <<tutorial-loading,loaded>> so it can be used by LEAF error handlers, while the `error_id` itself is initialized by <<new_error>>.
1788* The `leaf::result<T>` type can be implicitly initialized with an `error_id`, which means that it can be implicitly initialized with a `std::error_code`.
1789
1790'''
1791
1792[[tutorial-boost_exception_integration]]
1793=== Boost Exception Integration
1794
1795Instead of the https://www.boost.org/doc/libs/release/libs/exception/doc/get_error_info.html[`boost::get_error_info`] API defined by Boost Exception, it is possible to use LEAF error handlers directly. Consider the following use of `boost::get_error_info`:
1796
1797[source,c++]
1798----
1799typedef boost::error_info<struct my_info_, int> my_info;
1800
1801void f(); // Throws using boost::throw_exception
1802
1803void g()
1804{
1805  try
1806  {
1807    f();
1808  },
1809  catch( boost::exception & e )
1810  {
1811    if( int const * x = boost::get_error_info<my_info>(e) )
1812      std::cerr << "Got my_info with value = " << *x;
1813  } );
1814}
1815----
1816
1817We can rewrite `g` to access `my_info` using LEAF:
1818
1819[source,c++]
1820----
1821#include <boost/leaf/handle_errors.hpp>
1822
1823void g()
1824{
1825  leaf::try_catch(
1826    []
1827    {
1828      f();
1829    },
1830
1831    []( my_info x )
1832    {
1833      std::cerr << "Got my_info with value = " << x.value();
1834    } );
1835}
1836----
1837[.text-right]
1838<<try_catch>>
1839
1840Taking `my_info` means that the handler will only be selected if the caught exception object carries `my_info` (which LEAF accesses via `boost::get_error_info`).
1841
1842The use of <<match>> is also supported:
1843
1844[source,c++]
1845----
1846void g()
1847{
1848  leaf::try_catch(
1849    []
1850    {
1851      f();
1852    },
1853
1854    []( leaf::match_value<my_info, 42> )
1855    {
1856      std::cerr << "Got my_info with value = 42";
1857    } );
1858}
1859----
1860
1861Above, the handler will be selected if the caught exception object carries `my_info` with `.value()` equal to 42.
1862
1863[[examples]]
1864== Examples
1865
1866See https://github.com/boostorg/leaf/tree/master/examples[github].
1867
1868[[synopsis]]
1869== Synopsis
1870
1871This section lists each public header file in LEAF, documenting the definitions it provides.
1872
1873LEAF headers are designed to minimize coupling:
1874
1875* Headers needed to report or forward but not handle errors are lighter than headers providing error-handling functionality.
1876* Headers that provide exception handling or throwing functionality are separate from headers that provide error-handling or reporting but do not use exceptions.
1877
1878A standalone single-header option is available; please `#include <boost/leaf.hpp>`.
1879
1880'''
1881
1882[[synopsis-reporting]]
1883=== Error Reporting
1884
1885[[error.hpp]]
1886==== `error.hpp`
1887
1888====
1889.#include <boost/leaf/error.hpp>
1890[source,c++]
1891----
1892namespace boost { namespace leaf {
1893
1894  class error_id
1895  {
1896  public:
1897
1898    error_id() noexcept;
1899
1900    template <class Enum>
1901    error_id( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
1902
1903    error_id( std::error_code const & ec ) noexcept;
1904
1905    int value() const noexcept;
1906    explicit operator bool() const noexcept;
1907
1908    std::error_code to_error_code() const noexept;
1909
1910    friend bool operator==( error_id a, error_id b ) noexcept;
1911    friend bool operator!=( error_id a, error_id b ) noexcept;
1912    friend bool operator<( error_id a, error_id b ) noexcept;
1913
1914    template <class... Item>
1915    error_id load( Item && ... item ) const noexcept;
1916
1917    friend std::ostream & operator<<( std::ostream & os, error_id x );
1918  };
1919
1920  bool is_error_id( std::error_code const & ec ) noexcept;
1921
1922  template <class... Item>
1923  error_id new_error( Item && ... item ) noexcept;
1924
1925  error_id current_error() noexcept;
1926
1927  //////////////////////////////////////////
1928
1929  class polymorphic_context
1930  {
1931  protected:
1932
1933    polymorphic_context() noexcept = default;
1934    ~polymorphic_context() noexcept = default;
1935
1936  public:
1937
1938    virtual void activate() noexcept = 0;
1939    virtual void deactivate() noexcept = 0;
1940    virtual bool is_active() const noexcept = 0;
1941
1942    virtual void propagate() noexcept = 0;
1943
1944    virtual void print( std::ostream & ) const = 0;
1945  };
1946
1947  //////////////////////////////////////////
1948
1949  template <class Ctx>
1950  class context_activator
1951  {
1952    context_activator( context_activator const & ) = delete;
1953    context_activator & operator=( context_activator const & ) = delete;
1954
1955  public:
1956
1957    explicit context_activator( Ctx & ctx ) noexcept;
1958    context_activator( context_activator && ) noexcept;
1959    ~context_activator() noexcept;
1960  };
1961
1962  template <class Ctx>
1963  context_activator<Ctx> activate_context( Ctx & ctx ) noexcept;
1964
1965  template <class R>
1966  struct is_result_type: std::false_type
1967  {
1968  };
1969
1970  template <class R>
1971  struct is_result_type<R const>: is_result_type<R>
1972  {
1973  };
1974
1975} }
1976
1977#define BOOST_LEAF_ASSIGN(v, r)\
1978  auto && <<temp>> = r;\
1979  if( !<<temp>> )\
1980    return <<temp>>.error();\
1981  v = std::forward<decltype(<<temp>>)>(<<temp>>).value()
1982
1983#define BOOST_LEAF_AUTO(v, r)\
1984  BOOST_LEAF_ASSIGN(auto v, r)
1985
1986#define BOOST_LEAF_CHECK(r)\
1987	{\
1988		auto && <<temp>> = r;\
1989		if( !<<temp>> )\
1990			return <<temp>>.error();\
1991	}
1992
1993#define BOOST_LEAF_NEW_ERROR <<inject e_source_location voodoo>> ::boost::leaf::new_error
1994----
1995
1996[.text-right]
1997Reference: <<error_id>> | <<is_error_id>> | <<new_error>> | <<current_error>> | <<polymorphic_context>> | <<context_activator>> | <<activate_context>> | <<is_result_type>> | <<BOOST_LEAF_ASSIGN>> | <<BOOST_LEAF_AUTO>> | <<BOOST_LEAF_CHECK>> | <<BOOST_LEAF_NEW_ERROR>>
1998====
1999
2000[[common.hpp]]
2001==== `common.hpp`
2002
2003====
2004.#include <boost/leaf/common.hpp>
2005[source,c++]
2006----
2007namespace boost { namespace leaf {
2008
2009  struct e_api_function { char const * value; };
2010
2011  struct e_file_name { std::string value; };
2012
2013  struct e_type_info_name { char const * value; };
2014
2015  struct e_at_line { int value; };
2016
2017  struct e_errno
2018  {
2019    int value;
2020    friend std::ostream & operator<<( std::ostream &, e_errno const & );
2021  };
2022
2023  namespace windows
2024  {
2025    struct e_LastError
2026    {
2027      unsigned value;
2028      friend std::ostream & operator<<( std::ostream &, e_LastError const & );
2029    };
2030  }
2031
2032} }
2033----
2034
2035[.text-right]
2036Reference: <<e_api_function>> | <<e_file_name>> | <<e_at_line>> | <<e_type_info_name>> | <<e_source_location>> | <<e_errno>> | <<e_LastError>>
2037====
2038
2039[[result.hpp]]
2040==== `result.hpp`
2041
2042====
2043.#include <boost/leaf/result.hpp>
2044[source,c++]
2045----
2046namespace boost { namespace leaf {
2047
2048  template <class T>
2049  class result
2050  {
2051  public:
2052
2053    result() noexcept;
2054    result( T && v ) noexcept;
2055    result( T const & v );
2056
2057    template <class U>
2058    result( U && u, <<enabled_if_T_can_be_inited_with_U>> );
2059
2060    result( error_id err ) noexcept;
2061    result( std::shared_ptr<polymorphic_context> && ctx ) noexcept;
2062
2063    template <class Enum>
2064    result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
2065
2066    result( std::error_code const & ec ) noexcept;
2067
2068    result( result && r ) noexcept;
2069
2070    template <class U>
2071    result( result<U> && r ) noexcept;
2072
2073    result & operator=( result && r ) noexcept;
2074
2075    template <class U>
2076    result & operator=( result<U> && r ) noexcept;
2077
2078    explicit operator bool() const noexcept;
2079
2080    T const & value() const;
2081    T & value();
2082
2083    T const & operator*() const;
2084    T & operator*();
2085
2086    T const * operator->() const;
2087    T * operator->();
2088
2089    <<unspecified-type>> error() noexcept;
2090
2091    template <class... Item>
2092    error_id load( Item && ... item ) noexcept;
2093  };
2094
2095  template <>
2096  class result<void>
2097  {
2098  public:
2099
2100    result() noexcept;
2101
2102    result( error_id err ) noexcept;
2103    result( std::shared_ptr<polymorphic_context> && ctx ) noexcept;
2104
2105    template <class Enum>
2106    result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
2107
2108    result( std::error_code const & ec ) noexcept;
2109
2110    result( result && r ) noexcept;
2111
2112    template <class U>
2113    result( result<U> && r ) noexcept;
2114
2115    result & operator=( result && r ) noexcept;
2116
2117    template <class U>
2118    result & operator=( result<U> && r ) noexcept;
2119
2120    explicit operator bool() const noexcept;
2121
2122    void value() const;
2123
2124    <<unspecified-type>> error() noexcept;
2125
2126    template <class... Item>
2127    error_id load( Item && ... item ) noexcept;
2128  };
2129
2130  struct bad_result: std::exception { };
2131
2132  template <class T>
2133  struct is_result_type<result<T>>: std::true_type
2134  {
2135  };
2136
2137} }
2138----
2139
2140[.text-right]
2141Reference: <<result>> | <<is_result_type>>
2142====
2143
2144[[on_error.hpp]]
2145==== `on_error.hpp`
2146
2147====
2148[source,c++]
2149.#include <boost/leaf/on_error.hpp>
2150----
2151namespace boost { namespace leaf {
2152
2153  template <class... Item>
2154  <<unspecified-type>> on_error( Item && ... e ) noexcept;
2155
2156  class error_monitor
2157  {
2158  public:
2159
2160    error_monitor() noexcept;
2161
2162    error_id check() const noexcept;
2163    error_id assigned_error_id() const noexcept;
2164  };
2165
2166} }
2167----
2168
2169[.text-right]
2170Reference: <<on_error>> | <<error_monitor>>
2171====
2172
2173[[exception.hpp]]
2174==== `exception.hpp`
2175
2176====
2177.#include <boost/leaf/exception.hpp>
2178[source,c++]
2179----
2180namespace boost { namespace leaf {
2181
2182  template <class Ex, class... E> <1>
2183  <<unspecified-exception-type>> exception( Ex &&, E && ... ) noexcept;
2184
2185  template <class E1, class... E> <2>
2186  <<unspecified-exception-type>> exception( E1 &&, E && ... ) noexcept;
2187
2188  <<unspecified-exception-type>> exception() noexcept;
2189
2190} }
2191
2192#define BOOST_LEAF_EXCEPTION <<inject e_source_location voodoo>> ::boost::leaf::exception
2193
2194#define BOOST_LEAF_THROW_EXCEPTION <<inject e_source_location + invoke boost::throw_exception voodoo>> ::boost::leaf::exception
2195----
2196
2197[.text-right]
2198Reference: <<exception>> | <<BOOST_LEAF_EXCEPTION>> | <<BOOST_LEAF_THROW_EXCEPTION>>
2199
2200<1> Only enabled if std::is_base_of<std::exception, Ex>::value.
2201<2> Only enabled if !std::is_base_of<std::exception, E1>::value.
2202====
2203
2204==== `capture.hpp`
2205
2206====
2207[source,c++]
2208.#include <boost/leaf/capture_exception.hpp>
2209----
2210namespace boost { namespace leaf {
2211
2212  template <class F, class... A>
2213  decltype(std::declval<F>()(std::forward<A>(std::declval<A>())...))
2214  capture(std::shared_ptr<polymorphic_context> && ctx, F && f, A... a);
2215
2216  template <class... Ex, class F>
2217  <<result<T>-deduced>> exception_to_result( F && f ) noexcept;
2218
2219} }
2220----
2221
2222[.text-right]
2223Reference: <<capture>> | <<exception_to_result>>
2224====
2225
2226'''
2227
2228[[tutorial-handling]]
2229
2230=== Error Handling
2231
2232[[context.hpp]]
2233==== `context.hpp`
2234
2235====
2236.#include <boost/leaf/context.hpp>
2237[source,c++]
2238----
2239namespace boost { namespace leaf {
2240
2241  template <class... E>
2242  class context
2243  {
2244    context( context const & ) = delete;
2245    context & operator=( context const & ) = delete;
2246
2247  public:
2248
2249    context() noexcept;
2250    context( context && x ) noexcept;
2251    ~context() noexcept;
2252
2253    void activate() noexcept;
2254    void deactivate() noexcept;
2255    bool is_active() const noexcept;
2256
2257    void propagate () noexcept;
2258
2259    void print( std::ostream & os ) const;
2260
2261    template <class R, class... H>
2262    R handle_error( R &, H && ... ) const;
2263  };
2264
2265  //////////////////////////////////////////
2266
2267  template <class... H>
2268  using context_type_from_handlers = typename <<unspecified>>::type;
2269
2270  template <class...  H>
2271  BOOST_LEAF_CONSTEXPR context_type_from_handlers<H...> make_context() noexcept;
2272
2273  template <class...  H>
2274  BOOST_LEAF_CONSTEXPR context_type_from_handlers<H...> make_context( H && ... ) noexcept;
2275
2276  template <class...  H>
2277  context_ptr make_shared_context() noexcept;
2278
2279  template <class...  H>
2280  context_ptr make_shared_context( H && ... ) noexcept;
2281
2282} }
2283----
2284
2285[.text-right]
2286Reference: <<context>> | <<context_type_from_handlers>> | <<make_context>> | <<make_shared_context>>
2287====
2288
2289[[handle_errors.hpp]]
2290==== `handle_errors.hpp`
2291
2292====
2293.#include <boost/leaf/handle_errors.hpp>
2294[source,c++]
2295----
2296namespace boost { namespace leaf {
2297
2298  template <class TryBlock, class... H>
2299  typename std::decay<decltype(std::declval<TryBlock>()().value())>::type
2300  try_handle_all( TryBlock && try_block, H && ... h );
2301
2302  template <class TryBlock, class... H>
2303  typename std::decay<decltype(std::declval<TryBlock>()())>::type
2304  try_handle_some( TryBlock && try_block, H && ... h );
2305
2306  template <class TryBlock, class... H>
2307  typename std::decay<decltype(std::declval<TryBlock>()())>::type
2308  try_catch( TryBlock && try_block, H && ... h );
2309
2310  //////////////////////////////////////////
2311
2312  class error_info
2313  {
2314    //No public constructors
2315
2316  public:
2317
2318    error_id error() const noexcept;
2319
2320    bool exception_caught() const noexcept;
2321    std::exception const * exception() const noexcept;
2322
2323    friend std::ostream & operator<<( std::ostream & os, error_info const & x );
2324  };
2325
2326  class diagnostic_info: public error_info
2327  {
2328    //No public constructors
2329
2330    friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x );
2331  };
2332
2333  class verbose_diagnostic_info: public error_info
2334  {
2335    //No public constructors
2336
2337    friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x );
2338  };
2339
2340} }
2341----
2342
2343[.text-right]
2344Reference: <<try_handle_all>> | <<try_handle_some>> | <<try_catch>> | <<error_info>> | <<diagnostic_info>> | <<verbose_diagnostic_info>>
2345====
2346
2347[[pred.hpp]]
2348==== `pred.hpp`
2349
2350====
2351.#include <boost/leaf/pred.hpp>
2352[source,c++]
2353----
2354namespace boost { namespace leaf {
2355
2356  template <class T>
2357  struct is_predicate: std::false_type
2358  {
2359  };
2360
2361  template <class E, auto... V>
2362  struct match
2363  {
2364    E matched;
2365
2366    // Other members not specified
2367  };
2368
2369  template <class E, auto... V>
2370  struct is_predicate<match<E, V...>>: std::true_type
2371  {
2372  };
2373
2374  template <class E, auto... V>
2375  struct match_value
2376  {
2377    E matched;
2378
2379    // Other members not specified
2380  };
2381
2382  template <class E, auto... V>
2383  struct is_predicate<match_value<E, V...>>: std::true_type
2384  {
2385  };
2386
2387  template <auto, auto...>
2388  struct match_member;
2389
2390  template <class E, class T, T E::* P, auto... V>
2391  struct member<P, V...>
2392  {
2393    E matched;
2394
2395    // Other members not specified
2396  };
2397
2398  template <auto P, auto... V>
2399  struct is_predicate<match_member<P, V...>>: std::true_type
2400  {
2401  };
2402
2403  template <class... Ex>
2404  struct catch_
2405  {
2406    std::exception const & matched;
2407
2408    // Other members not specified
2409  };
2410
2411  template <class Ex>
2412  struct catch_<Ex>
2413  {
2414    Ex const & matched;
2415
2416    // Other members not specified
2417  };
2418
2419  template <class... Ex>
2420  struct is_predicate<catch_<Ex...>>: std::true_type
2421  {
2422  };
2423
2424  template <class Pred>
2425  struct if_not
2426  {
2427    E matched;
2428
2429    // Other members not specified
2430  };
2431
2432  template <class Pred>
2433  struct is_predicate<if_not<Pred>>: std::true_type
2434  {
2435  };
2436
2437  template <class ErrorCodeEnum>
2438  bool category( std::error_code const & ec ) noexcept;
2439
2440  template <class Enum, class EnumType = Enum>
2441  struct condition;
2442
2443} }
2444----
2445
2446[.text-right]
2447Reference: <<match>> | <<match_value>> | <<match_member>> | <<catch_>> | <<if_not>> | <<category,`category`>> | <<condition,`condition`>>
2448====
2449
2450[[functions]]
2451== Reference: Functions
2452
2453TIP: The contents of each Reference section are organized alphabetically.
2454
2455'''
2456
2457[[activate_context]]
2458=== `activate_context`
2459
2460.#include <boost/leaf/error.hpp>
2461[source,c++]
2462----
2463namespace boost { namespace leaf {
2464
2465  template <class Ctx>
2466  context_activator<Ctx> activate_context( Ctx & ctx ) noexcept
2467  {
2468    return context_activator<Ctx>(ctx);
2469  }
2470
2471} }
2472----
2473
2474[.text-right]
2475<<context_activator>>
2476
2477.Example:
2478[source,c++]
2479----
2480leaf::context<E1, E2, E3> ctx;
2481
2482{
2483  auto active_context = activate_context(ctx); <1>
2484} <2>
2485----
2486<1> Activate `ctx`.
2487<2> Automatically deactivate `ctx`.
2488
2489'''
2490
2491[[capture]]
2492=== `capture`
2493
2494.#include <boost/leaf/capture.hpp>
2495[source,c++]
2496----
2497namespace boost { namespace leaf {
2498
2499  template <class F, class... A>
2500  decltype(std::declval<F>()(std::forward<A>(std::declval<A>())...))
2501  capture(std::shared_ptr<polymorphic_context> && ctx, F && f, A... a);
2502
2503} }
2504----
2505
2506[.text-right]
2507<<polymorphic_context>>
2508
2509This function can be used to capture error objects stored in a <<context>> in one thread and transport them to a different thread for handling, either in a `<<result,result>><T>` object or in an exception.
2510
2511Returns: :: The same type returned by `F`.
2512
2513Effects: :: Uses an internal <<context_activator>> to <<context::activate>> `*ctx`, then invokes `std::forward<F>(f)(std::forward<A>(a)...)`. Then:
2514+
2515--
2516* If the returned value `r` is not a `result<T>` type (see <<is_result_type>>), it is forwarded to the caller.
2517* Otherwise:
2518** If `!r`, the return value of `capture` is initialized with `ctx`;
2519+
2520NOTE: An object of type `leaf::<<result,result>><T>` can be initialized with a `std::shared_ptr<leaf::polymorphic_context>`.
2521+
2522** otherwise, it is initialized with `r`.
2523--
2524+
2525In case `f` throws, `capture` catches the exception in a `std::exception_ptr`, and throws a different exception of unspecified type that transports both the `std::exception_ptr` as well as `ctx`. This exception type is recognized by <<try_catch>>, which automatically unpacks the original exception and propagates the contents of `*ctx` (presumably, in a different thread).
2526
2527TIP: See also <<tutorial-async>> from the Tutorial.
2528
2529'''
2530
2531[[context_type_from_handlers]]
2532=== `context_type_from_handlers`
2533
2534.#include <boost/leaf/context.hpp>
2535[source,c++]
2536----
2537namespace boost { namespace leaf {
2538
2539  template <class... H>
2540  using context_type_from_handlers = typename <<unspecified>>::type;
2541
2542} }
2543----
2544
2545.Example:
2546[source,c++]
2547----
2548auto error_handlers = std::make_tuple(
2549  [](e_this const & a, e_that const & b)
2550  {
2551    ....
2552  },
2553
2554  [](leaf::diagnostic_info const & info)
2555  {
2556    ....
2557  },
2558  .... );
2559
2560leaf::context_type_from_handlers<decltype(error_handlers)> ctx; <1>
2561----
2562<1> `ctx` will be of type `context<e_this, e_that>`, deduced automatically from the specified error handlers.
2563
2564TIP: Alternatively, a suitable context may be created by calling <<make_context>>, or allocated dynamically by calling <<make_shared_context>>.
2565
2566'''
2567
2568[[current_error]]
2569=== `current_error`
2570
2571.#include <boost/leaf/error.hpp>
2572[source,c++]
2573----
2574namespace boost { namespace leaf {
2575
2576  error_id current_error() noexcept;
2577
2578} }
2579----
2580
2581Returns: :: The `error_id` value returned the last time <<new_error>> was invoked from the calling thread.
2582
2583TIP: See also <<on_error>>.
2584
2585'''
2586
2587[[exception]]
2588=== `exception`
2589
2590[source,c++]
2591.#include <boost/leaf/exception.hpp>
2592----
2593namespace boost { namespace leaf {
2594
2595  template <class Ex, class... E> <1>
2596  <<unspecified>> exception( Ex && ex, E && ... e ) noexcept;
2597
2598  template <class E1, class... E> <2>
2599  <<unspecified>> exception( E1 && e1, E && ... e ) noexcept;
2600
2601  <<unspecified>> exception() noexcept;
2602
2603} }
2604----
2605The `exception` function is overloaded: it can be invoked with no arguments, or else there are two alternatives, selected using `std::enable_if` based on the type of the first argument:
2606
2607<1> Selected if the first argument is an exception object, that is, iff `Ex` derives publicly from `std::exception`. In this case the return value is of unspecified type which derives publicly from `Ex` *and* from class <<error_id>>, such that:
2608* its `Ex` subobject is initialized by `std::forward<Ex>(ex)`;
2609* its `error_id` subobject is initialized by `<<new_error,new_error>>(std::forward<E>(e)...`).
2610<2> Selected otherwise. In this case the return value is of unspecified type which derives publicly from `std::exception` *and* from class `error_id`, such that:
2611** its `std::exception` subobject is default-initialized;
2612** its `error_id` subobject is initialized by `<<new_error,new_error>>(std::forward<E1>(e1), std::forward<E>(e)...`).
2613
2614.Example 1:
2615[source,c++]
2616----
2617struct my_exception: std::exception { };
2618
2619throw leaf::exception(my_exception{}); <1>
2620----
2621<1> Throws an exception of a type that derives from `error_id` and from `my_exception` (because `my_exception` derives from `std::exception`).
2622
2623.Example 2:
2624[source,c++]
2625----
2626enum class my_error { e1=1, e2, e3 }; <1>
2627
2628throw leaf::exception(my_error::e1);
2629----
2630<1> Throws an exception of a type that derives from `error_id` and from `std::exception` (because `my_error` does not derive from `std::exception`).
2631
2632NOTE: To automatically capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` with the returned object, use <<BOOST_LEAF_EXCEPTION>> instead of `leaf::exception`.
2633
2634'''
2635
2636[[exception_to_result]]
2637=== `exception_to_result`
2638
2639[source,c++]
2640.#include <boost/leaf/capture.hpp>
2641----
2642namespace boost { namespace leaf {
2643
2644  template <class... Ex, class F>
2645  <<result<T>-deduced>> exception_to_result( F && f ) noexcept;
2646
2647} }
2648----
2649
2650This function can be used to catch exceptions from a lower-level library and convert them to `<<result,result>><T>`.
2651
2652Returns: :: Where `f` returns a type `T`, `exception_to_result` returns `leaf::result<T>`.
2653
2654Effects: ::
2655
2656. Catches all exceptions, then captures `std::current_exception` in a `std::exception_ptr` object, which is <<tutorial-loading,loaded>> with the returned `result<T>`.
2657. Attempts to convert the caught exception, using `dynamic_cast`, to each type `Ex~i~` in `Ex...`. If the cast to `Ex~i~` succeeds, the `Ex~i~` slice of the caught exception is loaded with the returned `result<T>`.
2658
2659TIP: An error handler that takes an argument of an exception type (that is, of a type that derives from `std::exception`) will work correctly whether the object is thrown as an exception or communicated via <<new_error>> (or converted using `exception_to_result`).
2660
2661.Example:
2662[source,c++]
2663----
2664int compute_answer_throws();
2665
2666//Call compute_answer, convert exceptions to result<int>
2667leaf::result<int> compute_answer()
2668{
2669  return leaf::exception_to_result<ex_type1, ex_type2>(compute_answer_throws());
2670}
2671----
2672
2673At a later time we can invoke <<try_handle_some>> / <<try_handle_all>> as usual, passing handlers that take `ex_type1` or `ex_type2`, for example by reference:
2674
2675[source,c++]
2676----
2677return leaf::try_handle_some(
2678
2679  [] -> leaf::result<void>
2680  {
2681    BOOST_LEAF_AUTO(answer, compute_answer());
2682    //Use answer
2683    ....
2684    return { };
2685  },
2686
2687  [](ex_type1 & ex1)
2688  {
2689    //Handle ex_type1
2690    ....
2691    return { };
2692  },
2693
2694  [](ex_type2 & ex2)
2695  {
2696    //Handle ex_type2
2697    ....
2698    return { };
2699  },
2700
2701  [](std::exception_ptr const & p)
2702  {
2703    //Handle any other exception from compute_answer.
2704    ....
2705    return { };
2706  } );
2707----
2708
2709[.text-right]
2710<<try_handle_some>> | <<result>> | <<BOOST_LEAF_AUTO>>
2711
2712WARNING: When a handler takes an argument of an exception type (that is, a type that derives from `std::exception`), if the object is thrown, the argument will be matched dynamically (using `dynamic_cast`); otherwise (e.g. after being converted by `exception_to_result`) it will be matched based on its static type only (which is the same behavior used for types that do not derive from `std::exception`).
2713
2714TIP: See also <<tutorial-exception_to_result>> from the tutorial.
2715
2716'''
2717
2718[[make_context]]
2719=== `make_context`
2720
2721.#include <boost/leaf/context.hpp>
2722[source,c++]
2723----
2724namespace boost { namespace leaf {
2725
2726  template <class...  H>
2727  context_type_from_handlers<H...> make_context() noexcept
2728  {
2729    return { };
2730  }
2731
2732  template <class...  H>
2733  context_type_from_handlers<H...> make_context( H && ... ) noexcept
2734  {
2735    return { };
2736  }
2737
2738} }
2739----
2740
2741[.text-right]
2742<<context_type_from_handlers>>
2743
2744.Example:
2745[source,c++]
2746----
2747auto ctx = leaf::make_context( <1>
2748  []( e_this ) { .... },
2749  []( e_that ) { .... } );
2750----
2751<1> `decltype(ctx)` is `leaf::context<e_this, e_that>`.
2752
2753'''
2754
2755[[make_shared_context]]
2756=== `make_shared_context`
2757
2758.#include <boost/leaf/context.hpp>
2759[source,c++]
2760----
2761namespace boost { namespace leaf {
2762
2763  template <class...  H>
2764  context_ptr make_shared_context() noexcept
2765  {
2766    return std::make_shared<leaf_detail::polymorphic_context_impl<context_type_from_handlers<H...>>>();
2767  }
2768
2769  template <class...  H>
2770  context_ptr make_shared_context( H && ... ) noexcept
2771  {
2772    return std::make_shared<leaf_detail::polymorphic_context_impl<context_type_from_handlers<H...>>>();
2773  }
2774
2775} }
2776----
2777
2778[.text-right]
2779<<context_type_from_handlers>>
2780
2781TIP: See also <<tutorial-async>> from the tutorial.
2782
2783'''
2784
2785[[new_error]]
2786=== `new_error`
2787
2788.#include <boost/leaf/error.hpp>
2789[source,c++]
2790----
2791namespace boost { namespace leaf {
2792
2793  template <class... Item>
2794  error_id new_error(Item && ... item) noexcept;
2795
2796} }
2797----
2798
2799Requires: :: Each of the `Item...` types must be no-throw movable.
2800
2801Effects: :: As if:
2802+
2803[source,c++]
2804----
2805error_id id = <<generate-new-unique-id>>;
2806return id.load(std::forward<Item>(item)...);
2807----
2808
2809Returns: :: A new `error_id` value, which is unique across the entire program.
2810
2811Ensures: :: `id.value()!=0`, where `id` is the returned `error_id`.
2812
2813NOTE: `new_error` discards error objects which are not used in any active error-handling calling scope.
2814
2815CAUTION: When loaded into a `context`, an error object of a type `E` will overwrite the previously loaded object of type `E`, if any.
2816
2817'''
2818
2819[[on_error]]
2820=== `on_error`
2821
2822.#include <boost/leaf/on_error.hpp>
2823[source,c++]
2824----
2825namespace boost { namespace leaf {
2826
2827  template <class... Item>
2828  <<unspecified-type>> on_error(Item && ... item) noexcept;
2829
2830} }
2831----
2832
2833Requires: :: Each of the `Item...` types must be no-throw movable.
2834
2835Effects: :: All `item...` objects are forwarded and stored, together with the value returned from `std::unhandled_exceptions`, into the returned object of unspecified type, which should be captured by `auto` and kept alive in the calling scope. When that object is destroyed, if an error has occurred since `on_error` was invoked, LEAF will process the stored items to obtain error objects to be associated with the failure.
2836+
2837On error, LEAF first needs to deduce an `error_id` value `err` to associate error objects with. This is done using the following logic:
2838+
2839--
2840* If <<new_error>> was invoked (by the calling thread) since the object returned by `on_error` was created, `err` is initialized with the value returned by <<current_error>>;
2841* Otherwise, if `std::unhandled_exceptions` returns a greater value than it returned during initialization, `err` is initialized with the value returned by <<new_error>>;
2842* Otherwise, the stored `item...` objects are discarded and no further action is taken (no error has occurred).
2843--
2844+
2845Next, LEAF proceeds similarly to:
2846+
2847[source,c++]
2848----
2849err.load(std::forward<Item>(item)...);
2850----
2851+
2852The difference is that unlike <<error_id::load>>, `on_error` will not overwrite any error objects already associated with `err`.
2853
2854TIP: See <<tutorial-on_error>> from the Tutorial.
2855
2856'''
2857
2858[[try_catch]]
2859=== `try_catch`
2860
2861.#include <boost/leaf/handle_errors.hpp>
2862[source,c++]
2863----
2864namespace boost { namespace leaf {
2865
2866  template <class TryBlock, class... H>
2867  typename std::decay<decltype(std::declval<TryBlock>()())>::type
2868  try_catch( TryBlock && try_block, H && ... h );
2869
2870} }
2871----
2872
2873The `try_catch` function works similarly to <<try_handle_some>>, except that it does not use or understand the semantics of `result<T>` types; instead:
2874
2875* It assumes that the `try_block` throws to indicate a failure, in which case `try_catch` will attempt to find a suitable handler among `h...`;
2876* If a suitable handler isn't found, the original exception is re-thrown using `throw;`.
2877
2878TIP: See also Five Minute Introduction <<introduction-eh>>.
2879
2880'''
2881
2882[[try_handle_all]]
2883=== `try_handle_all`
2884
2885.#include <boost/leaf/handle_errors.hpp>
2886[source,c++]
2887----
2888namespace boost { namespace leaf {
2889
2890  template <class TryBlock, class... H>
2891  typename std::decay<decltype(std::declval<TryBlock>()().value())>::type
2892  try_handle_all( TryBlock && try_block, H && ... h );
2893
2894} }
2895----
2896
2897The `try_handle_all` function works similarly to <<try_handle_some>>, except:
2898
2899* In addition, it requires that at least one of  `h...` can be used to handle any error (this requirement is enforced at compile time);
2900* If the `try_block` returns some `result<T>` type, it must be possible to initialize a value of type `T` with the value returned by each of `h...`, and
2901* Because it is required to handle all errors, `try_handle_all` unwraps the `result<T>` object `r` returned by the `try_block`, returning `r.value()` instead of `r`.
2902
2903TIP: See also <<introduction-result,Five Minute Introduction>>.
2904
2905'''
2906
2907[[try_handle_some]]
2908=== `try_handle_some`
2909
2910.#include <boost/leaf/handle_errors.hpp>
2911
2912[source,c++]
2913----
2914namespace boost { namespace leaf {
2915
2916  template <class TryBlock, class... H>
2917  typename std::decay<decltype(std::declval<TryBlock>()())>::type
2918  try_handle_some( TryBlock && try_block, H && ... h );
2919
2920} }
2921----
2922
2923Requires: ::
2924* The `try_block` function may not take any arguments.
2925* The type `R` returned by the `try_block` function must be a `result<T>` type (see <<is_result_type>>). It is valid for the `try_block` to return `leaf::<<result,result>><T>`, however this is not a requirement.
2926* Each of the `h...` functions:
2927** must return a type that can be used to initialize an object of the type `R`; in case R is a `result<void>` (that is, in case of success it does not communicate a value), handlers that return `void` are permitted. If such a handler is selected, the `try_handle_some` return value is initialized by `{}`;
2928** may take any error objects, by value, by (`const`) reference, or as pointer (to `const`);
2929** may take arguments, by value, of any predicate type: <<catch_>>, <<match>>, <<match_value>>, <<match_member>>, <<if_not>>, or of any user-defined predicate type `Pred` for which `<<is_predicate,is_predicate>><Pred>::value` is `true`;
2930** may take an <<error_info>> argument by `const &`;
2931** may take a <<diagnostic_info>> argument by `const &`;
2932** may take a <<verbose_diagnostic_info>> argument by `const &`.
2933
2934Effects: ::
2935
2936* Creates a local `<<context,context>><E...>` object `ctx`, where the `E...` types are automatically deduced from the types of arguments taken by each of `h...`, which guarantees that `ctx` is able to store all of the types required to handle errors.
2937* Invokes the `try_block`:
2938** if the returned object `r` indicates success [.underline]#and# the `try_block` did not throw, `r` is forwarded to the caller.
2939** otherwise, LEAF  considers each of the `h...` handlers, in order, until it finds one that it can supply with arguments using the error objects currently stored in `ctx`, associated with `r.error()`. The first such handler is invoked and its return value is used to initialize the return value of `try_handle_some`, which can indicate success if the handler was able to handle the error, or failure if it was not.
2940+
2941** if `try_handle_some` is unable to find a suitable handler, it returns `r`.
2942
2943NOTE: `try_handle_some` is exception-neutral: it does not throw exceptions, however the `try_block` and any of `h...` are permitted to throw.
2944
2945[[handler_selection_procedure]]
2946Handler Selection Procedure: ::
2947+
2948A handler `h` is suitable to handle the failure reported by `r` iff `try_handle_some` is able to produce values to pass as its arguments, using the error objects currently available in `ctx`, associated with the error ID obtained by calling `r.error()`. As soon as it is determined that an argument value can not be produced, the current handler is dropped and the selection process continues with the next handler, if any.
2949+
2950The return value of `r.error()` must be implicitly convertible to <<error_id>>. Naturally, the `leaf::result` template satisfies this requirement. If an external `result` type is used instead, usually `r.error()` would return a `std::error_code`, which is able to communicate LEAF error IDs; see <<tutorial-interoperability>>.
2951+
2952If `err` is the `error_id` obtained from `r.error()`, each argument `a~i~` taken by the handler currently under consideration is produced as follows:
2953+
2954* If `a~i~` is of type `A~i~`, `A~i~ const&` or `A~i~&`:
2955+
2956--
2957** If an error object of type `A~i~`, associated with `err`, is currently available in `ctx`, `a~i~` is initialized with a reference to that object; otherwise
2958** If `A~i~` derives from `std::exception`, and the `try_block` throws an object `ex` of type that derives from `std::exception`, LEAF obtains `A~i~* p = dynamic_cast<A~i~*>(&ex)`. The handler is dropped if `p` is null, otherwise `a~i~` is initialized with `*p`.
2959** Otherwise the handler is dropped.
2960--
2961+
2962.Example:
2963[source,c++]
2964----
2965....
2966auto r = leaf::try_handle_some(
2967  []() -> leaf::result<int>
2968  {
2969    return f();
2970  },
2971
2972  [](leaf::e_file_name const & fn) <1>
2973  {
2974    std::cerr << "File Name: \"" << fn.value << '"' << std::endl; <2>
2975
2976    return 1;
2977  } );
2978----
2979+
2980[.text-right]
2981<<result>> | <<e_file_name>>
2982+
2983<1> In case the `try_block` indicates a failure, this handler will be selected if `ctx` stores an `e_file_name` associated with the error. Because this is the only supplied handler, if an `e_file_name` is not available, `try_handle_some` will return the `leaf::result<int>` returned by `f`.
2984<2> Print the file name, handle the error.
2985+
2986* If `a~i~` is of type `A~i~` `const*` or `A~i~*`, `try_handle_some` is always able to produce it: first it attempts to produce it as if it is taken by reference; if that fails, rather than dropping the handler, `a~i~` is initialized with `0`.
2987+
2988.Example:
2989[source,c++]
2990----
2991....
2992try_handle_some(
2993  []() -> leaf::result<int>
2994  {
2995    return f();
2996  },
2997
2998  [](leaf::e_file_name const * fn) <1>
2999  {
3000    if( fn ) <2>
3001      std::cerr << "File Name: \"" << fn->value << '"' << std::endl;
3002
3003    return 1;
3004  } );
3005}
3006----
3007+
3008[.text-right]
3009<<result>> | <<e_file_name>>
3010+
3011<1> This handler can be selected to handle any error, because it takes `e_file_name` as a `const *` (and nothing else).
3012<2> If an `e_file_name` is available with the current error, print it.
3013+
3014* If `a~i~` is of a predicate type `Pred` (for which `<<is_predicate,is_predicate>><Pred>::value` is `true`), `E` is deduced as `typename Pred::error_type`, and then:
3015** If `E` is not `void`, and an error object `e` of type `E`, associated with `err`, is not currently stored in `ctx`, the handler is dropped; otherwise the handler is dropped  if the expression `Pred::evaluate(e)` returns `false`.
3016** if `E` is `void`, and a `std::exception` was not caught, the handler is dropped; otherwise the handler is dropped if the expression `Pred::evaluate(e)`, where `e` is of type `std::exception const &`, returns `false`.
3017** To invoke the handler, the `Pred` argument `a~i~` is initialized with `Pred{e}`.
3018+
3019NOTE: See also: <<predicates,Predicates>>.
3020+
3021* If `a~i~` is of type `error_info const &`,  `try_handle_some` is always able to produce it.
3022+
3023.Example:
3024[source,c++]
3025----
3026....
3027try_handle_some(
3028  []
3029  {
3030    return f(); // returns leaf::result<T>
3031  },
3032
3033  [](leaf::error_info const & info) <1>
3034  {
3035    std::cerr << "leaf::error_info:" << std::endl << info; <2>
3036    return info.error(); <3>
3037  } );
3038----
3039+
3040[.text-right]
3041<<result>> | <<error_info>>
3042+
3043<1> This handler matches any error.
3044<2> Print error information.
3045<3> Return the original error, which will be returned out of `try_handle_some`.
3046+
3047* If `a~i~` is of type `diagnostic_info const &`,  `try_handle_some` is always able to produce it.
3048+
3049.Example:
3050[source,c++]
3051----
3052....
3053try_handle_some(
3054  []
3055  {
3056    return f(); // throws
3057  },
3058
3059  [](leaf::diagnostic_info const & info) <1>
3060  {
3061    std::cerr << "leaf::diagnostic_information:" << std::endl << info; <2>
3062    return info.error(); <3>
3063  } );
3064----
3065+
3066[.text-right]
3067<<result>> | <<diagnostic_info>>
3068+
3069<1> This handler matches any error.
3070<2> Print diagnostic information, including limited information about dropped error objects.
3071<3> Return the original error, which will be returned out of `try_handle_some`.
3072+
3073* If `a~i~` is of type `verbose_diagnostic_info const &`,  `try_handle_some` is always able to produce it.
3074+
3075.Example:
3076[source,c++]
3077----
3078....
3079try_handle_some(
3080  []
3081  {
3082    return f(); // throws
3083  },
3084
3085  [](leaf::verbose_diagnostic_info const & info) <1>
3086  {
3087    std::cerr << "leaf::verbose_diagnostic_information:" << std::endl << info; <2>
3088    return info.error(); <3>
3089  } );
3090----
3091+
3092[.text-right]
3093<<result>> | <<verbose_diagnostic_info>>
3094+
3095<1> This handler matches any error.
3096<2> Print verbose diagnostic information, including values of dropped error objects.
3097<3> Return the original error, which will be returned out of `try_handle_some`.
3098
3099[[types]]
3100
3101== Reference: Types
3102
3103TIP: The contents of each Reference section are organized alphabetically.
3104
3105'''
3106
3107[[context]]
3108=== `context`
3109
3110.#include <boost/leaf/context.hpp>
3111[source,c++]
3112----
3113namespace boost { namespace leaf {
3114
3115  template <class... E>
3116  class context
3117  {
3118    context( context const & ) = delete;
3119    context & operator=( context const & ) = delete;
3120
3121  public:
3122
3123    context() noexcept;
3124    context( context && x ) noexcept;
3125    ~context() noexcept;
3126
3127    void activate() noexcept;
3128    void deactivate() noexcept;
3129    bool is_active() const noexcept;
3130
3131    void propagate() noexcept;
3132
3133    void print( std::ostream & os ) const;
3134
3135    template <class R, class... H>
3136    R handle_error( error_id, H && ... ) const;
3137
3138  };
3139
3140  template <class... H>
3141  using context_type_from_handlers = typename <<unspecified>>::type;
3142
3143} }
3144----
3145[.text-right]
3146<<context::context>> | <<context::activate>> | <<context::deactivate>> | <<context::is_active>> | <<context::propagate>> | <<context::print>> | <<context::handle_error>> | <<context_type_from_handlers>>
3147
3148The `context` class template provides storage for each of the specified `E...` types. Typically, `context` objects are not used directly; they're created internally when the <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> functions are invoked, instantiated with types that are automatically deduced from the types of the arguments of the passed handlers.
3149
3150Independently, users can create `context` objects if they need to capture error objects and then transport them, by moving the `context` object itself.
3151
3152Even in that case it is recommended that users do not instantiate the `context` template by explicitly listing the `E...` types they want it to be able to store. Instead, use <<context_type_from_handlers>> or call the <<make_context>> function template, which deduce the correct `E...` types from a captured list of handler function objects.
3153
3154To be able to load up error objects in a `context` object, it must be activated. Activating a `context` object `ctx` binds it to the calling thread, setting thread-local pointers of the stored `E...` types to point to the corresponding storage within `ctx`. It is possible, even likely, to have more than one active `context` in any given thread. In this case, activation/deactivation must happen in a LIFO manner. For this reason, it is best to use a <<context_activator>>, which relies on RAII to activate and deactivate a `context`.
3155
3156When a `context` is deactivated, it detaches from the calling thread, restoring the thread-local pointers to their pre-`activate` values. Typically, at this point the stored error objects, if any, are either discarded (by default) or moved to corresponding storage in other `context` objects active in the calling thread (if available), by calling <<context::propagate>>.
3157
3158While error handling typically uses <<try_handle_some>>, <<try_handle_all>> or <<try_catch>>, it is also possible to handle errors by calling the member function <<context::handle_error>>. It takes an <<error_id>>, and attempts to select an error handler based on the error objects stored in `*this`, associated with the passed `error_id`.
3159
3160TIP: `context` objects can be moved, as long as they aren't active.
3161
3162WARNING: Moving an active `context` results in undefined behavior.
3163
3164'''
3165
3166[[context::context]]
3167==== Constructors
3168
3169.#include <boost/leaf/context.hpp>
3170[source,c++]
3171----
3172namespace boost { namespace leaf {
3173
3174  template <class... E>
3175  context<E...>::context() noexcept;
3176
3177  template <class... E>
3178  context<E...>::context( context && x ) noexcept;
3179
3180} }
3181----
3182
3183The default constructor initializes an empty `context` object: it provides storage for, but does not contain any error objects.
3184
3185The move constructor moves the stored error objects from one `context` to the other.
3186
3187WARNING: Moving an active `context` object results in undefined behavior.
3188
3189'''
3190
3191[[context::activate]]
3192==== `activate`
3193
3194.#include <boost/leaf/context.hpp>
3195[source,c++]
3196----
3197namespace boost { namespace leaf {
3198
3199  template <class... E>
3200  void context<E...>::activate() noexcept;
3201
3202} }
3203----
3204
3205Requires: :: `!<<context::is_active,is_active>>()`.
3206
3207Effects: :: Associates `*this` with the calling thread.
3208
3209Ensures: :: `<<context::is_active,is_active>>()`.
3210
3211When a context is associated with a thread, thread-local pointers are set to point each `E...` type in its store, while the previous value of each such pointer is preserved in the `context` object, so that the effect of `activate` can be undone by calling `deactivate`.
3212
3213When an error object is <<tutorial-loading,loaded>>, it is moved in the last activated (in the calling thread) `context` object that provides storage for its type (note that this may or may not be the last activated `context` object). If no such storage is available, the error object is discarded.
3214
3215'''
3216
3217[[context::deactivate]]
3218==== `deactivate`
3219
3220.#include <boost/leaf/context.hpp>
3221[source,c++]
3222----
3223namespace boost { namespace leaf {
3224
3225  template <class... E>
3226  void context<E...>::deactivate() noexcept;
3227
3228} }
3229----
3230
3231Requires: ::
3232* `<<context::is_active,is_active>>()`;
3233* `*this` must be the last activated `context` object in the calling thread.
3234
3235Effects: :: Un-associates `*this` with the calling thread.
3236
3237Ensures: :: `!<<context::is_active,is_active>>()`.
3238
3239When a context is deactivated, the thread-local pointers that currently point to each individual error object storage in it are restored to their original value prior to calling <<context::activate>>.
3240
3241'''
3242
3243[[context::handle_error]]
3244==== `handle_error`
3245
3246[source,c++]
3247.#include <boost/leaf/handle_errors.hpp>
3248----
3249namespace boost { namespace leaf {
3250
3251  template <class... E>
3252  template <class R, class... H>
3253  R context<E...>::handle_error( error_id err, H && ... h ) const;
3254
3255} }
3256----
3257
3258This function works similarly to <<try_handle_all>>, but rather than calling a `try_block` and obtaining the <<error_id>> from a returned `result` type, it matches error objects (stored in `*this`, associated with `err`) with a suitable error handler from the `h...` pack.
3259
3260NOTE: The caller is required to specify the return type `R`. This is because in general the supplied handlers may return different types (which must all be convertible to `R`).
3261
3262'''
3263
3264[[context::is_active]]
3265==== `is_active`
3266
3267[source,c++]
3268.#include <boost/leaf/context.hpp>
3269----
3270namespace boost { namespace leaf {
3271
3272  template <class... E>
3273  bool context<E...>::is_active() const noexcept;
3274
3275} }
3276----
3277
3278Returns: :: `true` if the `*this` is active in any thread, `false` otherwise.
3279
3280'''
3281
3282[[context::print]]
3283==== `print`
3284
3285.#include <boost/leaf/context.hpp>
3286[source,c++]
3287----
3288namespace boost { namespace leaf {
3289
3290  template <class... E>
3291  void context<E...>::print( std::ostream & os ) const;
3292
3293} }
3294----
3295
3296Effects: :: Prints all error objects currently stored in `*this`, together with the unique error ID each individual error object is associated with.
3297
3298'''
3299
3300[[context::propagate]]
3301==== `propagate`
3302
3303.#include <boost/leaf/context.hpp>
3304[source,c++]
3305----
3306namespace boost { namespace leaf {
3307
3308  template <class... E>
3309  void context<E...>::propagate() noexcept;
3310
3311} }
3312----
3313
3314Requires: ::
3315`!<<context::is_active,is_active>>()`.
3316
3317Effects: ::
3318
3319Each stored error object of some type `E` is moved into another `context` object active in the call stack that provides storage for objects of type `E`, if any, or discarded.
3320
3321'''
3322
3323[[context_activator]]
3324=== `context_activator`
3325.#include <boost/leaf/error.hpp>
3326[source,c++]
3327----
3328namespace boost { namespace leaf {
3329
3330  template <class Ctx>
3331  class context_activator
3332  {
3333    context_activator( context_activator const & ) = delete;
3334    context_activator & operator=( context_activator const & ) = delete;
3335
3336  public:
3337
3338    explicit context_activator( Ctx & ctx ) noexcept;
3339    context_activator( context_activator && ) noexcept;
3340    ~context_activator() noexcept;
3341  };
3342
3343} }
3344----
3345
3346`context_activator` is a simple class that activates and deactivates a <<context>> using RAII:
3347
3348If `<<context::is_active,ctx.is_active>>`() is `true` at the time the `context_activator` is initialized, the constructor and the destructor have no effects. Otherwise:
3349
3350* The constructor stores a reference to `ctx` in `*this` and calls `<<context::activate,ctx.activate>>`().
3351* The destructor:
3352** Has no effects if `ctx.is_active()` is `false` (that is, it is valid to call <<context::deactivate>> manually, before the `context_activator` object expires);
3353** Otherwise, calls `<<context::deactivate,ctx.deactivate>>`() and, if there are new uncaught exceptions since the constructor was called, the destructor calls `<<context::propagate,ctx.propagate>>`().
3354
3355For automatic deduction of `Ctx`, use <<activate_context>>.
3356
3357'''
3358
3359[[diagnostic_info]]
3360=== `diagnostic_info`
3361
3362.#include <boost/leaf/handle_errors.hpp>
3363[source,c++]
3364----
3365namespace boost { namespace leaf {
3366
3367  class diagnostic_info: public error_info
3368  {
3369    //Constructors unspecified
3370
3371    friend std::ostream & operator<<( std::ostream & os, diagnostic_info const & x );
3372  };
3373
3374} }
3375----
3376
3377Handlers passed to <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> may take an argument of type `diagnostic_info const &` if they need to print diagnostic information about the error.
3378
3379The message printed by `operator<<` includes the message printed by `error_info`, followed by basic information about error objects that were communicated to LEAF (to be associated with the error) for which there was no storage available in any active <<context>> (these error objects were discarded by LEAF, because no handler needed them).
3380
3381The additional information is limited to the type name of the first such error object, as well as their total count.
3382
3383[NOTE]
3384--
3385The behavior of `diagnostic_info` (and <<verbose_diagnostic_info>>) is affected by the value of the macro `BOOST_LEAF_DIAGNOSTICS`:
3386
3387* If it is 1 (the default), LEAF produces `diagnostic_info` but only if an active error handling context on the call stack takes an argument of type `diagnostic_info`;
3388* If it is 0, the `diagnostic_info` functionality is stubbed out even for error handling contexts that take an argument of type `diagnostic_info`. This could shave a few cycles off the error path in some programs (but it is probably not worth it).
3389--
3390
3391'''
3392
3393[[error_id]]
3394=== `error_id`
3395
3396.#include <boost/leaf/error.hpp>
3397[source,c++]
3398----
3399namespace boost { namespace leaf {
3400
3401  class error_id
3402  {
3403  public:
3404
3405    error_id() noexcept;
3406
3407    template <class Enum>
3408    result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
3409
3410    error_id( std::error_code const & ec ) noexcept;
3411
3412    int value() const noexcept;
3413    explicit operator bool() const noexcept;
3414
3415    std::error_code to_error_code() const noexcept;
3416
3417    friend bool operator==( error_id a, error_id b ) noexcept;
3418    friend bool operator!=( error_id a, error_id b ) noexcept;
3419    friend bool operator<( error_id a, error_id b ) noexcept;
3420
3421    template <class... Item>
3422    error_id load( Item && ... item ) const noexcept;
3423
3424    friend std::ostream & operator<<( std::ostream & os, error_id x );
3425  };
3426
3427  bool is_error_id( std::error_code const & ec ) noexcept;
3428
3429  template <class... E>
3430  error_id new_error( E && ... e ) noexcept;
3431
3432  error_id current_error() noexcept;
3433
3434} }
3435----
3436
3437[.text-right]
3438<<error_id::error_id>> | <<error_id::value>> | <<error_id::operator_bool>> | <<error_id::to_error_code>> | <<error_id::comparison_operators>> | <<error_id::load>> | <<is_error_id>> | <<new_error>> | <<current_error>>
3439
3440Values of type `error_id` identify a specific occurrence of a failure across the entire program. They can be copied, moved, assigned to, and compared to other `error_id` objects. They're as efficient as an `int`.
3441
3442'''
3443
3444[[error_id::error_id]]
3445==== Constructors
3446
3447.#include <boost/leaf/error.hpp>
3448[source,c++]
3449----
3450namespace boost { namespace leaf {
3451
3452  error_id::error_id() noexcept = default;
3453
3454  template <class Enum>
3455  error_id::error_id( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
3456
3457  error_id::error_id( std::error_code const & ec ) noexcept;
3458
3459} }
3460----
3461
3462A default-initialized `error_id` object does not represent a specific failure. It compares equal to any other default-initialized `error_id` object. All other `error_id` objects identify a specific occurrence of a failure.
3463
3464CAUTION: When using an object of type `error_id` to initialize a `result<T>` object, it will be initialized in error state, even when passing a default-initialized `error_id` value.
3465
3466Converting an `error_id` object to `std::error_code` uses an unspecified `std::error_category` which LEAF recognizes. This allows an `error_id` to be transported through interfaces that work with `std::error_code`. The `std::error_code` constructor allows the original `error_id` to be restored.
3467
3468TIP: To check if a given `std::error_code` is actually carrying an `error_id`, use <<is_error_id>>.
3469
3470Typically, users create new `error_id` objects by invoking <<new_error>>. The constructor that takes `std::error_code`, and the one that takes a type `Enum` for which `std::is_error_code_enum<Enum>::value` is `true`, have the following effects:
3471
3472* If `ec.value()` is `0`, the effect is the same as using the default constructor.
3473* Otherwise, if `<<is_error_id,is_error_id>>(ec)` is `true`, the original `error_id` value is used to initialize `*this`;
3474* Otherwise, `*this` is initialized by the value returned by <<new_error>>, while `ec` is passed to `load`, which enables handlers used with `try_handle_some`, `try_handle_all` or `try_catch` to receive it as an argument of type `std::error_code`.
3475
3476'''
3477
3478[[is_error_id]]
3479==== `is_error_id`
3480
3481.#include <boost/leaf/error.hpp>
3482[source,c++]
3483----
3484namespace boost { namespace leaf {
3485
3486  bool is_error_id( std::error_code const & ec ) noexcept;
3487
3488} }
3489----
3490
3491Returns: :: `true` if `ec` uses the LEAF-specific `std::error_category` that identifies it as carrying an error ID rather than another error code; otherwise returns `false`.
3492
3493'''
3494
3495[[error_id::load]]
3496==== `load`
3497
3498.#include <boost/leaf/error.hpp>
3499[source,c++]
3500----
3501namespace boost { namespace leaf {
3502
3503  template <class... Item>
3504  error_id error_id::load( Item && ... item ) const noexcept;
3505
3506} }
3507----
3508
3509Requires: :: Each of the `Item...` types must be no-throw movable.
3510
3511Effects: ::
3512* If `value()==0`, all of `item...` are discarded and no further action is taken.
3513* Otherwise, what happens with each `item` depends on its type:
3514** If it is a function that takes a single argument of some type `E &`, that function is called with the object of type `E` currently associated with `*this`. If no such object exists, a default-initialized object is associated with `*this` and then passed to the function.
3515** If it is a function that takes no arguments, than function is called to obtain an error object, which is associated with `*this`.
3516** Otherwise, the `item` itself is assumed to be an error object, which is associated with `*this`.
3517
3518Returns: :: `*this`.
3519
3520NOTE: `load` discards error objects which are not used in any active error-handling calling scope.
3521
3522CAUTION: When loaded into a `context`, an error object of a type `E` will overwrite the previously loaded object of type `E`, if any.
3523
3524See also: :: <<tutorial-loading>>.
3525
3526'''
3527
3528[[error_id::comparison_operators]]
3529==== `operator==`, `!=`, `<`
3530
3531.#include <boost/leaf/error.hpp>
3532[source,c++]
3533----
3534namespace boost { namespace leaf {
3535
3536  friend bool operator==( error_id a, error_id b ) noexcept;
3537  friend bool operator!=( error_id a, error_id b ) noexcept;
3538  friend bool operator<( error_id a, error_id b ) noexcept;
3539
3540} }
3541----
3542
3543These functions have the usual semantics, comparing `a.value()` and `b.value()`.
3544
3545NOTE: The exact strict weak ordering implemented by `operator<` is not specified. In particular, if for two `error_id` objects `a` and `b`, `a < b` is true, it does not follow that the failure identified by `a` ocurred earlier than the one identified by `b`.
3546
3547'''
3548
3549[[error_id::operator_bool]]
3550==== `operator bool`
3551
3552.#include <boost/leaf/error.hpp>
3553[source,c++]
3554----
3555namespace boost { namespace leaf {
3556
3557    explicit error_id::operator bool() const noexcept;
3558
3559} }
3560----
3561
3562Effects: :: As if `return value()!=0`.
3563
3564'''
3565
3566[[error_id::to_error_code]]
3567==== `to_error_code`
3568
3569.#include <boost/leaf/error.hpp>
3570[source,c++]
3571----
3572namespace boost { namespace leaf {
3573
3574    std::error_code error_id::to_error_code() const noexcept;
3575
3576} }
3577----
3578
3579Effects: :: Returns a `std::error_code` with the same `value()` as `*this`, using an unspecified `std::error_category`.
3580
3581NOTE: The returned object can be used to initialize an `error_id`, in which case the original `error_id` value will be restored.
3582
3583TIP: Use <<is_error_id>> to check if a given `std::error_code` carries an `error_id`.
3584
3585'''
3586
3587[[error_id::value]]
3588==== `value`
3589
3590.#include <boost/leaf/error.hpp>
3591[source,c++]
3592----
3593namespace boost { namespace leaf {
3594
3595    int error_id::value() const noexcept;
3596
3597} }
3598----
3599
3600Effects: ::
3601* If `*this` was initialized using the default constructor, returns 0.
3602* Otherwise returns an `int` that is guaranteed to not be 0: a program-wide unique identifier of the failure.
3603
3604'''
3605
3606[[error_monitor]]
3607=== `error_monitor`
3608
3609.#include <boost/leaf/on_error.hpp>
3610[source,c++]
3611----
3612namespace boost { namespace leaf {
3613
3614  class error_monitor
3615  {
3616  public:
3617
3618    error_monitor() noexcept;
3619
3620    error_id check() const noexcept;
3621
3622    error_id assigned_error_id( E && ... e ) const noexcept;
3623  };
3624
3625} }
3626----
3627
3628This class helps obtain an <<error_id>> to associate error objects with, when augmenting failures communicated using LEAF through uncooperative APIs that do not use LEAF to report errors (and therefore do not return an `error_id` on error).
3629
3630The common usage of this class is as follows:
3631
3632[source,c++]
3633----
3634error_code compute_value( int * out_value ) noexcept; <1>
3635
3636leaf::error<int> augmenter() noexcept
3637{
3638  leaf::error_monitor cur_err; <2>
3639
3640  int val;
3641  auto ec = compute_value(&val);
3642
3643  if( failure(ec) )
3644    return cur_err.assigned_error_id().load(e1, e2, ...); <3>
3645  else
3646    return val; <4>
3647}
3648----
3649<1> Uncooperative third-party API that does not use LEAF, but may result in calling a user callback that does use LEAF. In case our callback reports a failure, we'll augment it with error objects available in the calling scope, even though `compute_value` can not communicate an <<error_id>>.
3650<2> Initialize an `error_monitor` object.
3651<3> The call to `compute_value` has failed:
3652- If <<new_error>> was invoked (by the calling thread) after the `augment` object was initialized, `assigned_error_id` returns the last `error_id` returned by `new_error`. This would be the case if the failure originates in our callback (invoked internally by `compute_value`).
3653- Else, `assigned_error_id` invokes `new_error` and returns that `error_id`.
3654<4> The call was successful, return the computed value.
3655
3656The `check` function works similarly, but instead of invoking `new_error` it returns a defaul-initialized `error_id`.
3657
3658TIP: See <<tutorial-on_error_in_c_callbacks>>.
3659
3660'''
3661
3662[[e_api_function]]
3663=== `e_api_function`
3664
3665.#include <boost/leaf/common.hpp>
3666[source,c++]
3667----
3668namespace boost { namespace leaf {
3669
3670  struct e_api_function {char const * value;};
3671
3672} }
3673----
3674
3675
3676The `e_api_function` type is designed to capture the name of the API function that failed. For example, if you're reporting an error from `fread`, you could use `leaf::e_api_function {"fread"}`.
3677
3678WARNING: The passed value is stored as a C string (`char const *`), so `value` should only be initialized with a string literal.
3679
3680'''
3681
3682[[e_at_line]]
3683=== `e_at_line`
3684
3685.#include <boost/leaf/common.hpp>
3686[source,c++]
3687----
3688namespace boost { namespace leaf {
3689
3690  struct e_at_line { int value; };
3691
3692} }
3693----
3694
3695`e_at_line` can be used to communicate the line number when reporting errors (for example parse errors) about a text file.
3696
3697'''
3698
3699[[e_errno]]
3700=== `e_errno`
3701
3702.#include <boost/leaf/common.hpp>
3703[source,c++]
3704----
3705namespace boost { namespace leaf {
3706
3707  struct e_errno
3708  {
3709    int value;
3710    friend std::ostream & operator<<( std::ostream & os, e_errno const & err );
3711  };
3712
3713} }
3714----
3715
3716To capture `errno`, use `e_errno`. When printed in automatically-generated diagnostic messages, `e_errno` objects use `strerror` to convert the `errno` code to string.
3717
3718'''
3719
3720[[e_file_name]]
3721=== `e_file_name`
3722
3723.#include <boost/leaf/common.hpp>
3724[source,c++]
3725----
3726namespace boost { namespace leaf {
3727
3728  struct e_file_name { std::string value; };
3729
3730} }
3731----
3732
3733When a file operation fails, you could use `e_file_name` to store the name of the file.
3734
3735TIP: It is probably better to define your own file name wrappers to avoid clashes if different modules all use `leaf::e_file_name`. It is best to use a descriptive name that clarifies what kind of file name it is (e.g. `e_source_file_name`, `e_destination_file_name`), or at least define `e_file_name` in a given module's namespace.
3736
3737'''
3738
3739[[e_LastError]]
3740=== `e_LastError`
3741
3742.#include <boost/leaf/common.hpp>
3743[source,c++]
3744----
3745namespace boost { namespace leaf {
3746
3747  namespace windows
3748  {
3749    struct e_LastError
3750    {
3751      unsigned value;
3752      friend std::ostream & operator<<( std::ostream & os, e_LastError const & err );
3753    };
3754  }
3755
3756} }
3757----
3758
3759`e_LastError` is designed to communicate `GetLastError()` values on Windows.
3760
3761'''
3762
3763[[e_source_location]]
3764=== `e_source_location`
3765
3766.#include <boost/leaf/error.hpp>
3767[source,c++]
3768----
3769namespace boost { namespace leaf {
3770
3771  struct e_source_location
3772  {
3773    char const * const file;
3774    int const line;
3775    char const * const function;
3776
3777    friend std::ostream & operator<<( std::ostream & os, e_source_location const & x );
3778  };
3779
3780} }
3781----
3782
3783The <<BOOST_LEAF_NEW_ERROR>>, <<BOOST_LEAF_EXCEPTION>> and <<BOOST_LEAF_THROW_EXCEPTION>> macros capture `pass:[__FILE__]`, `pass:[__LINE__]` and `pass:[__FUNCTION__]` into a `e_source_location` object.
3784
3785'''
3786
3787[[e_type_info_name]]
3788=== `e_type_info_name`
3789
3790.#include <boost/leaf/common.hpp>
3791[source,c++]
3792----
3793namespace boost { namespace leaf {
3794
3795  struct e_type_info_name { char const * value; };
3796
3797} }
3798----
3799
3800`e_type_info_name` is designed to store the return value of `std::type_info::name`.
3801
3802'''
3803
3804[[error_info]]
3805=== `error_info`
3806
3807.#include <boost/leaf/handle_errors.hpp>
3808[source,c++]
3809----
3810namespace boost { namespace leaf {
3811
3812  class error_info
3813  {
3814    //Constructors unspecified
3815
3816  public:
3817
3818    error_id error() const noexcept;
3819
3820    bool exception_caught() const noexcept;
3821    std::exception const * exception() const noexcept;
3822
3823    friend std::ostream & operator<<( std::ostream & os, error_info const & x );
3824  };
3825
3826} }
3827----
3828
3829Handlers passed to error-handling functions such as <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> may take an argument of type `error_info const &` to receive generic information about the error being handled.
3830
3831The `error` member function returns the program-wide unique <<error_id>> of the error.
3832
3833The `exception_caught` member function returns `true` if the handler that received `*this` is being invoked to handle an exception, `false` otherwise.
3834
3835If handling an exception, the `exception` member function returns a pointer to the `std::exception` subobject of the caught exception, or `0` if that exception could not be converted to `std::exception`.
3836
3837WARNING: It is illegal to call the `exception` member function unless `exception_caught()` is `true`.
3838
3839The `operator<<` overload prints diagnostic information about each error object currently stored in the <<context>> local to the <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> scope that invoked the handler, but only if it is associated with the <<error_id>> returned by `error()`.
3840
3841'''
3842
3843[[polymorphic_context]]
3844=== `polymorphic_context`
3845
3846.#include <boost/leaf/error.hpp>
3847[source,c++]
3848----
3849namespace boost { namespace leaf {
3850
3851  class polymorphic_context
3852  {
3853  protected:
3854
3855    polymorphic_context() noexcept;
3856    ~polymorphic_context() noexcept;
3857
3858  public:
3859
3860    virtual void activate() noexcept = 0;
3861    virtual void deactivate() noexcept = 0;
3862    virtual bool is_active() const noexcept = 0;
3863
3864    virtual void propagate() noexcept = 0;
3865
3866    virtual void print( std::ostream & ) const = 0;
3867  };
3868
3869} }
3870----
3871
3872The `polymorphic_context` class is an abstract base type which can be used to erase the type of the exact instantiation of the <<context>> class template used. See <<make_shared_context>>.
3873
3874'''
3875
3876[[result]]
3877=== `result`
3878
3879.#include <boost/leaf/result.hpp>
3880[source,c++]
3881----
3882namespace boost { namespace leaf {
3883
3884  template <class T>
3885  class result
3886  {
3887  public:
3888
3889    result() noexcept;
3890    result( T && v ) noexcept;
3891    result( T const & v );
3892
3893    template <class U>
3894    result( U &&, <<enabled_if_T_can_be_inited_with_U>> );
3895
3896    result( error_id err ) noexcept;
3897    result( std::shared_ptr<polymorphic_context> && ctx ) noexcept;
3898
3899    template <class Enum>
3900    result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
3901
3902    result( std::error_code const & ec ) noexcept;
3903
3904    result( result && r ) noexcept;
3905
3906    template <class U>
3907    result( result<U> && r ) noexcept;
3908
3909    result & operator=( result && r ) noexcept;
3910
3911    template <class U>
3912    result & operator=( result<U> && r ) noexcept;
3913
3914    explicit operator bool() const noexcept;
3915
3916    T const & value() const;
3917    T & value();
3918
3919    T const & operator*() const;
3920    T & operator*();
3921
3922    T const * operator->() const;
3923    T * operator->();
3924
3925    <<unspecified-type>> error() noexcept;
3926
3927    template <class... Item>
3928    error_id load( Item && ... item ) noexcept;
3929  };
3930
3931  template <>
3932  class result<void>
3933  {
3934  public:
3935
3936    result() noexcept;
3937
3938    result( error_id err ) noexcept;
3939    result( std::shared_ptr<polymorphic_context> && ctx ) noexcept;
3940
3941    template <class Enum>
3942    result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
3943
3944    result( std::error_code const & ec ) noexcept;
3945
3946    result( result && r ) noexcept;
3947
3948    template <class U>
3949    result( result<U> && r ) noexcept;
3950
3951    result & operator=( result && r ) noexcept;
3952
3953    template <class U>
3954    result & operator=( result<U> && r ) noexcept;
3955
3956    explicit operator bool() const noexcept;
3957
3958    void value() const;
3959
3960    <<unspecified-type>> error() noexcept;
3961
3962    template <class... Item>
3963    error_id load( Item && ... item ) noexcept;
3964  };
3965
3966  struct bad_result: std::exception { };
3967
3968} }
3969----
3970[.text-right]
3971<<result::result>> | <<result::operator_eq>> | <<result::operator_bool>> | <<result::value>> | <<result::error>> | <<result::load>>
3972
3973The `result<T>` type can be returned by functions which produce a value of type `T` but may fail doing so.
3974
3975Requires: :: `T` must be movable, and its move constructor may not throw.
3976
3977Invariant: :: A `result<T>` object is in one of three states:
3978* Value state, in which case it contains an object of type `T`, and `<<result::value,value>>`/`<<result::value,operator*>>`/`<<result::value,operatorpass:[->]>>` can be used to access the contained value.
3979* Error state, in which case it contains an error ID, and calling `<<result::value,value>>`/`<<result::value,operator*>>`/`<<result::value,operatorpass:[->]>>` throws `leaf::bad_result`.
3980* Error-capture state, which is the same as the Error state, but in addition to the error ID, it holds a `std::shared_ptr<<<polymorphic_context,polymorphic_context>>>`.
3981
3982`result<T>` objects are nothrow-moveable but are not copyable.
3983
3984'''
3985
3986[[result::result]]
3987==== Constructors
3988
3989--
3990.#include <boost/leaf/result.hpp>
3991[source,c++]
3992----
3993namespace boost { namespace leaf {
3994
3995  template <class T>
3996  result<T>::result() noexcept;
3997
3998  template <class T>
3999  result<T>::result( T && v ) noexcept; <1>
4000
4001  template <class T>
4002  result<T>::result( T const & v ); <1>
4003
4004  template <class U>
4005  result<T>::result( U && u, <<enabled_if_T_can_be_inited_with_U>> ); <2>
4006
4007  template <class T>
4008  result<T>::result( leaf::error_id err ) noexcept;
4009
4010  template <class T>
4011  template <class Enum>
4012  result<T>::result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept;
4013
4014  template <class T>
4015  result<T>::result( std::error_code const & ec ) noexcept;
4016
4017  template <class T>
4018  result<T>::result( std::shared_ptr<polymorphic_context> && ctx ) noexcept;
4019
4020  template <class T>
4021  result<T>::result( result && ) noexcept;
4022
4023  template <class T>
4024  template <class U>
4025  result<T>::result( result<U> && ) noexcept;
4026
4027} }
4028----
4029<1> Not available if `T` is `void`.
4030<2> Available if an object of type `T` can be initialized with `std::forward<U>(u)`. This is to enable e.g. `result<std::string>` to be initialized with a string literal.
4031--
4032
4033Requires: :: `T` must be movable, and its move constructor may not throw; or `void`.
4034
4035Effects: ::
4036
4037Establishes the `result<T>` invariant:
4038+
4039--
4040* To get a `result<T>` in <<result,Value state>>, initialize it with an object of type `T` or use the default constructor.
4041* To get a `result<T>` in <<result,Error state>>, initialize it with:
4042** an <<error_id>> object.
4043+
4044CAUTION: Initializing a `result<T>` with a default-initialized `error_id` object (for which `.value()` returns `0`) will still result in <<result,Error state>>!
4045+
4046** a `std::error_code` object.
4047** an object of type `Enum` for which `std::is_error_code_enum<Enum>::value` is `true`.
4048* To get a `result<T>` in <<result,Error-capture state>>, initialize it with a `std::shared_ptr<<<polymorphic_context,polymorphic_context>>>` (which can be obtained by calling e.g. <<make_shared_context>>).
4049--
4050+
4051When a `result` object is initialized with a `std::error_code` object, it is used to initialize an `error_id` object, then the behavior is the same as if initialized with `error_id`.
4052
4053Throws: ::
4054* Initializing the `result<T>` in Value state may throw, depending on which constructor of `T` is invoked;
4055* Other constructors do not throw.
4056
4057TIP: A `result` that is in value state converts to `true` in boolean contexts. A `result` that is not in value state converts to `false` in boolean contexts.
4058
4059NOTE: `result<T>` objects are nothrow-moveable but are not copyable.
4060
4061'''
4062
4063[[result::error]]
4064==== `error`
4065
4066.#include <boost/leaf/result.hpp>
4067[source,c++]
4068----
4069namespace boost { namespace leaf {
4070
4071  template <class... E>
4072  <<unspecified-type>> result<T>::error() noexcept;
4073
4074} }
4075----
4076
4077Returns: A proxy object of unspecified type, implicitly convertible to any instance of the `result` class template, as well as to <<error_id>>.
4078
4079* If the proxy object is converted to some `result<U>`:
4080** If `*this` is in <<result,Value state>>, returns `result<U>(error_id())`.
4081** Otherwise the state of `*this` is moved into the returned `result<U>`.
4082* If the proxy object is converted to an `error_id`:
4083** If `*this` is in <<result,Value state>>, returns a default-initialized <<error_id>> object.
4084** If `*this` is in <<result,Error-capture state>>, all captured error objects are <<tutorial-loading,loaded>> in the calling thread, and the captured `error_id` value is returned.
4085** If `*this` is in <<result,Error state>>, returns the stored `error_id`.
4086* If the proxy object is not used, the state of `*this` is not modified.
4087
4088WARNING: The returned proxy object refers to `*this`; avoid holding on to it.
4089
4090'''
4091
4092[[result::load]]
4093==== `load`
4094
4095.#include <boost/leaf/result.hpp>
4096[source,c++]
4097----
4098namespace boost { namespace leaf {
4099
4100  template <class T>
4101  template <class... Item>
4102  error_id result<T>::load( Item && ... item ) noexcept;
4103
4104} }
4105----
4106
4107This member function is designed for use in `return` statements in functions that return `result<T>` to forward additional error objects to the caller.
4108
4109Effects: :: As if `error_id(thispass:[->]error()).load(std::forward<Item>(item)...)`.
4110
4111Returns: :: `*this`.
4112
4113'''
4114
4115[[result::operator_eq]]
4116==== `operator=`
4117
4118.#include <boost/leaf/result.hpp>
4119[source,c++]
4120----
4121namespace boost { namespace leaf {
4122
4123  template <class T>
4124  result<T> & result<T>::operator=( result && ) noexcept;
4125
4126  template <class T>
4127  template <class U>
4128  result<T> & result<T>::operator=( result<U> && ) noexcept;
4129
4130} }
4131----
4132
4133Effects: :: Destroys `*this`, then re-initializes it as if using the appropriate `result<T>` constructor. Basic exception-safety guarantee.
4134
4135'''
4136
4137[[result::operator_bool]]
4138==== `operator bool`
4139
4140.#include <boost/leaf/result.hpp>
4141[source,c++]
4142----
4143namespace boost { namespace leaf {
4144
4145  template <class T>
4146  result<T>::operator bool() const noexcept;
4147
4148} }
4149----
4150
4151Returns: :: If `*this` is in <<result,value state>>, returns `true`, otherwise returns `false`.
4152
4153'''
4154
4155[[result::value]]
4156==== `value`, `operator*`, `pass:[->]`
4157
4158.#include <boost/leaf/result.hpp>
4159[source,c++]
4160----
4161namespace boost { namespace leaf {
4162
4163  void result<void>::value() const; <1>
4164
4165  template <class T>
4166  T const & result<T>::value() const; <2>
4167
4168  template <class T>
4169  T & result<T>::value();
4170
4171  template <class T>
4172  T const & result<T>::operator*() const; <2>
4173
4174  template <class T>
4175  T & result<T>::operator*();
4176
4177  template <class T>
4178  T const * result<T>::operator->() const; <2>
4179
4180  template <class T>
4181  T * result<T>::operator->(); <2>
4182
4183  struct bad_result: std::exception { };
4184
4185} }
4186----
4187<1> Only when `T` is `void`.
4188<2> Only when `T` is not `void`.
4189
4190[[result::bad_result]]
4191Effects: :: If `*this` is in <<result,value state>>, returns a reference (or pointer) to the stored value, otherwise throws `bad_result`.
4192
4193'''
4194
4195[[verbose_diagnostic_info]]
4196=== `verbose_diagnostic_info`
4197
4198.#include <boost/leaf/handle_errors.hpp>
4199[source,c++]
4200----
4201namespace boost { namespace leaf {
4202
4203  class verbose_diagnostic_info: public error_info
4204  {
4205    //Constructors unspecified
4206
4207    friend std::ostream & operator<<( std::ostream & os, verbose_diagnostic_info const & x );
4208  };
4209
4210} }
4211----
4212
4213Handlers passed to error-handling functions such as <<try_handle_some>>, <<try_handle_all>> or <<try_catch>> may take an argument of type `verbose_diagnostic_info const &` if they need to print diagnostic information about the error.
4214
4215The message printed by `operator<<` includes the message printed by `error_info`, followed by information about error objects that were communicated to LEAF (to be associated with the error) for which there was no storage available in any active <<context>> (these error objects were discarded by LEAF, because no handler needed them).
4216
4217The additional information includes the types and the values of all such error objects.
4218
4219[NOTE]
4220--
4221The behavior of `verbose_diagnostic_info` (and <<diagnostic_info>>) is affected by the value of the macro `BOOST_LEAF_DIAGNOSTICS`:
4222
4223* If it is 1 (the default), LEAF produces `verbose_diagnostic_info` but only if an active error handling context on the call stack takes an argument of type `verbose_diagnostic_info`;
4224* If it is 0, the `verbose_diagnostic_info` functionality is stubbed out even for error handling contexts that take an argument of type `verbose_diagnostic_info`. This could save some cycles on the error path in some programs (but is probably not worth it).
4225--
4226
4227WARNING: Using `verbose_diagnostic_info` will likely allocate memory dynamically.
4228
4229[[predicates]]
4230== Reference: Predicates
4231
4232A predicate is a special type of error handler argument which enables the <<handler_selection_procedure,handler selection procedure>> to consider the _value_ of available error objects, not only their type; see <<tutorial-predicates>>.
4233
4234The following predicates are available:
4235
4236* <<match>>
4237* <<match_value>>
4238* <<match_member>>
4239* <<catch_>>
4240* <<if_not>>
4241
4242In addition, any user-defined type `Pred` for which `<<is_predicate,is_predicate>><Pred>::value` is `true` is treated as a predicate. In this case, it is required that:
4243
4244* `Pred` defines an accessible member type `error_type` to specify the error object type it requires;
4245* `Pred` defines an accessible static member function `evaluate`, which returns a boolean type, and can be invoked with an object of type `error_type const &`;
4246* A `Pred` instance can be initialized with an object of type `error_type`.
4247
4248When an error handler takes an argument of a predicate type `Pred`, the <<handler_selection_procedure,handler selection procedure>> drops the handler if an error object `e` of type `Pred::error_type` is not available. Otherwise, the handler is dropped if `Pred::evaluate(e)` returns `false`. If the handler is invoked, the `Pred` argument is initialized with `Pred{e}`.
4249
4250NOTE: Predicates are evaluated before the error handler is invoked, and so they may not access dynamic state (of course the error handler itself can access dynamic state, e.g. by means of lambda expression captures).
4251
4252.Example 1:
4253[source,c++]
4254----
4255enum class my_error { e1 = 1, e2, e3 };
4256
4257struct my_pred
4258{
4259  using error_type = my_error; <1>
4260
4261  static bool evaluate(my_error) noexcept; <2>
4262
4263  my_error matched; <3>
4264}
4265
4266namespace boost { namespace leaf {
4267
4268  template <>
4269  struct is_predicate<my_pred>: std::true_type
4270  {
4271  };
4272
4273} }
4274----
4275<1> This predicate requires an error object of type `my_error`.
4276<2> The handler selection procedure will call this function with an object `e` of type `my_error` to evaluate the predicate...
4277<3> ...and if successful, initialize the `my_pred` error handler argument with `my_pred{e}`.
4278
4279.Example 2:
4280[source,c++]
4281----
4282struct my_pred
4283{
4284  using error_type = leaf::e_errno; <1>
4285
4286  static bool evaluate(leaf::e_errno const &) noexcept; <2>
4287
4288  leaf::e_errno const & matched; <3>
4289}
4290
4291namespace boost { namespace leaf {
4292
4293  template <>
4294  struct is_predicate<my_pred>: std::true_type
4295  {
4296  };
4297
4298} }
4299----
4300<1> This predicate requires an error object of type <<e_errno>>.
4301<2> The handler selection procedure will call this function with an object `e` of type `e_errno` to evaluate the predicate...
4302<3> ...and if successful, initialize the `my_pred` error handler argument with `my_pred{e}`.
4303
4304'''
4305
4306[[catch_]]
4307=== `catch_`
4308
4309.#include <boost/leaf/pred.hpp>
4310[source,c++]
4311----
4312namespace boost { namespace leaf {
4313
4314  template <class... Ex>
4315  struct catch_
4316  {
4317    std::exception const & matched;
4318
4319    // Other members not specified
4320  };
4321
4322  template <class Ex>
4323  struct catch_<Ex>
4324  {
4325    Ex const & matched;
4326
4327    // Other members not specified
4328  };
4329
4330  template <class... Ex>
4331  struct is_predicate<catch_<Ex...>>: std::true_type
4332  {
4333  };
4334
4335} }
4336----
4337[.text-right]
4338<<is_predicate>>
4339
4340When an error handler takes an argument of type that is an instance of the `catch_` template, the <<handler_selection_procedure,handler selection procedure>> first checks if a `std::exception` was caught. If not, the handler is dropped. Otherwise, the handler is dropped if the caught `std::exception` can not be `dynamic_cast` to any of the specified types `Ex...`.
4341
4342If the error handler is invoked, the `matched` member can be used to access the exception object.
4343
4344NOTE: See also: <<tutorial-predicates>>.
4345
4346TIP: While `catch_` requires that the caught exception object is of type that derives from `std::exception`, it is not required that the `Ex...` types derive from `std::exception`.
4347
4348.Example 1:
4349[source,c++]
4350----
4351struct ex1: std::exception { };
4352struct ex2: std::exception { };
4353
4354leaf::try_catch(
4355  []
4356  {
4357    return f(); // throws
4358  },
4359
4360  [](leaf::catch_<ex1, ex2> c)
4361  { <1>
4362    assert(dynamic_cast<ex1 const *>(&c.matched) || dynamic_cast<ex2 const *>(&c.matched));
4363    ....
4364  } );
4365----
4366<1> The handler is selected if `f` throws an exception of type `ex1` or `ex2`.
4367
4368.Example 2:
4369[source,c++]
4370----
4371struct ex1: std::exception { };
4372
4373leaf::try_handle_some(
4374  []
4375  {
4376    return f(); // returns leaf::result<T>
4377  },
4378
4379  [](ex1 & e)
4380  { <1>
4381    ....
4382  } );
4383----
4384<1> The handler is selected if `f` throws an exception of type `ex1`. Notice that if we're interested in only one exception type, as long as that type derives from `std::exception`, the use of `catch_` is not required.
4385
4386'''
4387
4388[[if_not]]
4389=== `if_not`
4390
4391.#include <boost/leaf/pred.hpp>
4392[source,c++]
4393----
4394namespace boost { namespace leaf {
4395
4396  template <class P>
4397  struct if_not
4398  {
4399    <<deduced>> matched;
4400
4401    // Other members not specified
4402  };
4403
4404  template <class P>
4405  struct is_predicate<if_not<P>>: std::true_type
4406  {
4407  };
4408
4409} }
4410----
4411[.text-right]
4412<<is_predicate>>
4413
4414
4415When an error handler takes an argument of type `if_not<P>`, where `P` is another predicate type, the <<handler_selection_procedure,handler selection procedure>> first checks if an error object of the type `E` required by `P` is available. If not, the handler is dropped. Otherwise, the handler is dropped if `P` evaluates to `true`.
4416
4417If the error handler is invoked, `matched` can be used to access the matched object `E`.
4418
4419NOTE: See also <<tutorial-predicates>>.
4420
4421.Example:
4422[source,c++]
4423----
4424enum class my_enum { e1, e2, e3 };
4425
4426leaf::try_handle_some(
4427  []
4428  {
4429    return f(); // returns leaf::result<T>
4430  },
4431
4432  []( leaf::if_not<leaf::match<my_enum, my_enum::e1, my_enum::e2>> )
4433  { <1>
4434    ....
4435  } );
4436----
4437
4438[.text-right]
4439<<try_handle_some>> | <<match>>
4440
4441<1> The handler is selected if an object of type `my_enum`, which [.underline]#*does not*# compare equal to `e1` or to `e2`, [.underline]#*is*# associated with the detected error.
4442
4443'''
4444
4445[[match]]
4446=== `match`
4447
4448.#include <boost/leaf/pred.hpp>
4449[source,c++]
4450----
4451namespace boost { namespace leaf {
4452
4453  template <class E, auto... V>
4454  class match
4455  {
4456    <<deduced>> matched;
4457
4458    // Other members not specified
4459  };
4460
4461  template <class E, auto... V>
4462  struct is_predicate<match<E, V...>>: std::true_type
4463  {
4464  };
4465
4466} }
4467----
4468[.text-right]
4469<<is_predicate>>
4470
4471
4472When an error handler takes an argument of type `match<E, V...>`, the <<handler_selection_procedure,handler selection procedure>> first checks if an error object `e` of type `E` is available. If it is not available, the handler is dropped. Otherwise, the handler is dropped if the following condition is not met:
4473
4474[.text-center]
4475`p~1~ || p~2~ || ... p~n~`.
4476
4477Generally, `p~i~` is equivalent to `e == V~i~`, except if `V~i~` is pointer to a function
4478
4479[.text-center]
4480`bool (*V~i~)(T x)`.
4481
4482In this case it is required that `V~i~ != 0` and that `x` can be initialized with `E const &`, and `p~i~` is equivalent to:
4483
4484[.text-center]
4485`V~i~(e)`.
4486
4487[[category]]
4488In particular, it is valid to pass pointer to the function `leaf::category<Enum>` for any `V~i~`, where:
4489
4490[.text-center]
4491`std::is_error_code_enum<Enum>::value || std::is_error_condition_enum<Enum>::value`.
4492
4493In this case, `p~i~` is equivalent to:
4494
4495[.text-center]
4496`&e.category() == &std::error_code(Enum{}).category()`.
4497
4498If the error handler is invoked, `matched` can be used to access `e`.
4499
4500NOTE: See also <<tutorial-predicates>>.
4501
4502.Example 1: Handling of a subset of enum values.
4503[source,c++]
4504----
4505enum class my_enum { e1, e2, e3 };
4506
4507leaf::try_handle_some(
4508  []
4509  {
4510    return f(); // returns leaf::result<T>
4511  },
4512
4513  []( leaf::match<my_enum, my_enum::e1, my_enum::e2> m )
4514  { <1>
4515    static_assert(std::is_same<my_enum, decltype(m.matched)>::value);
4516    assert(m.matched == my_enum::e1 || m.matched == my_enum::e2);
4517    ....
4518  } );
4519----
4520<1> The handler is selected if an object of type `my_enum`, which compares equal to `e1` or to `e2`, is associated with the detected error.
4521
4522.Example 2: Handling of a subset of std::error_code enum values (requires at least {CPP}17, see Example 4 for a {CPP}11-compatible workaround).
4523[source,c++]
4524----
4525enum class my_enum { e1=1, e2, e3 };
4526
4527namespace std
4528{
4529  template <> struct is_error_code_enum<my_enum>: std::true_type { };
4530}
4531
4532leaf::try_handle_some(
4533  []
4534  {
4535    return f(); // returns leaf::result<T>
4536  },
4537
4538  []( leaf::match<std::error_code, my_enum::e1, my_enum::e2> m )
4539  { <1>
4540    static_assert(std::is_same<std::error_code const &, decltype(m.matched)>::value);
4541    assert(m.matched == my_enum::e1 || m.matched == my_enum::e2);
4542    ....
4543  } );
4544----
4545<1> The handler is selected if an object of type `std::error_code`, which compares equal to `e1` or to `e2`, is associated with the detected error.
4546
4547.Example 3: Handling of a specific std::error_code::category (requires at least {CPP}17).
4548[source,c++]
4549----
4550enum class enum_a { a1=1, a2, a3 };
4551enum class enum_b { b1=1, b2, b3 };
4552
4553namespace std
4554{
4555  template <> struct is_error_code_enum<enum_a>: std::true_type { };
4556  template <> struct is_error_code_enum<enum_b>: std::true_type { };
4557}
4558
4559leaf::try_handle_some(
4560  []
4561  {
4562    return f(); // returns leaf::result<T>
4563  },
4564
4565  []( leaf::match<std::error_code, leaf::category<enum_a>, enum_b::b2> m )
4566  { <1>
4567    static_assert(std::is_same<std::error_code const &, decltype(m.matched)>::value);
4568    assert(&m.matched.category() == &std::error_code(enum_{}).category() || m.matched == enum_b::b2);
4569    ....
4570  } );
4571----
4572<1> The handler is selected if an object of type `std::error_code`, which either has the same `std::error_category` as that of `enum_a` or compares equal to `enum_b::b2`, is associated with the detected error.
4573
4574[[condition]]
4575The use of the `leaf::category` template requires automatic deduction of the type of each `V~i~`, which in turn requires {CPP}17 or newer. The same applies to the use of `std::error_code` as `E`, but LEAF provides a compatible {CPP}11 workaround for this case, using the template `condition`. The following is equivalent to Example 2:
4576
4577.Example 4: Handling of a subset of std::error_code enum values using the {CPP}11-compatible API.
4578[source,c++]
4579----
4580enum class my_enum { e1=1, e2, e3 };
4581
4582namespace std
4583{
4584  template <> struct is_error_code_enum<my_enum>: std::true_type { };
4585}
4586
4587leaf::try_handle_some(
4588  []
4589  {
4590    return f(); // returns leaf::result<T>
4591  },
4592
4593  []( leaf::match<leaf::condition<my_enum>, my_enum::e1, my_enum::e2> m )
4594  {
4595    static_assert(std::is_same<std::error_code const &, decltype(m.matched)>::value);
4596    assert(m.matched == my_enum::e1 || m.matched == my_enum::e2);
4597    ....
4598  } );
4599----
4600
4601Instead of a set of values, the `match` template can be given pointers to functions that implement a custom comparison. In the following example, we define a handler which will be selected to handle any error that communicates an object of the user-defined type `severity` with value greater than 4:
4602
4603.Example 5: Handling of failures with severity::value greater than a specified threshold (requires at least {CPP}17).
4604[source,c++]
4605----
4606struct severity { int value; }
4607
4608template <int S>
4609constexpr bool severity_greater_than( severity const & e ) noexcept
4610{
4611  return e.value > S;
4612}
4613
4614leaf::try_handle_some(
4615  []
4616  {
4617    return f(); // returns leaf::result<T>
4618  },
4619
4620  []( leaf::match<severity, severity_greater_than<4>> m )
4621  {
4622    static_assert(std::is_same<severity const &, decltype(m.matched)>::value);
4623    assert(m.matched.value > 4);
4624    ....
4625  } );
4626
4627----
4628
4629'''
4630
4631[[match_member]]
4632=== `match_member`
4633
4634.#include <boost/leaf/pred.hpp>
4635[source,c++]
4636----
4637namespace boost { namespace leaf {
4638
4639  template <auto, auto... V>
4640  struct match_member;
4641
4642  template <class E, class T, T E::* P, auto... V>
4643  struct match_member<P, V...>
4644  {
4645    E const & matched;
4646
4647    // Other members not specified
4648  };
4649
4650  template <auto P, auto... V>
4651  struct is_predicate<match_member<P, V...>>: std::true_type
4652  {
4653  };
4654
4655} }
4656----
4657[.text-right]
4658<<is_predicate>>
4659
4660
4661This predicate is similar to <<match_value>>, but able to bind any accessible data member of `E`; e.g. `match_member<&E::value, V...>` is equivalent to `match_value<E, V...>`.
4662
4663NOTE: See also <<tutorial-predicates>>.
4664
4665WARNING: `match_member` requires at least {CPP}17, whereas `match_value` does not.
4666
4667'''
4668
4669[[match_value]]
4670=== `match_value`
4671
4672.#include <boost/leaf/pred.hpp>
4673[source,c++]
4674----
4675namespace boost { namespace leaf {
4676
4677  template <class E, auto... V>
4678  struct match_value
4679  {
4680    E const & matched;
4681
4682    // Other members not specified
4683  };
4684
4685  template <class E, auto... V>
4686  struct is_predicate<match_value<E, V...>>: std::true_type
4687  {
4688  };
4689
4690} }
4691----
4692[.text-right]
4693<<is_predicate>>
4694
4695
4696This predicate is similar to <<match>>, but where `match` compares the available error object `e` of type `E` to the specified values `V...`, `match_value` works with `e.value`.
4697
4698NOTE: See also <<tutorial-predicates>>.
4699
4700.Example:
4701[source,c++]
4702----
4703struct e_errno { int value; }
4704
4705leaf::try_handle_some(
4706  []
4707  {
4708    return f(); // returns leaf::result<T>
4709  },
4710
4711  []( leaf::match_value<e_errno, ENOENT> m )
4712  { <1>
4713    static_assert(std::is_same<e_errno const &, decltype(m.matched)>::value);
4714    assert(m.matched.value == ENOENT);
4715    ....
4716  } );
4717----
4718<1> The handler is selected if an object of type <<e_errno>>, with `.value` equal to `ENOENT`, is associated with the detected error.
4719
4720[[traits]]
4721== Reference: Traits
4722
4723[[is_predicate]]
4724=== `is_predicate`
4725
4726[source,c++]
4727.#include <boost/leaf/pred.hpp>>
4728----
4729namespace boost { namespace leaf {
4730
4731  template <class T>
4732  struct is_predicate: std::false_type
4733  {
4734  };
4735
4736} }
4737----
4738
4739The `is_predicate` template is used by the <<handler_selection_procedure,handler selection procedure>> to detect predicate types. See <<tutorial-predicates>>.
4740
4741'''
4742
4743[[is_result_type]]
4744=== `is_result_type`
4745
4746[source,c++]
4747.#include <boost/leaf/error.hpp>>
4748----
4749namespace boost { namespace leaf {
4750
4751  template <class R>
4752  struct is_result_type: std::false_type
4753  {
4754  };
4755
4756} }
4757----
4758
4759The error-handling functionality provided by <<try_handle_some>> and <<try_handle_all>> -- including the ability to <<tutorial-loading,load>> error objects of arbitrary types -- is compatible with any external `result<T>` type R, as long as for a given object `r` of type `R`:
4760
4761* If `bool(r)` is `true`, `r` indicates success, in which case it is valid to call `r.value()` to recover the `T` value.
4762* Otherwise `r` indicates a failure, in which case it is valid to call `r.error()`. The returned value is used to initialize an `error_id` (note: `error_id` can be initialized by `std::error_code`).
4763
4764To use an external `result<T>`  type R, you must specialize the `is_result_type` template so that `is_result_type<R>::value` evaluates to `true`.
4765
4766Naturally, the provided `leaf::<<result,result>><T>` class template satisfies these requirements. In addition, it allows error objects to be transported across thread boundaries, using a `std::shared_ptr<<<polymorphic_context,polymorphic_context>>>`.
4767
4768[[macros]]
4769== Reference: Macros
4770
4771TIP: The contents of each Reference section are organized alphabetically.
4772
4773'''
4774
4775[[BOOST_LEAF_ASSIGN]]
4776=== `BOOST_LEAF_ASSIGN`
4777
4778.#include <boost/leaf/error.hpp>
4779[source,c++]
4780----
4781#define BOOST_LEAF_ASSIGN(v, r)\
4782  auto && <<temp>> = r;\
4783  if( !<<temp>> )\
4784    return <<temp>>.error();\
4785  v = std::forward<decltype(<<temp>>)>(<<temp>>).value()
4786----
4787
4788`BOOST_LEAF_ASSIGN` is useful when calling a function that returns `result<T>` (other than `result<void>`), if the desired behavior is to forward any errors to the caller verbatim.
4789
4790In case of success, the result `value()` of type `T` is assigned to the specified variable `v`, which must have been declared prior to invoking `BOOST_LEAF_ASSIGN`. However, it is possible to use `BOOST_LEAF_ASSIGN` to declare a new variable, by passing in `v` its type together with its name, e.g. `BOOST_LEAF_ASSIGN(auto && x, f())` calls `f`, forwards errors to the caller, while capturing successful values in `x`.
4791
4792NOTE: See also <<BOOST_LEAF_AUTO>>.
4793
4794'''
4795
4796[[BOOST_LEAF_AUTO]]
4797=== `BOOST_LEAF_AUTO`
4798
4799.#include <boost/leaf/error.hpp>
4800[source,c++]
4801----
4802#define BOOST_LEAF_AUTO(v, r)\
4803  BOOST_LEAF_ASSIGN(auto v, r)
4804----
4805[.text-right]
4806<<BOOST_LEAF_ASSIGN>>
4807
4808`BOOST_LEAF_AUTO` is useful when calling a function that returns `result<T>` (other than `result<void>`), if the desired behavior is to forward any errors to the caller verbatim.
4809
4810.Example:
4811[source,c++]
4812----
4813leaf::result<int> compute_value();
4814
4815leaf::result<float> add_values()
4816{
4817  BOOST_LEAF_AUTO(v1, compute_value()); <1>
4818  BOOST_LEAF_AUTO(v2, compute_value()); <2>
4819  return v1 + v2;
4820}
4821----
4822<1> Call `compute_value`, bail out on failure, define a local variable `v1` on success.
4823<2> Call `compute_value` again, bail out on failure, define a local variable `v2` on success.
4824
4825Of course, we could write `add_value` without using `BOOST_LEAF_AUTO`. This is equivalent:
4826
4827----
4828leaf::result<float> add_values()
4829{
4830  auto v1 = compute_value();
4831  if( !v1 )
4832    return v1.error();
4833
4834  auto v2 = compute_value();
4835  if( !v2 )
4836    return v2.error();
4837
4838  return v1.value() + v2.value();
4839}
4840----
4841
4842NOTE: See also <<BOOST_LEAF_ASSIGN>>.
4843
4844'''
4845
4846[[BOOST_LEAF_CHECK]]
4847=== `BOOST_LEAF_CHECK`
4848
4849.#include <boost/leaf/error.hpp>
4850[source,c++]
4851----
4852#define BOOST_LEAF_CHECK(r)\
4853  {\
4854    auto && <<temp>> = r;\
4855    if(!<<temp>>)\
4856      return <<temp>>.error();\
4857  }
4858----
4859
4860`BOOST_LEAF_CHECK` is useful when calling a function that returns `result<void>`, if the desired behavior is to forward any errors to the caller verbatim.
4861
4862.Example:
4863[source,c++]
4864----
4865leaf::result<void> send_message( char const * msg );
4866
4867leaf::result<int> compute_value();
4868
4869leaf::result<int> say_hello_and_compute_value()
4870{
4871  BOOST_LEAF_CHECK(send_message("Hello!")); <1>
4872  return compute_value();
4873}
4874----
4875
4876<1> Try to send a message, then compute a value, report errors using BOOST_LEAF_CHECK.
4877
4878Equivalent implementation without `BOOST_LEAF_CHECK`:
4879
4880----
4881leaf::result<float> add_values()
4882{
4883  auto r = send_message("Hello!");
4884  if( !r )
4885    return r.error();
4886
4887  return compute_value();
4888}
4889----
4890
4891'''
4892
4893[[BOOST_LEAF_EXCEPTION]]
4894=== `BOOST_LEAF_EXCEPTION`
4895
4896[source,c++]
4897.#include <boost/leaf/exception.hpp>
4898----
4899#define BOOST_LEAF_EXCEPTION <<voodoo>>
4900----
4901
4902Effects: :: `BOOST_LEAF_EXCEPTION(e...)` is equivalent to `leaf::<<exception,exception>>(e...)`, except the current source location is automatically passed, in a `<<e_source_location>>` object (in addition to all `e...` objects).
4903
4904'''
4905
4906[[BOOST_LEAF_NEW_ERROR]]
4907=== `BOOST_LEAF_NEW_ERROR`
4908
4909.#include <boost/leaf/error.hpp>
4910[source,c++]
4911----
4912#define BOOST_LEAF_NEW_ERROR <<voodoo>>
4913----
4914
4915Effects: :: `BOOST_LEAF_NEW_ERROR(e...)` is equivalent to `leaf::<<new_error,new_error>>(e...)`, except the current source location is automatically passed, in a `<<e_source_location>>` object (in addition to all `e...` objects).
4916
4917'''
4918
4919[[BOOST_LEAF_THROW_EXCEPTION]]
4920=== `BOOST_LEAF_THROW_EXCEPTION`
4921
4922[source,c++]
4923.#include <boost/leaf/exception.hpp>
4924----
4925#define BOOST_LEAF_THROW_EXCEPTION throw BOOST_LEAF_EXCEPTION
4926----
4927
4928Effects: :: Throws the exception object returned by <<BOOST_LEAF_EXCEPTION>>.
4929
4930[[rationale]]
4931== Design
4932
4933=== Rationale
4934
4935Definition: :: Objects that carry information about error conditions are called error objects. For example, objects of type `std::error_code` are error objects.
4936
4937NOTE: The following reasoning is independent of the mechanism used to transport error objects, whether it is exception handling or anything else.
4938
4939Definition: :: Depending on their interaction with error objects, functions can be classified as follows:
4940* *Error-initiating*: functions that initiate error conditions by creating new error objects.
4941* *Error-neutral*: functions that forward to the caller error objects communicated by lower-level functions they call.
4942* *Error-handling*: functions that dispose of error objects they have received, recovering normal program operation.
4943
4944A crucial observation is that _error-initiating_ functions are typically low-level functions that lack any context and can not determine, much less dictate, the correct program behavior in response to the errors they may initiate. Error conditions which (correctly) lead to termination in some programs may (correctly) be ignored in others; yet other programs may recover from them and resume normal operation.
4945
4946The same reasoning applies to _error-neutral_ functions, but in this case there is the additional issue that the errors they need to communicate, in general, are initiated by functions multiple levels removed from them in the call chain, functions which usually are -- and should be treated as -- implementation details. An _error-neutral_ function should not be coupled with error object types communicated by _error-initiating_ functions, for the same reason it should not be coupled with any other aspect of their interface.
4947
4948Finally, _error-handling_ functions, by definition, have the full context they need to deal with at least some, if not all, failures. In their scope it is an absolute necessity that the author knows exactly what information must be communicated by lower level functions in order to recover from each error condition. Specifically, none of this necessary information can be treated as implementation details; in this case, the coupling which is to be avoided in _error-neutral_ functions is in fact desirable.
4949
4950We're now ready to define our
4951
4952Design goals: ::
4953* *Error-initiating* functions should be able to communicate [underline]#all# information available to them that is relevant to the failure being reported.
4954* *Error-neutral* functions should not be coupled with error types communicated by lower-level _error-initiating_ functions. They should be able to augment any failure with additional relevant information available to them.
4955* *Error-handling* functions should be able to access all the information communicated by _error-initiating_ or _error-neutral_ functions that is needed in order to deal with failures.
4956
4957The design goal that _error-neutral_ functions are not coupled with the static type of error objects that pass through them seems to require dynamic polymorphism and therefore dynamic memory allocations (the Boost Exception library meets this design goal at the cost of dynamic memory allocation).
4958
4959As it turns out, dynamic memory allocation is not necessary due to the following
4960
4961Fact: ::
4962* *Error-handling* functions "know" which of the information _error-initiating_ and _error-neutral_ functions are [.underline]#able# to communicate is [.underline]#actually needed# in order to deal with failures in a particular program. Ideally, no resources should be [.line-through]#used# wasted storing or communicating information which is not currently needed to handle errors, [.underline]#even if it is relevant to the failure#.
4963
4964For example, if a library function is able to communicate an error code but the program does not need to know the exact error code, then that information may be ignored at the time the library function attempts to communicate it. On the other hand, if an _error-handling_ function needs that information, the memory needed to store it can be reserved statically in its scope.
4965
4966The LEAF functions <<try_handle_some>>, <<try_handle_all>> and <<try_catch>> implement this idea. Users provide error-handling lambda functions, each taking arguments of the types it needs in order to recover from a particular error condition. LEAF simply provides the space needed to store these types (in the form of a `std::tuple`, using automatic storage duration) until they are passed to a suitable handler.
4967
4968At the time this space is reserved in the scope of an error-handling function, `thread_local` pointers of the required error types are set to point to the corresponding objects within it. Later on, _error-initiating_ or _error-neutral_ functions wanting to communicate an error object of a given type `E` use the corresponding `thread_local` pointer to detect if there is currently storage available for this type:
4969
4970* If the pointer is not null, storage is available and the object is moved into the pointed storage, exactly once -- regardless of how many levels of function calls must unwind before an _error-handling_ function is reached.
4971* If the pointer is null, storage is not available and the error object is discarded, since no error-handling function makes any use of it in this program -- saving resources.
4972
4973This almost works, except we need to make sure that _error-handling_ functions are protected from accessing stale error objects stored in response to previous failures, which would be a serious logic error. To this end, each occurrence of an error is assigned a unique <<error_id>>. Each of the `E...` objects stored in error-handling scopes is assigned an `error_id` as well, permanently associating it with a particular failure.
4974
4975Thus, to handle a failure we simply match the available error objects (associated with its unique `error_id`) with the argument types required by each user-provided error-handling function. In terms of {CPP} exception handling, it is as if we could write something like:
4976
4977[source,c++]
4978----
4979try
4980{
4981  auto r = process_file();
4982
4983  //Success, use r:
4984  ....
4985}
4986
4987catch(file_read_error &, e_file_name const & fn, e_errno const & err)
4988{
4989  std::cerr <<
4990    "Could not read " << fn << ", errno=" << err << std::endl;
4991}
4992
4993catch(file_read_error &, e_errno const & err)
4994{
4995  std::cerr <<
4996    "File read error, errno=" << err << std::endl;
4997}
4998
4999catch(file_read_error &)
5000{
5001  std::cerr << "File read error!" << std::endl;
5002}
5003----
5004
5005Of course this syntax is not valid, so LEAF uses lambda functions to express the same idea:
5006
5007[source,c++]
5008----
5009leaf::try_catch(
5010
5011  []
5012  {
5013    auto r = process_file(); //Throws in case of failure, error objects stored inside the try_catch scope
5014
5015    //Success, use r:
5016    ....
5017  }
5018
5019  [](file_read_error &, e_file_name const & fn, e_errno const & err)
5020  {
5021    std::cerr <<
5022      "Could not read " << fn << ", errno=" << err << std::endl;
5023  },
5024
5025  [](file_read_error &, e_errno const & err)
5026  {
5027    std::cerr <<
5028      "File read error, errno=" << err << std::endl;
5029  },
5030
5031  [](file_read_error &)
5032  {
5033    std::cerr << "File read error!" << std::endl;
5034  } );
5035----
5036
5037[.text-right]
5038<<try_catch>> | <<e_file_name>> | <<e_errno>>
5039
5040Similar syntax works without exception handling as well. Below is the same snippet, written using `<<result,result>><T>`:
5041
5042[source,c++]
5043----
5044return leaf::try_handle_some(
5045
5046  []() -> leaf::result<void>
5047  {
5048    BOOST_LEAF_AUTO(r, process_file()); //In case of errors, error objects are stored inside the try_handle_some scope
5049
5050    //Success, use r:
5051    ....
5052
5053    return { };
5054  }
5055
5056  [](leaf::match<error_enum, file_read_error>, e_file_name const & fn, e_errno const & err)
5057  {
5058    std::cerr <<
5059      "Could not read " << fn << ", errno=" << err << std::endl;
5060  },
5061
5062  [](leaf::match<error_enum, file_read_error>, e_errno const & err)
5063  {
5064    std::cerr <<
5065      "File read error, errno=" << err << std::endl;
5066  },
5067
5068  [](leaf::match<error_enum, file_read_error>)
5069  {
5070    std::cerr << "File read error!" << std::endl;
5071  } );
5072----
5073
5074[.text-right]
5075<<result>> | <<try_handle_some>> | <<match>> | <<e_file_name>> | <<e_errno>>
5076
5077NOTE: Please post questions and feedback on the Boost Developers Mailing List.
5078
5079'''
5080
5081[[exception_specifications]]
5082=== Critique 1: Error Types Do Not Participate in Function Signatures
5083
5084A knee-jerk critique of the LEAF design is that it does not statically enforce that each possible error condition is recognized and handled by the program. One idea I've heard from multiple sources is to add `E...` parameter pack to `result<T>`, essentially turning it into `expected<T,E...>`, so we could write something along these lines:
5085
5086[source,c++]
5087----
5088expected<T, E1, E2, E3> f() noexcept; <1>
5089
5090expected<T, E1, E3> g() noexcept <2>
5091{
5092  if( expected<T, E1, E2, E3> r = f() )
5093  {
5094    return r; //Success, return the T
5095  }
5096  else
5097  {
5098    return r.handle_error<E2>( [] ( .... ) <3>
5099      {
5100        ....
5101      } );
5102  }
5103}
5104----
5105<1> `f` may only return error objects of type `E1`, `E2`, `E3`.
5106<2> `g` narrows that to only `E1` and `E3`.
5107<3> Because `g` may only return error objects of type `E1` and `E3`, it uses `handle_error` to deal with `E2`. In case `r` contains `E1` or `E3`, `handle_error` simply returns `r`, narrowing the error type parameter pack from `E1, E2, E3` down to `E1, E3`. If `r` contains an `E2`, `handle_error` calls the supplied lambda, which is required to return one of `E1`, `E3` (or a valid `T`).
5108
5109The motivation here is to help avoid bugs in functions that handle errors that pop out of `g`: as long as the programmer deals with `E1` and `E3`, he can rest assured that no error is left unhandled.
5110
5111Congratulations, we've just discovered exception specifications. The difference is that exception specifications, before being removed from {CPP}, were enforced dynamically, while this idea is equivalent to statically-enforced exception specifications, like they are in Java.
5112
5113Why not use the equivalent of exception specifications, even if they are enforced statically?
5114
5115"The short answer is that nobody knows how to fix exception specifications in any language, because the dynamic enforcement {CPP} chose has only different (not greater or fewer) problems than the static enforcement Java chose. ... When you go down the Java path, people love exception specifications until they find themselves all too often encouraged, or even forced, to add `throws Exception`, which immediately renders the exception specification entirely meaningless. (Example: Imagine writing a Java generic that manipulates an arbitrary type `T`).footnote:[https://herbsutter.com/2007/01/24/questions-about-exception-specifications/]"
5116-- Herb Sutter
5117
5118Consider again the example above: assuming we don't want important error-related information to be lost, values of type `E1` and/or `E3` must be able to encode any `E2` value dynamically. But like Sutter points out, in generic contexts we don't know what errors may result in calling a user-supplied function. The only way around that is to specify a single type (e.g. `std::error_code`) that can communicate any and all errors, which ultimately defeats the idea of using static type checking to enforce correct error handling.
5119
5120That said, in every program there are certain _error-handling_ functions (e.g. `main`) which are required to handle any error, and it is highly desirable to be able to enforce this requirement at compile-time. In LEAF, the `try_handle_all` function implements this idea: if the user fails to supply at least one handler that will match any error, the result is a compile error. This guarantees that the scope invoking `try_handle_all` is prepared to recover from any failure.
5121
5122'''
5123
5124[[translation]]
5125=== Critique 2: LEAF Does Not Facilitate Mapping Between Different Error Types
5126
5127Most {CPP} programs use multiple C and {CPP} libraries, and each library may provide its own system of error codes. But because it is difficult to define static interfaces that can communicate arbitrary error code types, a popular idea is to map each library-specific error code to a common program-wide enum.
5128
5129For example, if we have --
5130
5131[source,c++,options="nowrap"]
5132----
5133namespace lib_a
5134{
5135  enum error
5136  {
5137    ok,
5138    ec1,
5139    ec2,
5140    ....
5141  };
5142}
5143----
5144
5145[source,c++,options="nowrap"]
5146----
5147namespace lib_b
5148{
5149  enum error
5150  {
5151    ok,
5152    ec1,
5153    ec2,
5154    ....
5155  };
5156}
5157----
5158
5159-- we could define:
5160
5161[source,c++]
5162----
5163namespace program
5164{
5165  enum error
5166  {
5167    ok,
5168    lib_a_ec1,
5169    lib_a_ec2,
5170    ....
5171    lib_b_ec1,
5172    lib_b_ec2,
5173    ....
5174  };
5175}
5176----
5177
5178An error-handling library could provide conversion API that uses the {CPP} static type system to automate the mapping between the different error enums. For example, it may define a class template `result<T,E>` with value-or-error variant semantics, so that:
5179
5180* `lib_a` errors are transported in `result<T,lib_a::error>`,
5181* `lib_b` errors are transported in `result<T,lib_b::error>`,
5182* then both are automatically mapped to `result<T,program::error>` once control reaches the appropriate scope.
5183
5184There are several problems with this idea:
5185
5186* It is prone to errors, both during the initial implementation as well as under maintenance.
5187
5188* It does not compose well. For example, if both of `lib_a` and `lib_b` use `lib_c`, errors that originate in `lib_c` would be obfuscated by the different APIs exposed by each of `lib_a` and `lib_b`.
5189
5190* It presumes that all errors in the program can be specified by exactly one error code, which is false.
5191
5192To elaborate on the last point, consider a program that attempts to read a configuration file from three different locations: in case all of the attempts fail, it should communicate each of the failures. In theory `result<T,E>` handles this case well:
5193
5194[source,c++]
5195----
5196struct attempted_location
5197{
5198  std::string path;
5199  error ec;
5200};
5201
5202struct config_error
5203{
5204  attempted_location current_dir, user_dir, app_dir;
5205};
5206
5207result<config,config_error> read_config();
5208----
5209
5210This looks nice, until we realize what the `config_error` type means for the automatic mapping API we wanted to define: an `enum` can not represent a `struct`. It is a fact that we can not assume that all error conditions can be fully specified by an `enum`; an error handling library must be able to transport arbitrary static types efficiently.
5211
5212[[errors_are_not_implementation_details]]
5213=== Critique 3: LEAF Does Not Treat Low Level Error Types as Implementation Details
5214
5215This critique is a combination of <<exception_specifications,Critique 1>> and <<translation,Critique 2>>, but it deserves special attention. Let's consider this example using LEAF:
5216
5217[source,c++]
5218----
5219leaf::result<std::string> read_line( reader & r );
5220
5221leaf::result<parsed_line> parse_line( std::string const & line );
5222
5223leaf::result<parsed_line> read_and_parse_line( reader & r )
5224{
5225  BOOST_LEAF_AUTO(line, read_line(r)); <1>
5226  BOOST_LEAF_AUTO(parsed, parse_line(line)); <2>
5227  return parsed;
5228}
5229----
5230[.text-right]
5231<<result>> | <<BOOST_LEAF_AUTO>>
5232
5233<1> Read a line, forward errors to the caller.
5234<2> Parse the line, forward errors to the caller.
5235
5236The objection is that LEAF will forward verbatim the errors that are detected in `read_line` or `parse_line` to the caller of  `read_and_parse_line`. The premise of this objection is that such low-level errors are implementation details and should be treated as such. Under this premise, `read_and_parse_line` should act as a translator of sorts, in both directions:
5237
5238* When called, it should translate its own arguments to call `read_line` and `parse_line`;
5239* If an error is detected, it should translate the errors from the error types returned by `read_line` and `parse_line` to a higher-level type.
5240
5241The motivation is to isolate the caller of `read_and_parse_line` from its implementation details `read_line` and `parse_line`.
5242
5243There are two possible ways to implement this translation:
5244
5245*1)* `read_and_parse_line` understands the semantics of *all possible failures* that may be reported by both `read_line` and `parse_line`, implementing a non-trivial mapping which both _erases_ information that is considered not relevant to its caller, as well as encodes _different_ semantics in the error it reports. In this case `read_and_parse_line` assumes full responsibility for describing precisely what went wrong, using its own type specifically designed for the job.
5246
5247*2)* `read_and_parse_line` returns an error object that essentially indicates which of the two inner functions failed, and also transports the original error object without understanding its semantics and without any loss of information, wrapping it in a new error type.
5248
5249The problem with *1)* is that typically the caller of `read_and_parse_line` is not going to handle the error, but it does need to forward it to its caller. In our attempt to protect the *one* error-handling function from "implementation details", we've coupled the interface of *all* intermediate error-neutral functions with the static types of errors they do not understand and do not handle.
5250
5251Consider the case where `read_line` communicates `errno` in its errors. What is `read_and_parse_line` supposed to do with e.g. `EACCESS`? Turn it into `READ_AND_PARSE_LINE_EACCESS`? To what end, other than to obfuscate the original (already complex and platform-specific) semantics of `errno`?
5252
5253And what if the call to `read` is polymorphic, which is also typical? What if it involves a user-supplied function object? What kinds of errors does it return and why should `read_and_parse_line` care?
5254
5255Therefore, we're left with *2)*. There's almost nothing wrong with this option, since it passes any and all error-related information from lower level functions without any loss. However, using a wrapper type to grant (presumably dynamic) access to any lower-level error type it may be transporting is cumbersome and (like Niall Douglas <<interoperability,explains>>) in general probably requires dynamic allocations. It is better to use independent error types that communicate the additional information not available in the original error object, while error handlers rely on LEAF to provide efficient access to any and all low-level error types, as needed.
5256
5257== Alternatives to LEAF
5258
5259* https://www.boost.org/doc/libs/release/libs/exception/doc/boost-exception.html[Boost Exception]
5260* https://ned14.github.io/outcome[Boost Outcome]
5261* https://github.com/TartanLlama/expected[`tl::expected`]
5262
5263Below we offer a comparison of Boost LEAF to Boost Exception and to Boost Outcome.
5264
5265[[boost_exception]]
5266=== Comparison to Boost Exception
5267
5268While LEAF can be used without exception handling, in the use case when errors are communicated by throwing exceptions, it can be viewed as a better, more efficient alternative to Boost Exception. LEAF has the following advantages over Boost Exception:
5269
5270* LEAF does not allocate memory dynamically;
5271* LEAF does not waste system resources communicating error objects not used by specific error handling functions;
5272* LEAF does not store the error objects in the exception object, and therefore it is able to augment exceptions thrown by external libraries (Boost Exception can only augment exceptions of types that derive from `boost::exception`).
5273
5274The following tables outline the differences between the two libraries which should be considered when code that uses Boost Exception is refactored to use LEAF instead.
5275
5276NOTE: It is possible to access Boost Exception error information using the LEAF error handling interface. See <<tutorial-boost_exception_integration>>.
5277
5278.Defining a custom type for transporting values of type T
5279[cols="1a,1a",options="header",stripes=none]
5280|====
5281| Boost Exception | LEAF
5282|
5283[source,c++,options="nowrap"]
5284----
5285typedef error_info<struct my_info_,T> my_info;
5286----
5287[.text-right]
5288https://www.boost.org/doc/libs/release/libs/exception/doc/error_info.html[`boost::error_info`]
5289|
5290[source,c++,options="nowrap"]
5291----
5292struct my_info { T value; };
5293----
5294|====
5295
5296.Passing arbitrary info at the point of the throw
5297[cols="1a,1a",options="header",stripes=none]
5298|====
5299| Boost Exception | LEAF
5300|
5301[source,c++,options="nowrap"]
5302----
5303throw my_exception() <<
5304  my_info(x) <<
5305  my_info(y);
5306----
5307[.text-right]
5308https://www.boost.org/doc/libs/release/libs/exception/doc/exception_operator_shl.html[`operator<<`]
5309|
5310[source,c++,options="nowrap"]
5311----
5312throw leaf::exception( my_exception(),
5313  my_info{x},
5314  my_info{y} );
5315----
5316[.text-right]
5317<<exception>>
5318|====
5319
5320.Augmenting exceptions in error-neutral contexts
5321[cols="1a,1a",options="header",stripes=none]
5322|====
5323| Boost Exception | LEAF
5324|
5325[source,c++,options="nowrap"]
5326----
5327try
5328{
5329  f();
5330}
5331catch( boost::exception & e )
5332{
5333  e << my_info(x);
5334  throw;
5335}
5336----
5337[.text-right]
5338https://www.boost.org/doc/libs/release/libs/exception/doc/exception.html[`boost::exception`] \| https://www.boost.org/doc/libs/release/libs/exception/doc/exception_operator_shl.html[`operator<<`]
5339|
5340[source,c++,options="nowrap"]
5341----
5342auto load = leaf::on_error( my_info{x} );
5343
5344f();
5345----
5346[.text-right]
5347<<on_error>>
5348|====
5349
5350.Obtaining arbitrary info at the point of the catch
5351[cols="1a,1a",options="header",stripes=none]
5352|====
5353| Boost Exception | LEAF
5354|
5355[source,c++,options="nowrap"]
5356----
5357try
5358{
5359  f();
5360}
5361catch( my_exception & e )
5362{
5363  if( T * v = get_error_info<my_info>(e) )
5364  {
5365    //my_info is available in e.
5366  }
5367}
5368----
5369[.text-right]
5370https://www.boost.org/doc/libs/release/libs/exception/doc/get_error_info.html[`boost::get_error_info`]
5371|
5372[source,c++,options="nowrap"]
5373----
5374leaf::try_catch(
5375  []
5376  {
5377    f(); // throws
5378  }
5379  [](my_exception &, my_info const & x)
5380  {
5381    //my_info is available with
5382    //the caught exception.
5383  } );
5384----
5385[.text-right]
5386<<try_catch>>
5387|====
5388
5389.Transporting of error objects
5390[cols="1a,1a",options="header",stripes=none]
5391|====
5392| Boost Exception | LEAF
5393| All supplied https://www.boost.org/doc/libs/release/libs/exception/doc/error_info.html[`boost::error_info`] objects are allocated dynamically and stored in the https://www.boost.org/doc/libs/release/libs/exception/doc/exception.html[`boost::exception`] subobject of exception objects.
5394|  User-defined error objects are stored statically in the scope of <<try_catch>>, but only if their types are needed to handle errors; otherwise they are discarded.
5395|====
5396
5397.Transporting of error objects across thread boundaries
5398[cols="1a,1a",options="header",stripes=none]
5399|====
5400| Boost Exception | LEAF
5401| https://www.boost.org/doc/libs/release/libs/exception/doc/exception_ptr.html[`boost::exception_ptr`] automatically captures https://www.boost.org/doc/libs/release/libs/exception/doc/error_info.html[`boost::error_info`] objects stored in a `boost::exception` and can transport them across thread boundaries.
5402| Transporting error objects across thread boundaries requires the use of <<capture>>.
5403|====
5404
5405.Printing of error objects in automatically-generated diagnostic information messages
5406[cols="1a,1a",options="header",stripes=none]
5407|====
5408| Boost Exception | LEAF
5409| `boost::error_info` types may define conversion to `std::string` by providing `to_string` overloads *or* by overloading `operator<<` for `std::ostream`.
5410| LEAF does not use `to_string`. Error types may define `operator<<` overloads for `std::ostream`.
5411|====
5412
5413[WARNING]
5414====
5415The fact that Boost Exception stores all supplied `boost::error_info` objects -- while LEAF discards them if they aren't needed -- affects the completeness of the message we get when we print `leaf::<<diagnostic_info,diagnostic_info>>` objects, compared to the string returned by https://www.boost.org/doc/libs/release/libs/exception/doc/diagnostic_information.html[`boost::diagnostic_information`].
5416
5417If the user requires a complete diagnostic message, the solution is to use `leaf::<<verbose_diagnostic_info,verbose_diagnostic_info>>`. In this case, before unused error objects are discarded by LEAF, they are converted to string and printed. Note that this allocates memory dynamically.
5418====
5419
5420'''
5421
5422[[boost_outcome]]
5423=== Comparison to Boost Outcome
5424
5425==== Design Differences
5426
5427Like LEAF, the https://ned14.github.io/outcome[Boost Outcome] library is designed to work in low latency environments. It provides two class templates, `result<>` and `outcome<>`:
5428
5429* `result<T,EC,NVP>` can be used as the return type in `noexcept` functions which may fail, where `T` specifies the type of the return value in case of success, while `EC` is an "error code" type. Semantically, `result<T,EC>` is similar to `std::variant<T,EC>`. Naturally, `EC` defaults to `std::error_code`.
5430* `outcome<T,EC,EP,NVP>` is similar to `result<>`, but in case of failure, in addition to the "error code" type `EC` it can hold a "pointer" object of type `EP`, which defaults to `std::exception_ptr`.
5431
5432NOTE: `NVP` is a policy type used to customize the behavior of `.value()` when the `result<>` or the `outcome<>` object contains an error.
5433
5434The idea is to use `result<>` to communicate failures which can be fully specified by an "error code", and `outcome<>` to communicate failures that require additional information.
5435
5436Another way to describe this design is that `result<>` is used when it suffices to return an error object of some static type `EC`, while `outcome<>` can also transport a polymorphic error object, using the pointer type `EP`.
5437
5438NOTE: In the default configuration of `outcome<T>` the additional information -- or the additional polymorphic object -- is an exception object held by `std::exception_ptr`. This targets the use case when an exception thrown by a lower-level library function needs to be transported through some intermediate contexts that are not exception-safe, to a higher-level context able to handle it. LEAF directly supports this use as well, see <<exception_to_result>>.
5439
5440Similar reasoning drives the design of LEAF as well. The difference is that while both libraries recognize the need to transport "something else" in addition to an "error code", LEAF provides an efficient solution to this problem, while Outcome shifts this burden to the user.
5441
5442The `leaf::result<>` template deletes both `EC` and `EP`, which decouples it from the type of the error objects that are transported in case of a failure. This enables lower-level functions to freely communicate anything and everything they "know" about the failure: error code, even multiple error codes, file names, URLs, port numbers, etc. At the same time, the higher-level error-handling functions control which of this information is needed in a specific client program and which is not. This is ideal, because:
5443
5444* Authors of lower-level library functions lack context to determine which of the information that is both relevant to the error _and_ naturally available to them needs to be communicated in order for a particular client program to recover from that error;
5445* Authors of higher-level error-handling functions can easily and confidently make this determination, which they communicate naturally to LEAF, by simply writing the different error handlers. LEAF will transport the needed error objects while discarding the ones handlers don't care to use, saving resources.
5446
5447TIP: The LEAF examples include an adaptation of the program from the https://ned14.github.io/outcome/tutorial/essential/result/[Boost Outcome `result<>` tutorial]. You can https://github.com/boostorg/leaf/blob/master/examples/print_half.cpp?ts=4[view it on GitHub].
5448
5449NOTE: Programs using LEAF for error-handling are not required to use `leaf::result<T>`; for example, it is possible to use `outcome::result<T>` with LEAF.
5450
5451[[interoperability]]
5452==== The Interoperability Problem
5453
5454The Boost Outcome documentation discusses the important problem of bringing together multiple libraries -- each using its own error reporting mechanism -- and incorporating them in a robust error handling infrastructure in a client program.
5455
5456Users are advised that whenever possible they should use a common error handling system throughout their entire codebase, but because this is not practical, both the `result<>` and the `outcome<>` templates can carry user-defined "payloads".
5457
5458The following analysis is from the Boost Outcome documentation:
5459====
5460If library A uses `result<T, libraryA::failure_info>`, and library B uses `result<T, libraryB::error_info>` and so on, there becomes a problem for the application writer who is bringing in these third party dependencies and tying them together into an application. As a general rule, each third party library author will not have built in explicit interoperation support for unknown other third party libraries. The problem therefore lands with the application writer.
5461
5462The application writer has one of three choices:
5463
5464. In the application, the form of result used is `result<T, std::variant<E1, E2, ...>>` where `E1, E2 …` are the failure types for every third party library in use in the application. This has the advantage of preserving the original information exactly, but comes with a certain amount of use inconvenience and maybe excessive coupling between high level layers and implementation detail.
5465
5466. One can translate/map the third party’s failure type into the application’s failure type at the point of the failure exiting the third party library and entering the application. One might do this, say, with a C preprocessor macro wrapping every invocation of the third party API from the application. This approach may lose the original failure detail, or mis-map under certain circumstances if the mapping between the two systems is not one-one.
5467
5468. One can type erase the third party’s failure type into some application failure type, which can later be reconstituted if necessary. *This is the cleanest solution with the least coupling issues and no problems with mis-mapping*, but it almost certainly requires the use of `malloc` which the previous two did not.
5469====
5470
5471The analysis above (emphasis added) is clear and precise, but LEAF and Boost Outcome tackle the interoperability problem differently:
5472
5473* The Boost Outcome design asserts that the "cleanest" solution based on type-erasure is suboptimal ("almost certainly requires the use of `malloc`pass:[]"), and instead provides a system for injecting custom converters into the `outcome::convert` namespace, used to translate between library-specific and program-wide error types, even though this approach "may lose the original failure detail".
5474
5475* The LEAF design asserts that coupling the signatures of <<rationale,error-neutral>> functions with the static types of the error objects they need to forward to the caller <<translation,does not scale>>, and instead transports error objects directly to error-handling scopes where they are stored statically, effectively implementing the third choice outlined above (without the use of `malloc`).
5476
5477Further, consider that Outcome aims to hopefully become _the_ one error-handling API all libraries would use, and in theory everyone would benefit from uniformity and standardization. But the reality is that this is wishful thinking. In fact, that reality is reflected in the design of `outcome::result<>`, in its lack of commitment to using `std::error_code` for its intended purpose: to be _the_ standard type for transporting error codes. The fact is that `std::error_code` became _yet another_ error code type programmers need to understand and support.
5478
5479In contrast, the design of LEAF acknowledges that {CPP} programmers don't even agree on what a string is. If your project uses 10 different libraries, this probably means 15 different ways to report errors, sometimes across uncooperative interfaces (e.g. C APIs). LEAF helps you get the job done.
5480
5481== Benchmark
5482
5483https://github.com/boostorg/leaf/blob/master/benchmark/benchmark.md[This benchmark] compares the performance of LEAF, Boost Outcome and `tl::expected`.
5484
5485== Running the Unit Tests
5486
5487The unit tests can be run with https://mesonbuild.com[Meson Build] or with Boost Build. To run the unit tests:
5488
5489=== Meson Build
5490
5491Clone LEAF into any local directory and execute:
5492
5493[source,sh]
5494----
5495cd leaf
5496meson bld/debug
5497cd bld/debug
5498meson test
5499----
5500
5501See `meson_options.txt` found in the root directory for available build options.
5502
5503=== Boost Build
5504
5505Assuming the current working directory is `<boostroot>/libs/leaf`:
5506
5507[source,sh]
5508----
5509../../b2 test
5510----
5511
5512== Configuration Macros
5513
5514The following configuration macros are recognized:
5515
5516* `BOOST_LEAF_DIAGNOSTICS`: Defining this macro to `0` stubs out both <<diagnostic_info>> and <<verbose_diagnostic_info>>, which could improve the performance of the error path in some programs (if the macro is left undefined, LEAF defines it as `1`).
5517* `BOOST_LEAF_NO_EXCEPTIONS`: Disables all exception handling support. If left undefined, LEAF defines it based on the compiler configuration (e.g. `-fno-exceptions`).
5518* `BOOST_LEAF_NO_THREADS`: Disable all multi-thread support.
5519
5520== Acknowledgements
5521
5522Special thanks to Peter Dimov and Sorin Fetche.
5523
5524Ivo Belchev, Sean Palmer, Jason King, Vinnie Falco, Glen Fernandes, Nir Friedman, Augustín Bergé -- thanks for the valuable feedback.
5525
5526Documentation rendered by https://asciidoctor.org/[Asciidoctor] with https://github.com/zajo/asciidoctor_skin[these customizations].
5527