Например, операция
_Pragma("nonstandardtreatmenttypeB on") является эквивалентом следующего указания:
#pragma nonstandardtreatmenttypeB on
Поскольку в этой операции не используется символ #, она может выступать в качестве части расширения макроса:
#define PRAGMA(X) _Pragma(#X)
#define LIMRG(X) PRAGMA(STDC CX_LIMITED_RANGE X)
После этого можно применять код вроде показанного ниже:
LIMRG ( ON )
Кстати, следующее определение не работает, хотя выглядит вполне корректным:
#define LIMRG(X) _Pragma(STDC CX_LIMITED_RANGE #X)
Проблема в том, что оно полагается на конкатенацию строк, но компилятор не выполняет конкатенацию до тех пор, пока не завершится работа препроцессора.
Оператор _Pragma выполняет всю работу по превращению из строк, т.е. управляющие последовательности в строке преобразуются в представляющие их символы. Таким образом, вызов операции
_Pragma("use_bool \"true \"false") принимает следующий вид:
#pragma use_bool "true "false
Обобщенный выбор (C11)
Термин обобщенное программирование относится к коду, который не является специфичным для конкретного типа, но после указания типа может транслироваться в код для этого типа. Например, язык C++ позволяет создавать обобщенные алгоритмы в форме шаблонов, которые компилятор затем использует при автоматическом создании экземпляра кода для указанного типа. В языке С нет ничего близко похожего на это. Тем не менее, в СИ появился новый вид выражения, называемого выражением обобщенного выбора, которое можно применять для выбора значения на основе типа выражения, т.е. базируясь на том, является ли типом выражения int, double и т.д. Выражение обобщенного выбора — это не оператор препроцессора, но обычно оно используется как часть определения макроса #define, обладающего определенными чертами обобщенного программирования.
Выражение обобщенного выбора выглядит следующим образом:
_Generic(x, int: 0, float: 1, double: 2, default: 3)
Здесь _Generic — новое ключевое слово С11. Круглые скобки после _Generic содержат несколько элементов, разделенных запятыми. Первый элемент представляет собой выражение, а каждый из оставшихся элементов — тип, за которым следует значение, наподобие float: 1. Тип первого элемента соответствует одной из меток, и значением всего выражения будет значение, указанное после давшей совпадение метки.
Препроцессор и библиотека С 687
Например, предположим, что х в показанном выше выражении является переменной типа int. Тогда тип х соответствует метке int:, приводя к тому, что все выражение получает значение 0. Если тип не соответствует ни одной метке, значением всего выражения становится то, что указано после метки default:. Оператор обобщенного выбора немного похож на оператор switch за исключением того, что сопоставление с метками производится для типа выражения, а не его значения.
Давайте рассмотрим пример объединения оператора обобщенного выбора с определение макроса:
#define MYTYPE(X) _Generic((X),\ int: "int",\ float : "float",\ double: "double",\ default: "other"\
)
Вспомните, что макрос должен быть определен в одной логической строке, но с помощью символа \ одну логическую строку можно разбивать на несколько физических строк. В данном случае выражение обобщенного выбора оценивается как строка. Скажем, вызов макроса MYTYPE (5) оценивается как строка "int", поскольку тип значения 5 соответствует метке int:. В листинге 16.13 приведена дальнейшая иллюстрация этого макроса.
Листинг 16.13. Программа predef .с

Вот вывод программы:
int
double
другой
другой
В последних двух обращениях к MYTYPE() используются типы, не имеющие соответствующих меток, поэтому выбирается строка с меткой default:. Мы могли бы предусмотреть большее число меток, расширив возможности макроса, но этот пример задуман только в качестве демонстрации особенностей работы макросов, основанных
на _Generic.
688 глава 16
При оценке выражения обобщенного выбора программа не вычисляет первый элемент: она только выясняет его тип. Единственным вычисляемым выражением являет ся то, которое указано в совпадающей метке.
Средство _Generic можно применять для определения макросов, которые действуют подобно функциям, не зависящим от типа (“обобщенным”). В разделе, посвященном библиотеке math, далее в главе будет приведен пример.
Встраиваемые функции (С99)
Обычно с вызовом функции связаны накладные расходы. Это означает, что подготовка вызова, передача аргументов, переход к коду функции и возврат требуют време ни на выполнение. Как вы уже видели, макрос можно использовать для встраивания кода, тем самым избегая таких накладных расходов. В стандарте С99 был позаимствован у C++ (но не во всем точно) другой подход — встраиваемые функции. Исходя из его названия, вы могли бы ожидать, что встраиваемая функция заменяет вызов функции встраиваемым кодом, но это не так. В стандартах С99 и C11 на самом деле указано так: “превращение функции во встраиваемую предполагает, что ее вызов будет настолько быстрым, насколько это возможно. Степень, до которой подобные предположения эффективны, зависит от реализации”. Таким образом, преобразование функции во встроенную может привести к тому, что компилятор заменит вызов функции встраиваемым кодом и/или предпримет оптимизации другого рода либо вообще не окажет никакого воздействия.
Читать дальше