Подтвердить что ты не робот

Почему нужно беспокоиться о директивах препроцессора?

Этот вопрос может показаться довольно простым, но, исходя из инженерного (не-компьютерного) фона, я не был уверен в том, что фрагменты "# были в некотором коде на С++.

Быстрый поиск привел меня к краткой, хорошо объясненной странице cplusplus по директивам препроцессора.

Но зачем вообще обсуждать концепцию предпроцессорных директив? Невозможно ли написать эквивалентный код, который может присваивать значения константам, определять подпрограммы/функции/макросы и обрабатывать ошибки?

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

4b9b3361

Ответ 1

Вы используете директивы препроцессора, когда вам нужно что-то делать вне сферы действия реального приложения. Например, вы увидите, что выполняется предварительная обработка, включающая или не включающую код на основе архитектуры, для которой создается исполняемый файл. Например:

#ifdef _WIN32 // _WIN32 is defined by Windows 32 compilers
#include <windows.h>
#else
#include <unistd.h>
#endif

Директивы препроцессора также используются для защиты, так что классы/функции и т.д. не определены более одного раза.

#ifndef MY_CLASS_6C1147A1_BE88_46d8_A713_64973C9A2DB7_H_
#define MY_CLASS_6C1147A1_BE88_46d8_A713_64973C9A2DB7_H_
    class MyClass {
    ....
    };
#endif

Другое использование - для внедрения версий внутри кода и библиотек.

В Makefile у вас есть что-то по строкам:

-D MY_APP_VERSION=4.5.1

Пока в коде есть

cout << "My application name version: " << MY_APP_VERSION << endl;

Ответ 2

Ответ 1: условный код, который должен меняться в зависимости от того, на каком компьютере он работает.

Ответ 2: включение и отключение языковых расширений и функций совместимости во время компиляции.

Препроцессор пришел из C, где было много чего вы не могли выразить. Хороший код на С++ находит меньше причин использовать его, чем код C, но, к сожалению, он не совсем бесполезен.

Ответ 3

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

Обратите внимание, что препроцессор C является довольно грубым механизмом для такого рода вещей; Система шаблонов С++ обеспечивает гораздо более мощную структуру для компиляции времени построения кода. Другие языки обладают еще более мощными функциями метапрограммирования (например, макросистемой Lisp).

Ответ 4

Он используется чаще всего для двух вещей, которые сложнее организовать без него:

Ответ 5

Предварительная обработка происходит до компиляции кода. Он подходит в следующих случаях:

#ifdef WIN32
#include <something.h>
#elseif LINUX
#include <somethingelse.h>
#endif

Очевидно, включая заголовочные файлы, которые вы хотите сделать во время компиляции, а не во время выполнения. Мы не можем делать это с помощью переменных.

С другой стороны. С С++ это хорошая практика и очень рекомендуется заменять постоянные выражения, как в следующем примере

#define PI 3.141592654
with
const double PI=3.141592654;

Причина в том, что вы получаете правильное управление типом и обработкой данных.

Кроме

#define MAX(x,y) (x) > (y) ? (x) : (y)

Не очень приятно, потому что вы можете написать

int i = 5
int max = MAX(i++, 6);

Препроцессор заменит это следующим образом:

int max = (i++) > (6) ? (i++) : (6);

Это явно не даст ожидаемого результата.

Вместо этого MAX должен быть функцией (а не макросом). Если это функция, она также может предоставить тип в параметре.

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

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

Ответ 6

Как правило, препроцессорные директивы не должны использоваться. К сожалению, иногда вам приходится работать на C и С++.

C изначально определял язык таким образом, что вы действительно не могли сделать с ним ничего серьезного, не используя препроцессор. Язык не имел никакой другой встроенной поддержки для создания модульных программ, констант, встроенного кода или для создания общего программирования.

