Nehmen Sie ein Beispiel mit optionalen Funktionen mit der Einschränkung, dass foo() vor bar() aufgerufen werden muss: Nehmen wir als motivierendes Beispiel an, wir schreiben einen Parser und beide foo und bar verbrauchen etwas Token aus unserer Liste der zu analysierenden Token.
Code: Select all
std::optional foo();
std::optional bar();
„Die übliche Vorgehensweise“
Code: Select all
std::optional baz() {
std::optional maybe_foo = foo();
if(!maybe_foo.has_value()) {
return std::nullopt;
}
std::optional maybe_bar = bar();
if(!maybe_bar.has_value()) {
return std::nullopt;
}
return maybe_foo.value() * maybe_bar.value();
}
Code: Select all
std::optional baz() {
return foo().and_then([](int foo_res) {
return bar().and_then([foo_res](int bar_res) {
return foo_res * bar_res;
});
});
}
Was mache ich falsch?
Als anschaulicheres Beispiel ist unten eine Funktion eines Parsers aufgeführt, die ich schreibe und die ich für besonders unlesbar halte. Die Funktionalität der Funktion ist weniger wichtig als die oben beschriebene Callback-Pyramide...
Code: Select all
template
Parser::maybe_expression Parser::assignment() {
// Given an arbitrary expression, return the VariableExpression contained within
// if one exists, otherwise return a nullopt
auto try_extract_variable = [](grammar::Expression expr)
-> std::optional {
return try_get(std::move(expr))
.and_then([](grammar::PrimaryExpression primary_expr) -> std::optional {
return try_get(std::move(primary_expr.data));
});
};
return equality()
.and_then([&](std::unique_ptr expr) {
// If the top token after parsing Equality() is an =, we either return an
// assignment expression or an error. Otherwise, we directly return the Equality() expression
return consume()
.transform([&](const token::Equal &equal) {
// We are parsing an assignment expression, and so we would like to extract the
// Variable that we are to assign, otherwise return an error.
return try_extract_variable(std::move(*expr))
.transform([&](const grammar::VariableExpression &variable) -> maybe_expression {
return expression()
.map([&](std::unique_ptr assign_to) {
return std::make_unique(grammar::AssignmentExpression{
variable, std::move(assign_to), variable.line_number
});
});
})
.value_or(tl::unexpected{Error{equal.line_number,
ErrorType::kBadAssign,
fmt::format("Incomplete assignment expression")
}});
})
.or_else([&] -> std::optional {
return std::move(expr);
})
.value();
});
}