[#MRDOCS_TRY] = MRDOCS_TRY :mrdocs: Evaluate an expected‐like expression and propagate its error. == Synopsis Declared in `<mrdocs/Support/Error/Expected.hpp>` [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- #define MRDOCS_TRY(...) ---- == Description The expression is evaluated once and stored in a hidden local. If it failed, the enclosing function returns `Unexpected` with its error. Otherwise the value is moved into the variable you name, if any. The enclosing function must return an xref:mrdocs/Expected.adoc[`mrdocs::Expected`] (or anything that can be constructed from `Unexpected`). A value counts as failed when it is: * an xref:mrdocs/Expected.adoc[`mrdocs::Expected`] without a value (its error is propagated), * a failed xref:mrdocs/Error.adoc[`mrdocs::Error`] (the error itself is propagated), * anything with an `empty()` member that returns `true` (the error is `Error("Empty value")`), * anything that converts to `false` (the error is `Error("Invalid value")`). Values that match none of these never count as failed. The macro takes one, two, or three arguments. The equivalent code below is simplified: the hidden local is really named `expected_result_<line>`, and `failed`/`error` are `::mrdocs::detail::failed` and `::mrdocs::detail::error`. With one argument, it checks the expression and discards the value: [,cpp] ---- MRDOCS_TRY(checkReadable(path)); ---- is equivalent to: [,cpp] ---- auto tmp = checkReadable(path); if (failed(tmp)) { return Unexpected(error(tmp)); } ---- This form also works on values that aren't an xref:mrdocs/Expected.adoc[`mrdocs::Expected`]. An empty container fails with `"Empty value"`, and a value that converts to `false` fails with `"Invalid value"`: [,cpp] ---- MRDOCS_TRY(findInputs(dir)); // a container MRDOCS_TRY(isConfigured()); // a bool ---- With two arguments, it declares a variable and moves the value into it: [,cpp] ---- MRDOCS_TRY(std::string text, readFile(path)); ---- is equivalent to: [,cpp] ---- auto tmp = readFile(path); if (failed(tmp)) { return Unexpected(error(tmp)); } std::string text = *std::move(tmp); ---- The first argument can also name a variable that already exists, in which case the value is assigned to it: [,cpp] ---- MRDOCS_TRY(text, trim(text)); ---- is equivalent to: [,cpp] ---- auto tmp = trim(text); if (failed(tmp)) { return Unexpected(error(tmp)); } text = *std::move(tmp); ---- With three arguments, it works like the two‐argument form but replaces the error with your own message: [,cpp] ---- MRDOCS_TRY(int n, parseInt(text), "count isn't a number"); ---- is equivalent to: [,cpp] ---- auto tmp = parseInt(text); if (failed(tmp)) { return Unexpected(Error("count isn't a number")); } int n = *std::move(tmp); ---- Things to keep in mind: * It expands to several statements, so use it only in a function body, and put braces around it when it's the body of an `if`, `else`, or loop. * The hidden local is named after `*LINE*`, so you can't use it twice on the same line (or twice inside one line of another macro). * Like any statement, it needs a trailing semicolon. * The expression is stored with `auto`, so an lvalue argument is copied, not moved. * The two‐ and three‐argument forms read the value with `*`, so they need a type with a value to dereference, such as `Expected<T>`. Use the one‐argument form for `Expected<void>`, xref:mrdocs/Error.adoc[`mrdocs::Error`], containers, and `bool`. * The three‐argument form discards the original error and returns `Error(msg)`, so `msg` can be anything xref:mrdocs/Error.adoc[`mrdocs::Error`] is constructible from, such as a non‐empty string. It's only evaluated on failure. `Error` is named unqualified, so it needs `mrdocs::Error` to be visible (inside namespace `mrdocs`, or after `using mrdocs::Error;`). To unpack the value into several names, use `MRDOCS_TRY_BIND`. [.small]#Created with https://www.mrdocs.com[MrDocs]#