С++ избавляется от большинства из этих проблем, но средство все еще существует, поэтому оно все еще используется. (Интересно, что это не модуляция. Мы все еще придерживаемся #include),

Если вы хотите сравнить язык, построенный на аналогичном уровне абстракции для подобных задач, у которого нет препроцессора, посмотрите Ada.

Ответ 7

Многие языки программирования имеют мета-программирование, где вы пишете код для компилятора, а не среду выполнения.

Например, в С++ у нас есть шаблоны, которые позволяют нам инструктировать компилятор для генерации определенного кода на основе типа или даже константы времени компиляции. Lisp - это, пожалуй, самый известный пример языка с передовыми средствами мета-программирования.

Директивы/макросы препроцессора C - это еще одна форма "метапрограммирования", хотя и относительно грубая форма, чем доступно на других языках. Директивы препроцессора инструктируют компилятор делать определенные вещи во время компиляции, например, игнорировать определенный код на определенных платформах или находить и заменять строку в коде другой строкой. Это невозможно сделать во время выполнения, после того как ваш код уже скомпилирован.

Таким образом, C-препроцессор является ранней формой "метапрограммирования" или программирования компилятора.

Ответ 8

Нет, на самом деле невозможно обойтись без препроцессора во всех случаях. Один из моих любимых макросов -

#define IFDEBUG if(DEBUG==1)
//usage:
IFDEBUG{
  printf("Dump of stuff:.,,,");
}else{
  //..do release stuff
}

Без макросов у меня было бы (возможно много) пространство в финальном исполняемом файле

А также вы должны понимать, что C/С++ не имеет какого-либо типа пакета require или другой такой системы. Таким образом, без препроцессора невозможно предотвратить дублирование кода. (файлы заголовков не могут быть включены)

Ответ 9

Но зачем вообще обсуждать концепцию предпроцессорных директив? Невозможно ли написать эквивалентный код, который может присваивать значения константам, определять подпрограммы/функции/макросы и обрабатывать ошибки?

Его использование очень мало на С++, функции языка были созданы, чтобы избежать проблем, связанных с препроцессором.

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

В общих источниках С++ это часто рассматривается как плохая практика - особенно когда есть среднее значение для этого, используя другие языковые функции. Это необходимо для некоторых вещей (например, программ, зависящих от платформы/сборки и генерирующих программ). Короче говоря, обычно есть замена, которая хорошо масштабируется. (такие как константа определяют как перечисление или встроенные шаблоны вместо макросов). Если вы обнаружите, что используете один, и вы не уверены, тогда просто спросите/выполните поиск, если есть лучший способ объявить this_code_snippet в С++ без препроцессора.

Ответ 10

Немного истории здесь: С++ был разработан с C, который требовал препроцессора намного больше, чем С++. Например, чтобы определить константу в С++, вы должны написать что-то вроде const int foo = 4;, например, вместо #define FOO 4, которое является грубым эквивалентом C. К сожалению, слишком много людей использовали свои препроцессорные привычки от C до С++.

Существует несколько разумных применений препроцессора в С++. Использование #include для файлов заголовков в значительной степени необходимо. Он также полезен для условной компиляции, включая заголовок, включая защитные устройства, поэтому он может #include заголовок несколько раз (например, в разных заголовках) и обрабатывать его только один раз. Оператор assert на самом деле является макросом препроцессора, и существует несколько подобных применений.

Кроме того, в С++ существует несколько законных применений.

Ответ 11

Это лучше, чем ничего, чтобы получить некоторые возможности отражения из С++.

Очень полезно создавать переменные и строки с одинаковыми именами.

Ответ 13

Препроцессор C выполняет ряд задач, некоторые, но не все из которых имеют лучшие альтернативы в С++. Где С++ лучше использует его. Эти альтернативы включают в себя шаблоны, inlining и const переменные (оксюморон, но это то, что их называет стандарт) вместо макросов #define.

Однако есть несколько вещей, от которых вы не хотели бы обойтись или просто не могли обойтись; #include, например, имеет важное значение, и при кодировании для нескольких платформ или конфигураций условная компиляция остается полезной (хотя ее следует использовать экономно во всех случаях).

Конкретные расширения, управляемые с помощью #pragma, могут быть неизбежными в некоторых случаях.