Как передать метод объекта в лямбда-функцию?

Сложный «калькулятор» в настоящее время вызывается следующим образом:

            result = calculator.calc(a, b, extra);

К сожалению, иногда это может привести к сбою из-за несвязанных изменений, происходящих на сервере. Синхронизироваться с разными отделами сложно — повторить попытку проще…

С этой целью я реализовал retry лямбда-функцию:

    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);
        }
    };

Вышеупомянутое компилируется, но, когда я пытаюсь его использовать:

            result = retry(calculator.calc, a, b, extra);

Я получаю сообщение об ошибке от clang: reference to non-static member function must be called. GNU C++ помечает одну и ту же строку знаком invalid use of non-static member function.

Действительно, calc() — это нестатический метод класса Calculator.

Как бы я использовал свою новую лямбду?

Почему это лямбда, а не обычная именованная функция?

Barmar 30.08.2024 00:19

Потому что вызывающая функция сама по себе является шаблоном, и создавать новый мне не хотелось :)

Mikhail T. 30.08.2024 00:23
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
65
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

В лямбде замените func(args...) на std::invoke(func, args...). Теперь ваша лямбда поддерживает указатели на функции-члены как вызываемые в дополнение к указателям/ссылкам на функции, лямбда-выражениям и другим объектам функций.

Чтобы передать указатель на функцию-член, синтаксис следующий:

result = retry(&Calculator::calc, &calculator, a, b, extra);

где Calculator — тип класса calculator. Дополнительный аргумент &calculator необходим для вызова нестатической функции-члена указанного объекта/экземпляра класса. std::invoke умеет справляться с этим автоматически.

Здесь нужно быть немного осторожным. Поскольку вы используете аргументы по значению, если вы напишете calculator вместо &calculator, вы случайно вызовете функцию-член для копии calculator.


Обычно было бы лучше передавать аргументы путем пересылки ссылки:

auto retry = [](auto&& func, auto&&... args) {

Тогда вы также можете назвать это как

result = retry(&Calculator::calc, calculator, a, b, extra);

std::invoke знает, как обрабатывать указатели и ссылки на экземпляр класса при вызове указателей на функции-члены.

Однако обратите внимание: поскольку вы потенциально вызываете func несколько раз, вам не следует std::forward использовать ссылки на пересылку в вызове.


Кроме того, если вы хотите сохранить retry общим, недостаточно поймать std::exception. Нет требования, чтобы исключения производились от std::exception. Это всего лишь соглашение, используемое в стандартной библиотеке, а иногда и в других библиотеках.

Резервный блок catch для всех остальных исключений должен выглядеть так:

catch(...)
{
    // do something
}

Альтернативой является передача calculator в списке захвата лямбды, а не в качестве аргумента.

Peter 30.08.2024 01:18

Вы можете передать напрямую лямбду:

retry([&](){ return calculator.calc(a, b, extra); });

Другие вопросы по теме