Программирование. Принципы и практика использования C++ Исправленное издание, стр. 398
{ return a<b?b:a; }Сообщения об ошибке, выдаваемые компилятором, интересны, но не слишком информативны. В случае опасности можете отменить определение макроса.
#undef maxК счастью, этот макрос не привел к большим неприятностям. Тем не менее в широко используемых заголовочных файлах существуют десятки тысяч макросов; вы не можете отменить их все, не вызвав хаоса.
Не все параметры макросов используются как выражения. Рассмотрим следующий пример:
#define ALLOC(T,n) ((T*)malloc(sizeof(T)*n))Это реальный пример, который может оказаться очень полезным для предотвращения ошибок, возникающих из-за согласованности между желательным типом выделяемой памяти и использованием оператора
sizeofdouble* p = malloc(sizeof(int)*10); /* похоже на ошибку */К сожалению, написать макрос, который позволял бы выявить исчерпание памяти, — нетривиальная задача. Это можно было бы сделать, если бы мы в каком-то месте программы соответствующим образом определили переменную
error_varerror()#define ALLOC(T,n) (error_var = (T*)malloc(sizeof(T)*n), \ (error_var==0)\ ?(error("Отказ выделения памяти"),0)\ :error_var)Строки, завершающиеся символом
\new27.8.2. Синтаксис макросов
Можно определить макрос, который приводит текст исходного кода в приятный для вас вид. Рассмотрим пример.
#define forever for(;;)#define CASE break; case#define begin {#define end }
• Многие люди не разделяют ваших взглядов на то, что считать лучшим синтаксисом.
• Улучшенный синтаксис является нестандартным и неожиданным; остальные люди будут сбиты с толку.
• Использование улучшенного синтаксиса может вызвать непонятные ошибки компиляции.
• Текст программы, который вы видите перед собой, не совпадает с текстом, который видит компилятор, и компилятор сообщает об ошибках, используя свой словарный запас, а не ваш.
Не пишите синтаксические макросы, для того чтобы улучшить внешний вид вашего кода. Вы и ваши лучшие друзья могут считать его превосходным, но опыт показывает, что вы окажетесь в крошечном меньшинстве среди более крупного сообщества программистов, поэтому кому-то придется переписать ваш код (если он сможет просуществовать до этого момента).
27.8.3. Условная компиляция
Представьте себе, что у вас есть два варианта заголовочного файла, например, один — для операционной системы Linux, а другой — для операционной системы Windows. Как выбрать правильный вариант в вашей программе? Вот как выглядит общепринятое решение этой задачи:
#ifdef WINDOWS #include "my_windows_header.h"#else #include "my_linux_header.h"#endifТеперь, если кто-нибудь уже определил
WINDOWS#include "my_windows_header.h"В противном случае будет включен другой заголовочный файл.
#include "my_linux_header.h"Директива
#ifdef WINDOWSWINDOWSВ большинстве крупных систем (включая все версии операционных систем) существуют макросы, поэтому вы можете их проверить. Например, можете проверить, как компилируется ваша программа: как программа на языке C++ или программа на языке C.
#ifdef __cplusplus // в языке C++#else /* в языке C */#endifАналогичная конструкция, которую часто называют стражем включения (include guard), обычно используется для предотвращения повторного включения заголовочного файла.
/* my_windows_header.h: */#ifndef MY_WINDOWS_HEADER#define MY_WINDOWS_HEADER /* информация о заголовочном файле */#endifДиректива
#ifndef#ifndef#ifdef27.9. Пример: интрузивные контейнеры
Контейнеры из стандартной библиотеки языка С++, такие как
vectormapОпределим двухсвязный список с девятью операциями.