Почему не было добавлено yield в C ++ 0x? - PullRequest
30 голосов
/ 05 октября 2010

Я использовал yield во многих моих программах на Python, и он действительно во многих случаях очищает код.Я написал об этом в блоге , и это одна из самых популярных страниц моего сайта.

C # также предлагает выход - он реализован с помощью сохранения состояний на стороне вызывающего, через автоматически сгенерированный класс, которыйсохраняет состояние, локальные переменные функции и т. д.

Я сейчас читаю о C ++ 0x и его дополнениях;и, читая о реализации лямбда-выражений в C ++ 0x, я обнаружил, что это было сделано через автоматически сгенерированные классы, снабженные operator (), хранящими лямбда-код.Естественный вопрос возник у меня в голове: они сделали это для лямбд, почему они тоже не рассмотрели это как поддержку «yield»?

Конечно, они могут видеть ценность совместных процедур ... такЯ могу только догадываться, что они думают, что основанные на макросах реализации (такие как Simon Tatham ) как адекватная замена.Они, однако, не по многим причинам: состояние сохраняемого абонента, не реентерабельный, основанный на макросах (достаточно одной причины) и т. Д.

Редактировать: yield нене зависит от сборки мусора, потоков или волокон.Вы можете прочитать статью Саймона, чтобы увидеть, что я говорю о том, что компилятор выполняет простое преобразование, такое как:

int fibonacci() {
    int a = 0, b = 1;
    while (true) {
        yield a;
        int c = a + b;
        a = b;
        b = c;
    }
}

Into:

struct GeneratedFibonacci {
    int state;
    int a, b;

    GeneratedFibonacci() : state (0), a (0), b (1) {}

    int operator()() {
        switch (state) {
        case 0:
            state = 1;
            while (true) {
                return a;

        case 1:
                int c = a + b;
                a = b;
                b = c;
            }
        }
    }
}

Сборка мусора?Нет темы?Волокна?Простое преобразование?Возможно, да.

Ответы [ 8 ]

22 голосов
/ 13 декабря 2010

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

Они началиЖизнь как реализация библиотеки в Boost, которая доказала, что

  • лямбды широко используются: многие люди будут использовать их, когда они будут доступны, и что
  • реализация библиотеки вC ++ 03 имеет ряд недостатков.

Исходя из этого, комитет решил принять некие лямбды в C ++ 0x, и я считаю, что они изначально экспериментировалис добавлением более общих языковых возможностей, позволяющих реализовать лучшую библиотеку, чем у Boost.

И в конечном итоге они сделали ее основной функцией языка, поскольку у них не было другого выбора: потому что она* Можно сделать достаточно хорошей библиотечной реализацией.

Новые возможности базового языка не просто добавляются в язык, потому что они кажутся хорошей идеей.Комитет очень не хочет добавлять их, и рассматриваемая особенность действительно должна проявить себя.Должно быть показано, что эта возможность:

  • возможна для реализации в компиляторе,
  • собирается решить реальную потребность и
  • , что реализация библиотеки нене достаточно хорошо.

В случае, если ключевое слово yield, мы знаем, что первый пункт может быть решен.Как вы показали, это довольно простое преобразование, которое можно выполнить механически.

Второй момент сложен.Сколько из нужно для этого есть?Насколько широко используются существующие реализации библиотек?Сколько людей попросили об этом или представили предложения для этого?

Кажется, последний пункт тоже прошел.По крайней мере, в C ++ 03 реализация библиотеки имеет некоторые недостатки, как вы указали, что может оправдать реализацию на базовом языке.Может ли быть реализована лучшая реализация библиотеки в C ++ 0x?

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

8 голосов
/ 05 октября 2010

Добавление ключевого слова всегда сложно, поскольку оно делает недействительным ранее действительный код. Вы пытаетесь избежать этого в языке с такой же кодовой базой, как C ++.

Эволюция C ++ является публичным процессом. Если вы чувствуете, что yield должен быть там, сформулируйте соответствующий запрос в стандартный комитет C ++.

Вы получите ответ непосредственно от людей, которые приняли решение.

7 голосов
/ 05 февраля 2016

Так что, похоже, он не попал в C ++ 11 или C ++ 14, но может быть на пути к C ++ 17.Взгляните на лекцию C ++ сопрограммы, отрицательную служебную абстракцию от CppCon2015 и статью здесь .

Подводя итог, они работают над расширением функций c ++, чтобы иметьприносить и ждать, как функции функций.Похоже, у них есть начальная реализация в Visual Studio 2015, но не уверен, что у clang есть реализация.Кроме того, кажется, что у них могут быть некоторые проблемы с использованием yield и await в качестве ключевых слов.

Презентация интересна тем, что он говорит о том, насколько это упростило сетевой код, когда вы ожидаете поступления данных, чтобы продолжитьпоследовательность обработки.Удивительно, но похоже, что использование этих новых сопрограмм приводит к более быстрому / меньшему количеству кода, чем то, что можно было бы сделать сегодня.Это отличная презентация.

Предложение по возобновляемым функциям для C ++ можно найти здесь .

7 голосов
/ 06 октября 2010

Они сделали это для лямбд, почему они не учли это и для поддержки урожая?

Отметьте документы . Кто-нибудь предлагал это?

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

Не обязательно. Я уверен, что они знают, что такие макро-решения существуют, но замена их сама по себе не является достаточной мотивацией для передачи новых функций.


