Реализовать асинхронный ленивый генератор на C ++ - PullRequest
1 голос
/ 24 апреля 2020

Мое намерение состоит в том, чтобы использовать универсальный интерфейс c для перебора файлов из различных источников ввода-вывода. Например, мне может понадобиться итератор, который, если позволит авторизация, будет лениво открывать каждый файл в моей файловой системе и возвращать дескриптор открытого файла. Затем я бы хотел использовать тот же интерфейс для перебора, возможно, объектов из корзины AWS S3. В этом последнем случае итератор будет загружать каждый объект / файл из S3 в локальную файловую систему, затем открывать этот файл и снова возвращать дескриптор файла. Очевидно, что реализация обоих интерфейсов итераторов будет очень разной.

Я считаю, что три наиболее важных цели проектирования заключаются в следующем:

  • Для каждого вызова iter++, std :: future или PPL pplx :: task возвращается, представляющий запрошенный дескриптор файла. Мне нужна возможность сделать эквивалент PPL choice(when_any), потому что я ожидаю одновременного запуска нескольких итераторов.
  • Пользовательская реализация итератора должна быть долговечной / восстанавливаемой. То есть он периодически записывает, где он находится в процессе сканирования файловой системы (или сканирования сегмента S3 и т. Д. c.), Чтобы он мог попытаться возобновить сканирование с последней известной позиции в случае приложения cra sh и перезагрузка.
  • Лучшее усилие, чтобы не go за пределами C ++ 11 (и, возможно, C ++ 14).

Я бы предположил сделать STL input_iterator моей отправной точкой для интерфейса. В конце концов, я вижу этот пост 2014 с простым примером . Это не связано с IO, но я вижу другую статью от 2001 года, которая якобы включает IO в пользовательский итератор STL . Пока все хорошо.

Я начинаю беспокоиться, когда читаю статью типа " Функции генератора в C ++ ". Ack! Эта статья создает у меня впечатление, что я не могу достичь своего намерения создать функцию генератора, замаскированную под итератор, возможно, не без ожидания C ++ 20. Аналогично, этот другой пост 2016 SO звучит так, как будто это шершень для создания функций генератора в C ++.

Хотя реализация для моих пользовательских итераторов будет сложной, возможно, что последние две ссылки были чем-то большим, чем я пытаюсь достичь. Другими словами, возможно, мой план не ошибочен? Я хотел бы знать, с какими барьерами я борюсь, если я собираюсь создать реализацию с отложенным генератором за собственным input_iterator. Если бы мне нужно было использовать что-то еще, например Boost iterator_facade, я был бы признателен за объяснение «почему». Кроме того, я хотел бы знать, было ли то, что я делаю, уже реализовано в другом месте. Возможно, PPL, который я только начал изучать, уже имеет решение для этого?

ps Я привел пример итератора S3, который лениво загружает каждый запрошенный файл и затем возвращает дескриптор открытого файла. Да, я знаю, это означает, что итератор создает побочный эффект, которого обычно я бы хотел избежать. Тем не менее, для моей предполагаемой цели я не уверен в более чистом способе сделать это.

1 Ответ

0 голосов
/ 24 апреля 2020

Вы смотрели на CoroutineTS? Он поставляется с C ++ 20 и позволяет выполнять то, что вы ищете.

Некоторые компиляторы (GNU 10, MSV C) уже имеют некоторую поддержку.

Specifi c библиотечные функции поверх стандартных сопрограмм, которые могут вас заинтересовать:

  • generator<T>

    cppcoro::generator<const std::uint64_t> fibonacci()
    {
      std::uint64_t a = 0, b = 1;
      while (true)
      {
        co_yield b;
        auto tmp = a;
        a = b;
        b += tmp;
      }
    }
    
    void usage()
    {
      for (auto i : fibonacci())
      {
        if (i > 1'000'000) break;
        std::cout << i << std::endl;
      }
    }
    

    Генератор представляет тип сопрограммы, который создает последовательность значений типа T, где значения создаются лениво и синхронно.

    Тело сопрограммы может выдавать значения типа T с помощью ключевого слова co_yield. Обратите внимание, что тело сопрограммы не может использовать ключевое слово co_await; значения должны создаваться синхронно.

  • async_generator<T>

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

    Тело сопрограммы может использовать как выражения co_await, так и co_yield.

    Потребители генератора могут использовать для co_await диапазон, основанный на -l oop для использования значений.

    Пример

    cppcoro::async_generator<int> ticker(int count, threadpool& tp)
    {
      for (int i = 0; i < count; ++i)
      {
        co_await tp.delay(std::chrono::seconds(1));
        co_yield i;
      }
    }
    
    cppcoro::task<> consumer(threadpool& tp)
    {
      auto sequence = ticker(10, tp);
      for co_await(std::uint32_t i : sequence)
      {
        std::cout << "Tick " << i << std::endl;
      }
    }
    

Sidenote: Boost Asio имеет экспериментальную поддержку CoroutineTS для нескольких выпусков, так что если вы хотите, чтобы вы можно объединить.

...