Отладка шаблонов - PullRequest
       3

Отладка шаблонов

48 голосов
/ 07 сентября 2011

При выполнении метапрограммирования с использованием шаблонов 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) комбинация дизассемблеров и отладчиков, чтобы увидеть, какой экземпляр кода был сгенерирован, если результаты времени неверны? Или есть какие-то другие утилиты, которые помогают «наблюдать» за тем, как создаются экземпляры шаблонов, и видеть / проверять, какой код генерируется компилятором для расследования и отладки ошибок шаблонов?

Ответы [ 4 ]

29 голосов
/ 07 сентября 2011

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

Извините за надуманные примеры.

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

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

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

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

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

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

template<typename T>
  T calc(const T &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 &val) {
    std::cout << static_cast<my_traits<T>::cast_type>(val) << std::endl;
  }

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

8 голосов
/ 01 февраля 2015

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

3 голосов
/ 07 сентября 2011

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

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

0 голосов
/ 09 октября 2018

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...