Несмотря на то, что вокруг нового ключевого слова существуют различные проблемы, они могут быть преодолены с помощью нового синтаксиса, например, для лямбда-выражений и использования auto в качестве возвращаемого типа функции.

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

Но, по сути, стандартная библиотека C ++ приняла концепцию итераторов, отличную от той, которую вы видели в yield. Сравните с итераторами Python, которые требуют только двух операций:

  1. an_iter.next () возвращает следующий элемент или вызывает StopIteration (next () встроено в 2.6 вместо использования метода)
  2. iter (an_iter) возвращает an_iter (так что вы можете одинаково обращаться с итераторами и итераторами в функциях)

Итераторы C ++ используются парами (которые должны быть одного типа), разделены на категории, это будет семантическим сдвигом для перехода к чему-то более поддающемуся конструкции урожайности, и этот сдвиг не будет вписываться в концепции (которая с тех пор была отброшена, но пришла относительно поздно). Например, см. обоснование для (оправданно, если неутешительно) отклонения моего комментария об изменении циклов на основе диапазона для формы, которая значительно облегчила бы написание этой другой формы итератора.

Чтобы конкретно прояснить, что я имею в виду для различных форм итераторов: вашему примеру сгенерированного кода требуется другой тип , чтобы быть типом итератора, плюс связанный механизм для получения и обслуживания этих итераторов. Не то чтобы с этим нельзя было справиться, но это не так просто, как вы можете себе представить. Реальная сложность заключается в «простом преобразовании», учитывающем исключения для «локальных» переменных (в том числе во время построения), контролирующем время жизни «локальных» переменных в локальных областях в генераторе (большинство должно быть сохранено при вызовах) и так далее.

2 голосов
/ 09 октября 2010

Ну, для такого тривиального примера, единственная проблема, которую я вижу, состоит в том, что std::type_info::hash_code() не указано constexpr.Я считаю, что соответствующая реализация все еще может сделать это и поддержать это.В любом случае настоящая проблема заключается в получении уникальных идентификаторов, поэтому может быть другое решение.(Очевидно, я заимствовал вашу конструкцию «главного переключателя», спасибо.)

#define YIELD(X) do { \
    constexpr size_t local_state = typeid([](){}).hash_code(); \
    return (X); state = local_state; case local_state: ; } \
while (0)

Использование:

struct GeneratedFibonacci {
    size_t state;
    int a, b;

    GeneratedFibonacci() : state (0), a (0), b (1) {}

    int operator()() {
        switch (state) {
        case 0:
            while (true) {
                YIELD( a );
                int c = a + b;
                a = b;
                b = c;
            }
        }
    }
}

Хмм, им также нужно будет гарантировать, что хэш не равен 0.Там тоже нет ничего важного.Макрос DONE легко реализовать.


Реальная проблема заключается в том, что происходит, когда вы возвращаетесь из области действия с локальными объектами.Нет надежды на сохранение стекового фрейма на языке Си.Решение состоит в том, чтобы использовать реальную сопрограмму, а C ++ 0x напрямую решает эту проблему с помощью потоков и фьючерсов.

Рассмотрим этот генератор / сопрограмму:

void ReadWords() {
    ifstream f( "input.txt" );

    while ( f ) {
        string s;
        f >> s;
        yield s;
    }
}

Если используется подобный приемдля yield, f уничтожается в первый yield, и продолжать цикл после него недопустимо, потому что вы не можете goto или switch после определения объекта без POD.

2 голосов
/ 05 октября 2010

В общем, вы можете отслеживать, что происходит с помощью комитетов , хотя лучше отслеживать, а не искать конкретную проблему.

Одна вещь, которую следует помнить о C ++комитет состоит в том, что это комитет добровольцев, и он не может выполнить все, что хочет.Например, в исходном стандарте не было карты хеш-типа, потому что они не смогли сделать это вовремя.Вполне возможно, что в комитете не было никого, кто достаточно заботился о yield и о том, что он делает, чтобы убедиться, что работа выполнена.

Лучший способ узнать это - спросить активного члена комитета.

1 голос
/ 13 декабря 2010

было несколько реализаций сопрограмм в качестве библиотек пространства пользователя. Однако, и вот в чем дело, эти реализации основаны на нестандартных деталях. Например, нигде в стандарте c ++ не указано, как хранятся кадры стека. Большинство реализаций просто копируют стек, потому что именно так работает большинство реализаций c ++

относительно стандартов, c ++ мог бы помочь поддержке сопрограмм, улучшив спецификацию стековых фреймов.

На самом деле, «добавление» его в язык не является для меня хорошей идеей, потому что это сделало бы вас «достаточно хорошей» реализацией для большинства случаев, полностью зависящих от компилятора. Для случаев, когда использование сопрограммы имеет значение, это в любом случае неприемлемо

0 голосов
/ 26 марта 2013

сначала согласитесь с @Potatoswatter.

Поддержка сопрограммы - это не то же самое, что поддержка лямбд, и это не простое преобразование , как в случае игры с устройством Даффа.

Вам нужно полных асимметричных сопрограмм (стек), чтобы работать как генераторы в Python. Реализация * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1015

К сожалению, C ++ 11 до сих пор не имеет yield для сопрограмм, может быть C ++ 1y;)

PS: Если вам действительно нравятся генераторы в стиле Python, взгляните на this .

...