|
Программирование >> Перегруженные имена функций и идентификаторы
предлагает (а может и требовать) выбор из нескольких близких вариантов поведения. Если вы заинтересованы в написании мобильных программ, можете игнорировать различия между этими тремя случаями, поскольку всех их необходимо будет избегать. Как написать макрос для обмена любых двух значений? На этот вопрос нет хорошего ответа. При обмене целых значений может быть использован хорошо известный трюк с использованием исключающего ИЛИ, но это не сработает дя чисел с плавающей точкой или указателей. Не годится этот прием и в случае, когда оба числа - на самом деле одно и то же число. Из-за многих побочных эффектов не годится и очевидное суперкомпактное решение для целых чисел a=b=a=b. Когда макрос предназначен для переменных произвольного типа (обычно так и бывает), нельзя использовать временную переменную, поскольку не известен ее тип, а стандартный Си не имеет оператора typeof. Если вы не хотите передавать тип переменной третьим параметров, то, возможно, наиболее гибким, универсальным решением будет отказ от использования макроса. У меня есть старая программа, которая пытается конструировать идентификаторы с помощью макроса #define Paste(a, b) a/**/b, но у меня это не работает То, что комментарий полностью исчезает, и, следовательно, может быть использован для склеивания соседних лексем (в частности, для создания новых идентификаторов), было недокументированной особенностью некоторых ранних реализаций препроцессора, среди которых заметна была реализация Рейзера (Reiser). Стандарт ANSI, как и K&R, утверждает, что комментарии заменяются единичными пробелами. Но поскольку необходимость склеивания лексем стала очевидной, стандарт ANSI ввел для этого специальный оператор ##, который может быть использован так: #define Paste(a, b) a##b Как наилучшим образом написать cpp макрос, в котором есть несколько инструкций? Обычно цель состоит в том, чтобы написать макрос, который не отличался бы по виду от функции. Это значит, что завершающая точка с запятой ставится тем, кто вызывает макрос, а в самом теле макроса ее нет. Тело макроса не может быть просто составной инструкцией, заключенной в фигурные скобки, поскольку возникнут сообщения об ошибке (очевидно, из-за лишней точки с запятой, стоящей после инструкции) в том случае, когда макрос вызывается после if, а в инструкции if/else имеется else-часть. Обычно эта проблема решается с помощью такого определения: #define Func() do { \ /* объявления */ \ что-то1; \ что-то2; \ /* ... */ \ } while(0) /* (нет завершающей ; ) */ Когда при вызове макроса добавляется точка с запятой, это расширение становится простой инструкцией вне зависимости от контекста. (Оптимизирующий компилятор удалит излишние проверки или переходы по условию 0, хотя lint это может и не принять.) Если требуется макрос, в котором нет деклараций или ветвлений, а все инструкции - простые выражения, то возможен другой подход, когда пишется одно, заключенное в круглые скобки выражение, использующее одну или несколько запятых. Такой подход позволяет также реализовать возврат значения). Можно ли в головной файл с помощью #include включить другой головной файл? Это вопрос стиля, и здесь возникают большие споры. Многие полагают, что вложенных с помощью #include файлов следует избегать: авторитетный Indian Hill Style Guide неодобрительно отзывается о таком стиле; становится труднее найти соответствующее определение; вложенные #include могут привести к сообщениям о многократном объявлении, если головной файл включен дважды; также затрудняется корректировка управляющего файла дя утилиты Make. С другой стороны, становится возможным использовать модульный принцип при создании головных файлов (головной файл включает с помощью #include то, что необходимо только ему; в противном случае придется каждый раз использовать дополнительный #include, что способно вызвать постоянную головную боль); с помощью утилит, подобных grep (или файа tags) можно легко найти нужные определения вне зависимости от того, где они находятся, наконец, популярный прием: #ifndef HEADERUSED #define HEADERUSED ...содержимое головного файла... #endif делает головной файл идемпотентным , то есть такой файл можно безболезненно включать несколько раз; средства автоматической поддержки файлов для утилиты Make (без которых все равно не обойтись в случае больших проектов) легко обнаруживают зависимости при наличии вложенных #include. Работает ли оператор sizeof при использовании средства препроцессора #if? Нет. Препроцессор работает на ранней стадии компиляции, до того как становятся известны типы переменных. Попробуйте использовать константы, определенные в файле <limits.h>, предусмотренном ANSI, или сконфигурировать вместо этого командный файл. (А еще лучше написать программу, которая по самой своей природе нечувствительна к размерам переменных). Можно ли с помощью #if узнать, как организована память машины - по принципу: младший байт - меньший адрес или наоборот? Видимо, этого сделать нельзя. (Препроцессор использует для внутренних нужд только длинные целые и не имеет понятия об адресации). А уверены ли вы, что нужно точно знать тип организации памяти? Уж лучше написать программу, которая от этого не зависит. Во время компиляции мне необходимо сложное препроцесссирование, и я никак не могу придумать, как это сделать с помощью cpp cpp не задуман как универсальный препроцессор. Чем заставлять cpp делать что-то ему не свойственное, подумайте о написании небольшого специализированного препроцессора. Легко раздобыть утилиту типа make(1), которая автоматизирует этот процесс. Если вы пытаетесь препроцессировать что-то отличное от Си, воспользуйтесь универсальным препроцессором, (таким как m4).
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |