Отладка экземпляров шаблонов

#c #templates #debugging #generics

#c #c 11

Вопрос:

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

Например, допустим, я создал некоторый простой код, подобный следующему:

 template<typename T, typename R = void>
struct int_return_type {};

template<typename R>
struct int_return_type<int, R>
{
    typedef R type;
};

template<typename T, typename R = void>
struct float_return_type {};

template<typename R>
struct float_return_type<float, R> 
{
    typedef R type;
};

template<typename T>
typename int_return_type<T>::type test()
{
    cout << "T type is int" << endl;
}

template<typename T>
typename float_return_type<T>::type test()
{
    cout << "T type is float" << endl;
}

int main()
{
    test<int>();
    test<float>();
    return 0;
}
 

Я знаю, что это относительно простой код для выполнения, но шаблоны могут быть немного сложнее, особенно при выполнении метапрограммирования, рекурсии и т.д. Я понимаю, что компилятор будет выдавать сообщения об ошибках, которые можно использовать для определения того, как создаются экземпляры шаблонов, но мне также интересно, что можно сделать, когда фактический код шаблона корректен в синтаксическом смысле, но результаты выполнения по-прежнему неверны. Было бы неплохо, например, иметь метод для остановки компилятора и просмотра того, с чем test , а также int_return_type и float_return_type , создавался экземпляр, или какие экземпляры терпели неудачу.

Доступны ли сейчас единственные варианты отладки шаблонов с таким уровнем детализации: 1) сообщения об ошибках компилятора, когда код неверен, и 2) комбинация дизассемблеров и отладчиков, чтобы увидеть, какой экземпляр кода был сгенерирован, если результаты во время выполнения неверны? Или есть какие-то другие утилиты, которые помогают «наблюдать» за тем, как создаются экземпляры шаблонов, и видеть / проверять, какой код генерируется компилятором для расследования и отладки ошибок шаблона?

Комментарии:

1. static_assert я не думаю, что есть что-то, что может помочь, кроме как превращать вещи, которые вы хотите просмотреть, в ошибки

Ответ №1:

Они довольно простые, но в большинстве случаев они работали для меня. Мне интересно посмотреть, что скажут другие.

Прошу прощения за надуманные примеры.

Используйте песочницы

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

Укажите временные типы

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

 template<typename T>
  T calc(const T amp;val) {
    return some_other_calc(val) / 100.0;
  }
 

Указание компилятору, какой тип вы ожидаете, приведет к сбою быстрее и, возможно, даст вам лучшее сообщение для обработки.

 template<typename T>
  T calc(const T amp;val) {
    T val_ = some_other_calc(val);
    return val_ / 100.0;
  }
 

Используйте typeid

Используется typeid(T).name() для печати имен шаблонов в отладочных операторах. Это даст вам строку, которую вы можете использовать, чтобы увидеть, как компилятор решил выполнить тип.

 template<typename T>
  typename void test() {
    std::cout << "testing type " << typeid(T).name() << std::endl;
    // ...
  }
 

Избегайте ненужных реализаций по умолчанию

Пишите шаблоны таким образом, чтобы у них не было реализаций по умолчанию.

 template<typename T, bool is_integral = boost::is_numeric<T>::value >
  struct my_traits;

template<typename T>
  struct my_traits<T, true> {
    typedef uint32_t cast_type;
  };

template<typename T>
  void print_whole_number(T amp;val) {
    std::cout << static_cast<my_traits<T>::cast_type>(val) << std::endl;
  }
 

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

Комментарии:

1. 1 Напишите шаблоны таким образом, чтобы у них не было реализаций по умолчанию

Ответ №2:

Да, существует шаблонный отладчик метапрограммирования. Темплайт

Комментарии:

1. Хотя, похоже, теперь он мертв:(

2. Однако Metashell все еще находится в активной разработке: github.com/metashell/metashell

Ответ №3:

Мне нравится использовать отличный веб-компилятор Comeau для отладки. Он может замечать ошибки с точки зрения стандартной компиляции там, где другие компиляторы не могут…

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

Кроме того, не забывайте использовать static_assert ‘s везде, где это возможно, даже если вы уверены, что ответ верен.

Комментарии:

1. Кстати, я знаю, что это немного не по теме, но мне любопытно, мой пример кода, похоже, компилируется просто отлично , иначе я бы его не опубликовал (нет ничего лучше публикации кода, который не будет компилироваться)… какие настройки в компиляторе Comeau вы использовали?

2. @Jason, строгий режим с расширениями C 0x.

3. Никаких проблем … Я понимаю, что вы подразумеваете под более подробными сообщениями об ошибках. Я также заметил шаблон, аналогичный сообщениям об ошибках clang . Есть ли у вас какие-нибудь мысли по поводу этого компилятора? Кроме того, если я решил отказаться от gcc , есть ли какие-либо другие недостатки, такие как уменьшенная интеграция с утилитами цепочки инструментов GNU, такими как binutils , gdb , и т. Д.?

4. @Jason: Clang создает код, который должен быть совместим с gdb, если это не так, вы можете сообщить об ошибке. Его сообщения об ошибках с шаблоном и макросами предназначены для чтения, и (на данный момент) ведется работа над получением функции «разница в шаблонах»; то есть возможность указать пользователю точную разницу в двух типах шаблонов, которые не совпадают.

5. Веб-сайт компилятора Comeau не работает, так что это больше не вариант.

Ответ №4:

В 2018 году у нас есть cppinsights.io . Хотя я не уверен, насколько это полезно для действительно сложных шаблонов.

Комментарии:

1. Он основан на clang, что значительно отстает от последнего стандарта.