Как повторно использовать замыкание C ++ одновременно с переменными аргументами - подобно JavaScript? - PullRequest
0 голосов
/ 18 февраля 2020

JavaScript создает замыкание с областью, в которой оно было создано. Например:

function startGreeter( name, iterations, delay ) {
    function greeter() {
        console.log("greetings, "+name+"! i="+iterations);
        if( --iterations )
            setTimeout( greeter, delay );
    }
    setTimeout( greeter, delay );
}

startGreeter("Alex",3,1000);
startGreeter("Beth",2,1500);

JavaScript создает два разных экземпляра замыкания для функции greeter (); один для Алекса и один для Бет. Конечно, мы могли бы создать десятки или сотни таких экземпляров; из массива данных, например.

Я хотел бы сделать то же самое sh в C ++; в частности, чтобы иметь возможность создавать N различных экземпляров одновременного закрытия функции.

void startGreeterTask( AsyncManager& asyncManager, std::string name, int iterations, longtime msDelay )
{
    TaskFunction greetingTask = [&]()
    {
        std::cout << "greetings, task " << name << "! iterations = " << iterations << std::endl;
        if( --iterations )
        {
            asyncManager.setTimeout( greetingTask, msDelay );
        }
    };
    asyncManager.setTimeout( greetingTask, msDelay );
}

int main( int argc, char* argv[] )
{
    AsyncManager asyncManager;
    startGreeterTask( asyncManager, "Alex", 3, 1000 );
    startGreeterTask( asyncManager, "Beth", 2, 1500 );
    asyncManager.execute();
}

AsyncManager предоставляет функциональность setTimeout (), и он работает, если я создаю несколько замыканий в области действия main (). Но в startGreetingTask () функция закрытия приветствия приветствия не работает так же, как функция greeter () в JavaScript. Насколько я понимаю, welcomeTask () и все упоминаемые параметры выпадают из области видимости, когда startGreeterTask () завершает выполнение, поэтому память стека освобождается. Вызов startGreeterTask () во второй раз создает конфликтующее использование стековой памяти, и ошибка сегмента гарантирует.

Как можно заставить startGreeterTask () работать как функция startGreeter ()?

1 Ответ

0 голосов
/ 19 февраля 2020

Спасибо Игорю Тандетнику за то, что он указал мне правильное направление. Я публикую полный ответ здесь, чтобы другие могли его найти.

Решением было (а) использовать захват копии по умолчанию, (б) использовать захват ссылок для экземпляра класса, который я не хочу копировать, (c) добавьте ключевое слово mutable, чтобы разрешить изменение захваченных переменных, и (d) избежать ссылки на функцию замыкания на себя. Теперь вместо того, чтобы вызывать себя, замыкание вызывает функцию, в которой определено замыкание. Эта форма самоссылки через слой косвенности немного теряет гибкость и выражение идиомы JavaScript, но она полностью функциональна и довольно хорошо решает проблему.

Тем не менее, если кто-то еще решит эту конкретную проблему самоссылки замыкания, я хотел бы увидеть решение.

void greetTask( AsyncManager& asyncManager, std::string name, int iterations, longtime msDelay )
{
    TaskFunction greetingClosure = [=,&asyncManager]() mutable
    {
        std::cout << "greetings, task " << name << "! iterations = " << iterations << std::endl;
        if( --iterations )
        {
            greetTask( asyncManager, name, iterations, msDelay );
        }
    };

    asyncManager.setTimeout( greetingClosure, msDelay );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...