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

используйте «rb», чтобы открыть двоичный файл. Тогда байты файла не будут закодированы, когда вы их прочитаете

13
задан bhups 10 July 2010 в 16:06
поделиться

10 ответов

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

Наличие препроцессора текста, такого как cpp , является смешанным преимуществом. Поскольку cpp на самом деле не знает C , все, что он делает, это преобразовывает текст в другой текст. Это вызывает множество проблем с обслуживанием.Возьмем, к примеру, C ++, где многие варианты использования препроцессора явно устарели в пользу более совершенных функций, таких как:

  • Для констант, const вместо #define
  • Для небольших функций, inline вместо #define макросов

C ++ FAQ называет макросы злом и дает несколько причин, чтобы их не использовать.

9
ответ дан 25 October 2019 в 20:16
поделиться

Поскольку современные компиляторы достаточно умны, чтобы удалить мертвый код почти в любом случае, необходимость в ручном кормлении компилятора таким образом отпадает. То есть, вместо :

#include <iostream>

#define DEBUG

int main()
{
#ifdef DEBUG
        std::cout << "Debugging...";
#else
        std::cout << "Not debugging.";
#endif
}

вы можете сделать:

#include <iostream>

const bool debugging = true;

int main()
{
    if (debugging)
    {
        std::cout << "Debugging...";
    }
    else
    {
        std::cout << "Not debugging.";
    }
}

и, вероятно, получите тот же или, по крайней мере, похожий код.


Edit/Note: В C и C++ я бы абсолютно никогда не стал этого делать -- я бы использовал препроцессор, хотя бы потому, что он мгновенно дает понять читателю моего кода, что часть его не должна выполняться при определенных условиях. Я говорю, однако, что именно поэтому многие языки избегают препроцессора.

6
ответ дан 25 October 2019 в 20:16
поделиться

Многие современные языки на самом деле обладают возможностями синтаксического метапрограммирования, которые выходят далеко за рамки CPP. Практически все современные Лиспы (Arc, Clojure, Common Lisp, Scheme, newLISP, Qi, PLOT, MISC, ...), например, имеют чрезвычайно мощные (фактически, по Тьюрингу) макросистемы, так почему же должны ли они ограничиваться паршивыми макросами в стиле CPP, которые даже не являются настоящими макросами, а просто текстовыми фрагментами?

Другие языки с мощным синтаксическим метапрограммированием включают Io, Ioke, Perl 6, OMeta, Converge.

1
ответ дан 25 October 2019 в 20:16
поделиться

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

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

2
ответ дан 25 October 2019 в 20:16
поделиться

Из-за уменьшения размера двоичного файла:

  1. Можно сделать и другими способами (например, сравните средний размер исполняемого файла C ++ с исполняемым файлом C #).
  2. Это не так уж важно, если сравнить это с возможностью писать программы, которые действительно работают.
0
ответ дан 25 October 2019 в 20:16
поделиться

Другие языки также имеют лучшую динамическую привязку. Например, у нас есть код, который мы не можем отправить некоторым клиентам по причинам экспорта. Наши библиотеки «C» используют операторы #ifdef и тщательно продумывают трюки с Makefile (что почти то же самое).

В коде Java используются плагины (например, Eclipse), поэтому мы просто не отправляем этот код.

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

0
ответ дан 25 October 2019 в 20:16
поделиться

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

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

  • . Код может дублироваться в разных ветках #ifdef ], что затрудняет сохранение единственной истины о том, что происходит.

  • Когда приложение предназначено для нескольких платформ, становится очень трудно скомпилировать весь код, в отличие от любого кода, выбранного для платформы разработчика. Возможно, вам потребуется настроить несколько компьютеров. (Например, в системе BSD создание среды кросс-компиляции, которая точно имитирует заголовки GNU, обходится дорого.) В те дни, когда большинство разновидностей Unix были проприетарными и поставщики должны были поддерживать их все, эта проблема была очень серьезной. Сегодня, когда так много версий Unix бесплатны, это не проблема, хотя все еще довольно сложно дублировать собственные заголовки Windows в среде Unix.

  • Какой-то код защищен таким количеством #ifdef , что вы не можете понять, какая комбинация параметров -D необходима для выбора кода. Проблема является NP-сложной, поэтому наиболее известные решения требуют использования экспоненциально большого числа различных комбинаций определений.Это, конечно, непрактично, поэтому реальным следствием является то, что постепенно ваша система заполняется кодом, который не был скомпилирован . Эта проблема убивает рефакторинг, и, конечно же, такой код полностью невосприимчив к вашим модульным тестам и регрессионным тестам - если вы не создадите огромную мультиплатформенную ферму тестирования, а может быть, и тогда.

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

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

  • Для некоторых языков, таких как Java, все ] платформо-зависимый код находится в реализации JVM и в связанных библиотеках. Люди приложили огромные усилия, чтобы сделать JVM и библиотеки независимыми от платформы.

  • Во многих языках, таких как Haskell, Lua, Python, Ruby и многих других, дизайнеры приложили некоторые усилия, чтобы уменьшить объем платформенно-зависимого кода по сравнению с C.

  • В современном языке вы может помещать зависимый от платформы код в отдельную единицу компиляции за скомпилированным интерфейсом.Многие современные компиляторы имеют хорошие возможности для встраивания функций через границы интерфейса, так что вы не платите больших (или каких-либо) штрафов за такой вид абстракции. В случае C этого не произошло, потому что (а) нет отдельно скомпилированных интерфейсов; модель раздельной компиляции предполагает #include и препроцессор; и (б) компиляторы C достигли совершеннолетия на машинах с 64 КБ пространства кода и 64 КБ пространства данных; компилятор, достаточно сложный для встраивания через границы модуля, был почти немыслим. Сегодня такие компиляторы стали обычным делом. Некоторые продвинутые компиляторы встраивают и специализируют методы динамически .

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

7
ответ дан 25 October 2019 в 20:16
поделиться

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

Во многих языках ситуация, когда директивы условной компиляции могут быть лучше, чем код времени выполнения if-then-else, - это ситуация, когда утверждения времени компиляции (например, объявления переменных) должны быть условными. Например,

$if debug
array x
$endif
...
$if debug
dump x
$endif

объявляет/выделяет/компилирует x только тогда, когда требуется x, тогда как

array x
boolean debug
...
if debug then dump x

вероятно, придется объявлять x независимо от того, истинно ли значение debug.

2
ответ дан 25 October 2019 в 20:16
поделиться

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

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

2
ответ дан 25 October 2019 в 20:16
поделиться

Лучше задать вопрос: почему C прибегает к использованию препроцессора для реализации такого рода задач метапрограммирования? Это не столько особенность, сколько компромисс с технологиями того времени.

Директивы препроцессора в C были разработаны в то время, когда машинные ресурсы (скорость ЦП, ОЗУ) были дефицитными (и дорогими). Препроцессор предоставил возможность реализовать эти функции на медленных машинах с ограниченной памятью. Например, первая машина, которой я когда-либо владел, имела 56 КБ ОЗУ и процессор 2 МГц. В нем по-прежнему был доступен полный компилятор K&R C, который доводил ресурсы системы до предела, но был работоспособен.

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

1
ответ дан 25 October 2019 в 20:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: