GCC реализация угловых скобок включает в себя. Почему это должно быть так, как описано ниже?

11

Этот документ в разделе 2.6 «Вычисленные включения» имеет следующий параграф:

Если строка расширяется до потока токенов, начинающегося с токена <и включающего токен>, то токены между <и первым> объединяются, чтобы сформировать имя файла для включения. Любой пробел между токенами сводится к одному пробелу; тогда любой пробел после начального <сохраняется, но завершающий пробел до закрытия> игнорируется . CPP ищет файл в соответствии с правилами для угловых скобок.

Я знаю, что это определяется реализацией, но почему так должно быть в GCC? Я имею в виду конкретно выделенное предложение выше.

РЕДАКТИРОВАТЬ

Я только что заметил, что третий абзац перед приведенным выше говорит следующее:

Вы должны быть осторожны при определении макроса. #defineсохраняет токены, а не текст. Препроцессор не может знать, что макрос будет использоваться в качестве аргумента #include, поэтому он генерирует обычные токены, а не имя заголовка. Это вряд ли вызовет проблемы, если вы используете двойные кавычки, которые достаточно близки к строковым константам. Однако если вы используете угловые скобки, у вас могут возникнуть проблемы .

Кто-нибудь знает, на какую проблему указывают здесь?

Ayrosa
источник
6
Вероятно, разработчики GCC считают, что наличие пробелов в конце имени файла является мерзостью.
user3386109
1
Имена файлов с начальными и / или конечными пробелами очень придирчивы, особенно в Windows.
Реми Лебо
1
То, что оно определено именно так, не обязательно означает, что оно должно быть определено так. Это не предписано стандартом.
eerorika
Visual Studio удаляет начальное и конечное пространство, поэтому ведет себя по-разному. HP aCC ведет себя как gcc (возможно, по причинам совместимости).
Слимак
Иногда документация просто описывает, что делает код, а не наоборот, особенно в тех случаях, когда это не имеет значения (вы можете использовать любое место в любом месте, если используете двойные кавычки).
rustyx

Ответы:

9

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

Кажется, что первоначальная реализация приземлилась в 2000-07-03 (два десятилетия назад!). Соответствующая часть выглядит так ( источник ):

  for (;;)
    {
      t = cpp_get_token (pfile);
      if (t->type == CPP_GREATER || t->type == CPP_EOF)
        break;

      CPP_RESERVE (pfile, TOKEN_LEN (t));
      if (t->flags & PREV_WHITE)
        CPP_PUTC_Q (pfile, ' ');
      pfile->limit = spell_token (pfile, t, pfile->limit);
    }

Примечательно, что он вспыхивает, когда видит CPP_GREATERтокен (то есть >), прежде чем резервировать память для токена. Это имеет смысл, поскольку нет необходимости выделять память, когда токен не будет записан в буфер.

Затем, только после того, как память зарезервирована, препроцессор проверяет, имеет ли токен предшествующий пробел ( t->flags & PREV_WHITE), и, когда это происходит, записывает символ пробела в буфер.

В результате в < foo / bar >, сохраняются только пробелы до foo(то есть после начального <) /и bar.

cpplearner
источник
Блестящий, отличный ответ. Это первый раз, когда у меня есть возможность увидеть кусок кода в GCC. Спасибо тебе за это.
Айроса
Но разве это не тот случай, когда условие if (t->flags & PREV_WHITE) CPP_PUTC_Q (pfile, ' ');противоречит тому, что говорится в документе: «Любой пробел между токенами сводится к одному пробелу; ...»?
Айроса