|
Программирование >> Решение нетривиальных задач
84. Если все альтернативы отпали, то используйте препроцессор Мы увидим в главе, посвященной Си++, что препроцессор Си не играет большой роли в Си++. Хотя есть немного мест, где он все еще кстати. Вот первое из них: #ifdef DEBUG # define D(x) x #else # define D(X) /* пусто */ #endif Вместо макроса D() подставляется его аргумент, если вы занимаетесь отладкой, иначе он расширяется до пустой строки. Он используется так: f() { D( printf( 3TO отладочная информация\n ); ) В данном случае аргументом D() является целиком оператор printf() , который исчезает после того, как вы закончите отладку. Другой подобный вариант использования кстати, когда вы должны инициализировать те несколько неизбежных глобальных переменных в большой программе. Проблема заключается в синхронизации объявлений переменных (в заголовочном файле) с их определениями (в файле .c), где реально выделяется память и переменные инициализируются. Вот образец заголовочного файла: #ifdef ALLOC # define I(x) x # define EXPORTED /* пусто */ #else # define I(x) /* пусто */ # define EXPORTED extern #endif EXPORTED int glob x[10] I( ={1, 2, 3, 4} ); EXPORTED some object glob y I( ( конструктор , аргументы )); В определенном месте своей программы (я обычно делаю это в файле с именем globals.cpp) вы помещаете следующие строки: #define ALLOC #include globals.h Далее везде вы просто включаете этот файл без предварительной директивы #define ALLOC. Когда вы компилируете globals.cpp, директива #define ALLOC вызывает следующую подстановку: /* пусто */ int glob x[10] ={1, 2, 3, 4} ; /* пусто */ some object glob y ( конструктор , аргументы ); Отсутствие #define ALLOC везде приводит к следующей подстановке: extern int glob x[10] /* пусто */ ; extern some object glob y /* пусто */ ; Последним примером использования препроцессора будет макрос ASSERT(), который выводит сообщение об ошибке и завершает программу, лишь если вы осуществляете отладку (директивой #define определена константа debug) и аргумент assert() имеет значение ложь . Он очень удобен для тестирования, например, аргументов типа указателей со значением null. Вариант ASSERT(), используемый в виде: f( char *p) ASSERT( p, f() : Неожиданный аргумент NULL. ); определяется следующим образом: #ifdef DEBUG #define ASSERT(условие, сообщение) if ( !(условие) ) \ fprintf(stderr, ASSERT( #условие ) НЕ ВЫПОЛНЕНО \ [Файл FILE , Строка %d]:\n\t%s\n ,\ LINE , (сообщение) );\ exit( -1 );\ else #else # efine ASSERT(c,m) /* пусто */ #endif В вышеуказанном примере ASSERT() выводит следующую строку при отрицательном результате проверки: ASSERT(p) НЕ ВЫПОЛНЕНО [Файл whatever.cpp, Строка 123]: f() : Неожиданный аргумент NULL. и затем выходит в вызывающую программу. Он получает текущее имя файла и номер строки от препроцессора, используя предопределенные макросы FILE и LINE . Условие, вызывающее отрицательный результат, выводится посредством оператора получения строки ANSI Си (символ #), который фактически окружает расширенный аргумент кавычками после выполнения подстановки аргумента. Строка #условие расширяется до p в настоящем примере). Затем вступает в действие обычная конкатенация строк Си для слияния вместе разных строк, создавая единый отформатированный аргумент для fprintf() . Здесь следует использовать препроцессор, потому что вам нужно вывести на консоль имя файла и номер строки, для которых выполнена проверка. Встроенная функция Си++ может вывести лишь имя того файла с номером строки, в котором определена встроенная функция. Все компиляторы, поддерживающие стандарт ANSI Си, должны реализовывать макрос assert(expr) в заголовочном файле assert.h, но макрос ANSI Си не может выводить заказанное сообщение об ошибке. Макрос ANSI Си assert() действует, если не определена константа NDEBUG (вариант по умолчанию).
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.002
При копировании материалов приветствуются ссылки. |