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

Почему использование препроцессора менее распространено на других языках, чем C/С++/ObjC?

Я программист на Java и VB.Net около 4 лет, а программист на С# - около 6 месяцев. Я также использовал кучу динамических языков, таких как Perl, Python, PHP и JavaScript.

У меня никогда не было необходимости в препроцессоре.

Мой вопрос: почему вы видите такое широкое использование препроцессоров в C, С++ и Objective-C, но редко (или никогда) не видите его на таких языках, как Java, С# или Scala?

4b9b3361

Ответ 1

Я не знаю Objective-C, поэтому мой ответ будет о противопоставлении использования препроцессора в C и C++.

Препроцессор изначально был необходим для C по нескольким причинам. Если я правильно помню, изначально в C не было констант, поэтому для избежания магических чисел требовался #define. До 1999 года в C не было встроенных функций, поэтому снова #define использовался для создания макросов или "псевдофункций" для сохранения накладных расходов на вызов функции при сохранении структурированного кода. C также не во время выполнения или во время компиляции полиморфизма, так #ifdef были необходимы для условной компиляции. Компиляторы, как правило, не были достаточно умны, чтобы оптимизировать доступ к недоступному коду, поэтому, опять же, #ifdef использовался для вставки кода отладки или диагностики.

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

Несколько случаев, когда использование препроцессора в C++ приемлемо или даже необходимо, включают средства защиты для файлов заголовков, чтобы предотвратить многократное #ifdef __cplusplus и того же заголовка, #ifdef __cplusplus для использования одного и того же заголовка для обоих C и C++, __FILE__ и __LINE__ для регистрации и несколько других.

Препроцессор также часто используется для определений, специфичных для платформы, хотя C++ Gotchas от Stephen Dewhurst рекомендует иметь отдельные каталоги включения для определений, специфичных для платформы, и использовать их в отдельных конфигурациях сборки для каждой платформы.

Ответ 2

Причина, по которой вы не видите препроцессор, используемый в Java, С# или Scala, заключается в том, что в этих языках его, очевидно, нет.

Одним из распространенных применений препроцессора C является предоставление кода для платформы. Поскольку C (я включаю C++ и Objective-C здесь) - это язык низкого уровня, который должен напрямую взаимодействовать с операционной системой, в переносимом коде обязательно должны быть разные разделы кода, скомпилированные для разных операционных систем. Вы можете найти обширные примеры такого рода вещей в зрелой, очень переносимой базе кода, такой как zlib.

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

#ifdef WIN32
    closesocket(s);
#else
    close(s);
#endif

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

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

В Проекте и Развитии C++ Бьярн Страуструп заявил, что он хотел удалить зависимость от препроцессора в C++, но не смог.

Ответ 3

Каждому языку нужен механизм для отдельной компиляции. В идеале язык отличает интерфейсы от реализаций, а модуль зависит только от интерфейсов модулей, которые он экспортирует. (См., Например, Ada, Clu, Modula и т.д.)

C не имеет языковой конструкции для интерфейсов или реализаций. Поскольку жизненно важно, чтобы разные .c файлы совместно использовали один вид интерфейсов, дисциплина программирования развивалась с помощью объявления объявлений (то есть интерфейсов) в файлах .h и обмена этими объявлениями/интерфейсами с использованием текстового включения (#include). В принципе #define и #ifdef можно отказаться, но #include не удалось.

В настоящее время разработчики языка признают, что текстовое включение - это не способ запуска железной дороги, поэтому языки, как правило, работают либо с отдельными компилируемыми интерфейсами (Ada, Modula, OCaml), либо с помощью интерфейсов, созданных с помощью компилятора (Haskell), либо динамическими системами, которые гарантировать согласованность интерфейса (Java, Smalltalk). С таким механизмом нет необходимости в препроцессоре и множестве причин не иметь его (подумайте об исходном коде и отладке).

Ответ 4

Потому что дизайн и цели этих языков не совпадают.

C был создан с использованием препроцессора в качестве мощного инструмента, он использовался для реализации очень простых вещей (таких как защитные функции включения), и разработчики могли использовать его для оптимизации своего кода с помощью макросов или необязательно включать/исключать некоторые блоки кода в дополнение к другим вещам. С++ унаследовал большинство C-идиом, макросы больше не используются для скорости (потому что inline был введен), но он все еще используется для множества вещей, см. Сообщение Что представляют собой макросы препроцессора, подходящие для?

Ответ 5

Потому что Гослинг и Хейлсберг понимают опасения и технический долг, связанные с неправильным использованием предварительной обработки!

Ответ 6

Я не согласен с тем, что кажется консенсусом, когда этот cpp не нужен на современных языках. У меня много случаев, когда у меня есть 3 несколько разных версии одной и той же программы, и я хочу иметь возможность сделать кучу изменений для каждой версии. С CPP я могу поместить их все в #if #else блоки, и я могу определить #if на линии компиляции. в Java, мне нужно создать какой-то статический глобальный и инициализировать его во время компиляции. У меня никогда не было того, чтобы нормально работать.

Ответ 7

Предварительная обработка очень, очень распространена в мире Java. Это использовало для компенсации нехватки языка для адекватных встроенных объектов абстракции, что в противном случае привело бы к бесконечному скопированному и вставляемому шаблону.

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

Ответ 8

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

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

Ответ 9

Препроцессор в C и С++ имеет две разные функции

  • сбор файлов в процессе сборки - языки как Java et al. имеют свои собственные механизмы, такие как импорт для этого.

  • выполнение текстовых подстановок - это все еще необходимо в определенной степени в C, но С++ может сделать это (по большей части) лучше с помощью шаблонов

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

Ответ 10

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

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

Ответ 11

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

Ответ 12

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

С# копирует (или наследует) большую часть дизайнерского решения от Java.

Языки программирования более высокого уровня избегают подобных артефактов низкого уровня.