MRDOCS_TRY

Evaluate an expected‐like expression and propagate its error.

Synopsis

#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 mrdocs::Expected (or anything that can be constructed from Unexpected).

A value counts as failed when it is:

  • an mrdocs::Expected without a value (its error is propagated),

  • a failed 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:

MRDOCS_TRY(checkReadable(path));

is equivalent to:

auto tmp = checkReadable(path);
if (failed(tmp)) {
    return Unexpected(error(tmp));
}

This form also works on values that aren't an mrdocs::Expected. An empty container fails with "Empty value", and a value that converts to false fails with "Invalid value":

MRDOCS_TRY(findInputs(dir));  // a container
MRDOCS_TRY(isConfigured());   // a bool

With two arguments, it declares a variable and moves the value into it:

MRDOCS_TRY(std::string text, readFile(path));

is equivalent to:

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:

MRDOCS_TRY(text, trim(text));

is equivalent to:

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:

MRDOCS_TRY(int n, parseInt(text), "count isn't a number");

is equivalent to:

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>, mrdocs::Error, containers, and bool.

  • The three‐argument form discards the original error and returns Error(msg), so msg can be anything 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.

Created with MrDocs