Uma "calculadora" complexa é atualmente invocada assim:
result = calculator.calc(a, b, extra);
Infelizmente, às vezes pode falhar devido a mudanças não relacionadas acontecendo no servidor. Sincronizar com diferentes departamentos é difícil -- tentar novamente é mais fácil.
Para isso, implementei uma retry
função lambda:
auto retry = [](const auto& func, auto... args) {
int attempts = 5;
for (;;) try {
return func(args...);
}
catch (const std::system_error& e) {
/* Don't retry in case of a system_error */
throw;
}
catch (const exception& e) {
if (attempts-- == 0)
throw; /* Give up and rethrow */
Logging::log(Logging::Level::Warning,
"%s. Retrying %d more times.", e.what(), attempts);
sleep(2);
}
};
O acima compila, mas, conforme tento usá-lo:
result = retry(calculator.calc, a, b, extra);
Recebo um erro do clang:
referência à função membro não estática deve ser chamada
GNU c++ sinaliza a mesma linha com:
uso inválido de função de membro não estático
De fato, este calc()
é um método não estático da Calculator
classe.
Como eu usaria meu novo lambda?
No lambda, substitua
func(args...)
porstd::invoke(func, args...)
. Agora seu lambda suporta ponteiros de função membro como chamáveis, além de ponteiros/referências de função, lambdas e outros objetos de função.Para passar o ponteiro da função membro, a sintaxe é:
onde
Calculator
é o tipo de classe decalculator
. O argumento extra&calculator
é necessário para invocar a função membro não estática no objeto/instância especificado da classe.std::invoke()
sabe como lidar com isso automaticamente.Você precisa ter um pouco de cuidado aqui. Como você passa os argumentos por valor, se você escrever
calculator
em vez de&calculator
você acidentalmente chamará a função membro em uma cópia decalculator
.Geralmente, seria melhor passar os argumentos encaminhando a referência:
Então você também pode chamá-lo de:
std::invoke()
sabe como lidar com ponteiros e referências à instância da classe ao invocar ponteiros de função membro.Observe, no entanto, que, como você pode ligar
func()
diversas vezes, não devestd::forward
incluir referências de encaminhamento na chamada.Além disso, como uma nota lateral, se você quiser manter
retry
genérico, não é suficiente capturarstd::exception
. Não há nenhuma exigência de que exceções devam ser derivadas destd::exception
. Essa é apenas uma convenção usada na biblioteca padrão e, às vezes, por outras bibliotecas.Um bloco de fallback
catch
para todas as outras exceções deve ser parecido com este:Você pode passar diretamente um lambda: