Почему в С++ 0x не был добавлен доход?

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

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

В настоящее время я читаю о С++ 0x и его добавлениях; и, читая о реализации lambdas в С++ 0x, я узнал, что это было сделано и с помощью автоматически созданных классов, оснащенных operator(), хранящими лямбда-код. Естественный вопрос сформировался в моем сознании: они сделали это для лямбда, почему они тоже не рассматривали его для поддержки "урожая"?

Конечно, они могут видеть значение co-подпрограмм... поэтому я могу только догадываться, что они думают о реализации на основе макросов (таких как Simon Tatham's) в качестве адекватной замены. Тем не менее, они не по многим причинам: состояние, поддерживаемое людьми, не реентеративное, основанное на макроуровне (это само по себе достаточно) и т.д.

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

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

В:

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

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

Ответ 1

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

Проверьте papers. Кто-нибудь предлагал это?

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

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


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

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

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

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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

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

Ответ 6

Ну, для такого тривиального примера, что единственная проблема, я вижу, что 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.

Ответ 7

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

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

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

Ответ 8

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

Поддержка сопрограммы - это не то же самое, что поддержка lambdas, а не простая трансформация, как играемая с устройством Duff.

Вам нужно полные асимметричные сопрограммы (стеки) для работы как генераторы в Python. Реализация Саймона Татхама и Chris ' являются стеклыми, а Boost.Coroutine является сложным, хотя и тяжелым.

К сожалению, у С++ 11 до сих пор нет yield для сопрограмм, возможно, С++ 1y;)

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