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

Статическое утверждение, если это возможно, динамическое утверждение иначе?

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

Если все использования этой функции будут использовать постоянные целые числа, можно было бы сделать int параметром шаблона и использовать статическое утверждение, чтобы проверить, допустимо ли это. Поэтому вместо func(1,c) можно использовать func<1>(c) и получить проверку типа компиляции. Есть ли способ написать func(1,c) и по-прежнему держать проверку времени компиляции, а также писать func(i,c) и использовать динамическое утверждение? Цель состоит в том, чтобы сделать его прозрачным для разработчика. Было бы здорово добавить эту безопасность, не беспокоя разработчиков о таких вещах, как константы времени компиляции. Вероятно, они только помнят, что func(1,c) всегда работает и использует это, избегая проверки.

Как я могу определить функцию со статическим утверждением, когда это возможно, и динамическое утверждение иначе?


Следующий код показывает решение для GCC Иван Щербаков:

#include <iostream>
#include <cassert>

template<typename T>
void __attribute__((always_inline)) func(const int& i, const T& t);

void compile_time_error_() __attribute__((__error__ ("assertion failed")));

template<>
  void __attribute__((always_inline))
  func(const int& i, const float& t)
{
    do {
        if (i != 0) {
            if (__builtin_constant_p(i)) compile_time_error_();
            std::cerr << "assertion xzy failed" << std::endl;
            exit(1);
        }
    } while (0);
    func_impl<float>(i,t);
}

Это позволит только комбинацию я = 0 и T = float. Для других комбинаций хорошим способом было бы создание макроса, создающего код template<> func(const int& i, const T& t) с заменой T и я!= 0.

4b9b3361

Ответ 1

Ну, если вы используете GCC, вы можете использовать грязный взломать, но он будет работать только при включенной функции inlining (-O1 или более):

void my_error() __attribute__((__error__ ("Your message here")));

template <typename T1, typename T2> struct compare_types 
{
    enum {Equals = 0};
};

template <typename T1> struct compare_types<T1,T1> 
{
    enum {Equals = 1};
};

template <typename Type> __attribute__((always_inline)) void func(int a, Type &x)
{
    if (__builtin_constant_p(a))
    {
        if (a == 1 && compare_types<Type,char>::Equals)
            my_error();
    }
}

В этом случае, когда a == 1 и Type char, вы получите сообщение об ошибке. Вот пример, который вызовет его:

int main()
{
    char x;
    func(1, x);
    return 0;
}

Обратите внимание, что этот пример в значительной степени зависит от функции gcc-specific __builtin_constant_p() и не будет работать с другими компиляторами!

Ответ 2

Позвольте мне перефразировать вопрос, чтобы быть более точным в моем ответе:

Может ли runtime assert иногда сообщать об ошибках во время компиляции.

Gcc может, но только на некотором уровне оптимизации и сообщении об ошибке очень неинформативный. Сам Clang не может (нет атрибута ошибки), но делать не забывайте о clang анализаторе. Анализатор может сообщать о некоторых ошибках во время выполнения, таких как разыменование нулевого указателя.

Итак, вот идея и простой тест "умного" времени выполнения:

#include <cstdlib> // std::abort

#if !defined(__clang__) && defined(__GNUC__)
// clang emulates gcc
# define GCC_COMPILER 1
#else
# define GCC_COMPILER 0
#endif

#if GCC_COMPILER
void assertion_failed_message() __attribute__((__error__("assertion failed")));
#endif

inline void smart_assert(bool condition) {
#if GCC_COMPILER
  // gcc is very 'sensitive', it must be first code lines in this function
  if (__builtin_constant_p(condition) && !condition) {
    assertion_failed_message();
  }
#endif

  if (condition) {
    // Ok
    return;
  }

#if defined(__clang_analyzer__)
  enum {
    ASSERTION_FAILED = 0xdeadfull
  };
  int *a = nullptr;
  *a = ASSERTION_FAILED;
#endif

  // Implement some standart error, like abort
  std::abort();
}

void test_condition_2(bool condition) {
  smart_assert(condition);
}

void test_condition_1(bool condition) {
  test_condition_2(condition);
}

void test_condition_0(bool condition) {
  test_condition_1(condition);
}

int main() {
  test_condition_0(0==1);
  return EXIT_SUCCESS;
}

Ошибка отчета Gcc на уровне оптимизации O2, это хорошо. Сообщение о сообщении находится в основной функции и не оставляет никакой информации о test_condition_ {0,1,2}.

Ошибка отчета анализатора Clang, и если вы используете Xcode, вы можете увидеть весь путь от main до smart_assert: clang_analyzer_report

P.S. анализатор clang не идеален, поэтому, если вы попробуете test_condition_0 (argc), никакая ошибка не будет сообщена (истинно проверка времени выполнения), но если вы попытаетесь test_condition_0 (argc == 1), будет сообщено ложное срабатывание.