C ++ vs. D, Ada и Eiffel (ужасные сообщения об ошибках с шаблонами) - PullRequest
25 голосов
/ 03 апреля 2011

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

Мне интересно, является ли эта проблема общей для всех языков, которые поддерживают универсальное программирование? Или что-то не так с шаблонами C ++?

К сожалению, я не знаю другого языка, который бы поддерживал универсальное программирование (дженерики Java и C # слишком упрощены и не так мощны, как шаблоны C ++).

Итак, я спрашиваю вас, ребята: шаблоны D (Ada, Eiffel) (дженерики) тоже выдают такие ужасные сообщения об ошибках? И возможно ли иметь язык с мощной универсальной парадигмой программирования, но без уродливых сообщений об ошибках? И если да, то как эти языки решают эту проблему?

Редактировать: для downvoters. Я действительно люблю C ++ и шаблоны. Я не говорю, что шаблоны плохие. На самом деле я большой поклонник общего программирования и шаблонного метапрограммирования. Я просто спрашиваю, почему я получаю такие ужасные сообщения об ошибках от компиляторов.

Ответы [ 6 ]

15 голосов
/ 03 апреля 2011

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

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

Давайте проиллюстрируем распространенную ошибку: забыть точку с запятой.

struct CType {
  int a;
  char b;
}
foo
bar() { /**/ }

Хорошо, так что это не так, куда должна идти пропущенная точка с запятой?Ну, к сожалению, это неоднозначно, оно может идти до или после foo, потому что:

  • C считает нормальным объявление переменной с шага после определения struct
  • C считаетНормально не указывать тип возвращаемого значения для функции (в этом случае значение по умолчанию равно int)

Если мы рассуждаем, мы можем видеть, что:

  • если foo называет тип, то он принадлежит объявлению функции
  • , если нет, он, вероятно, обозначает переменную ... если, конечно, мы не сделали опечатку, и она должна была быть написана fool,это тип: /

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

Кажется, что люди, работающие над gcc больше заинтересованы в создании быстрого кода (и я имею в виду быстро, поиск последних тестов в gcc 4.6) и добавлении интересных функций (gcc уже реализует большинство - если не все - C ++ 0x)чем производить легко читаемые сообщения об ошибках.Можете ли вы их винить?Я не могу.

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

Некоторые приятные функции, в верхней части моей головы:

  • Краткие, но полные сообщения об ошибках, которые включают исходные диапазоны, чтобы точно указать, откуда произошла ошибка
  • Fix-It замечает, когда становится очевидным, что имелось в виду
  • В этом случае компилятор анализирует остальную часть файла, как если бы исправление уже было там, вместо выписывания строкна строчках
  • (недавние) избегайте включения стека включения для заметок, чтобы вырезать излишне
  • (недавние), пытаясь раскрыть только те типы параметров шаблона, которые фактически написал разработчик,и сохранение typedefs (таким образом, речь идет о std::vector<Name> вместо std::vector<std::basic_string<char, std::allocator<char>>, std::allocator<std::basic_string<char, std::allocator<char>> >, который имеет все значение)
  • (в последнее время) корректно восстанавливается в случаеотсутствует template в случае его отсутствия при вызове метода шаблона из другого метода шаблона

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

Они, конечно, не пришли бесплатно.

Теперь концепции должны (как правило) сделать нашу жизнь проще.Но они были в основном непроверенными, и поэтому было сочтено предпочтительным удалить их из проекта.Я должен сказать, что рад этому.Учитывая относительную инерцию C ++, лучше не включать функции, которые не были полностью пересмотрены, и концептуальные карты меня не очень порадовали.Похоже, они не восхищались Бьярном или Хербом, поскольку говорили, что будут переосмысливать Концепции с нуля для следующего стандарта.

14 голосов
/ 04 апреля 2011

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

Сообщения об ошибках шаблона C ++, с другой стороны, печально известны как романы об ошибках .Основное отличие, на мой взгляд, состоит в том, как C ++ выполняет создание шаблонов.Дело в том, что шаблоны C ++ гораздо более гибкие, чем дженерики Ada.Это настолько гибко, это почти как макропроцессор.Умные люди в Boost использовали это для реализации таких вещей, как лямбды и даже целые другие языки.

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

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

Так что да, сообщения об ошибках шаблона C ++ намного хуже, чем у Ada.

Теперь отладка - это совсем другая история ...

9 голосов
/ 03 апреля 2011

D имеет две функции для улучшения качества сообщений об ошибках шаблона: Ограничения и static assert.

// Use constraints to only allow a function to operate on random access 
// ranges as defined in std.range.  If something that doesn't satisfy this
// is passed, the compiler will error before even trying to instantiate
// fun().
void fun(R)(R range) if(isRandomAccessRange!(R)) {
    // Do stuff.
}


// Use static assert to check a high level invariant.  If 
// the predicate is false, the error message will be 
// printed and compilation will stop before a screen 
// worth of more confusing errors are encountered.
// This function takes any number of ranges to merge sort
// and the same number of temporary buffers to merge into.
void mergeSort(R...)(R ranges) {
    static assert(R.length % 2 == 0, 
        "Must have equal number of ranges to be sorted and temporary buffers.");

    static assert(allSatisfy!(isRandomAccessRange, R), 
        "All arguments to mergeSort must be random access ranges.");

    // Implementation
}
8 голосов
/ 04 апреля 2011

В статье Общее программирование описаны многие плюсы и минусы генериков на нескольких языках, включая Ада, в частности . Несмотря на отсутствие специализации шаблонов, все общие Ada экземпляры"эквивалентны объявлению экземпляра ... сразу после которого идет тело экземпляра". На практике сообщения об ошибках, как правило, появляются во время компиляции, и они обычно представляют собой известные нарушения безопасности типов.

4 голосов
/ 20 апреля 2011

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

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

2 голосов
/ 03 апреля 2011

Есть некоторые попытки улучшить сообщения об ошибках.Например, Clang уделяет большое внимание созданию более легко читаемых сообщений об ошибках компилятора.Я пользуюсь им только в течение короткого времени, но мой опыт этого пока был довольно положительным по сравнению с эквивалентными ошибками GCC.

...