2012-04-23 2 views
12

Ранее я спросил о function overloading based on whether the arguments are constexpr. Я пытаюсь обойти неутешительный ответ на этот вопрос, чтобы сделать более умную функцию утверждения. Это примерно то, что я пытаюсь сделать:constexpr, static_assert и inlining

inline void smart_assert (bool condition) { 
    if (is_constexpr (condition)) 
     static_assert (condition, "Error!!!"); 
    else 
     assert (condition); 
} 

В принципе, идея заключается в том, что проверка во время компиляции всегда лучше, чем проверка времени выполнения, если это возможно, чтобы проверить во время компиляции. Однако из-за таких вещей, как inlining и постоянное складывание, я не всегда знаю, возможна ли проверка времени компиляции. Это означает, что могут быть случаи, когда assert (condition) компилируется до assert(false), и код просто ждет меня, чтобы запустить его и выполнить этот путь до того, как я выясню, что есть ошибка.

Таким образом, если бы был какой-то способ проверить, является ли условие constexpr (из-за встраивания или других оптимизаций), я мог бы позвонить по телефону static_assert, когда это возможно, и, в случае необходимости, вернуться к заявке. К счастью, gcc имеет встроенный __builtin_constant_p (exp), который возвращает true, если exp является constexpr. Я не знаю, есть ли у других компиляторов это внутреннее, но я надеялся, что это решит мою проблему. Это код, который я придумал:

#include <cassert> 
#undef IS_CONSTEXPR 

#if defined __GNUC__ 
    #define IS_CONSTEXPR(exp) __builtin_constant_p (exp) 
#else 
    #define IS_CONSTEXPR(exp) false 
#endif 
// TODO: Add other compilers 

inline void smart_assert (bool const condition) { 
    static_assert (!IS_CONSTEXPR(condition) or condition, "Error!!!"); 
    if (!IS_CONSTEXPR(condition)) 
     assert (condition); 
} 

#undef IS_CONSTEXPR 

static_assert основывается на короткое замыкание в or. Если значение IS_CONSTEXPR истинно, то можно использовать static_assert, а условие равно !true or condition, что равно condition. Если IS_CONSTEXPR является ложным, то static_assert не может быть использован, и условие равно !false or condition, что точно так же, как true, а static_assert игнорируется. Если static_assert не может быть проверен, потому что condition не является constexpr, то я добавляю во время выполнения assert в мой код как последнее усилие. Однако это не работает, благодаря not being able to use function arguments in a static_assert, even if the arguments are constexpr.

В частности, это то, что произойдет, если я пытаюсь скомпилировать с помощью GCC:

// main.cpp 
int main() { 
    smart_assert (false); 
    return 0; 
} 

g++ main.cpp -std=c++0x -O0

Все нормально, компилирует нормально. Нет никакой вставки без оптимизации, поэтому IS_CONSTEXPR является ложным, а static_assert игнорируется, поэтому я просто получаю инструкцию assert (что не получается). Тем не менее,

[[email protected] test]$ g++ main.cpp -std=c++0x -O1 
In file included from main.cpp:1:0: 
smart_assert.hpp: In function ‘void smart_assert(bool)’: 
smart_assert.hpp:12:3: error: non-constant condition for static assertion 
smart_assert.hpp:12:3: error: ‘condition’ is not a constant expression 

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

Это не похоже на создание smart_assert Функциональный макрос решит проблему в общем случае. Очевидно, что он будет работать в этом простом примере, но condition, возможно, пришел из функции на два уровня вверх по графику вызовов (но все равно становится известен компилятору как constexpr из-за inlining), который сталкивается с той же проблемой использования параметр функции в static_assert.

+0

можно написать макрос, который определяет, является ли выражение константы в стандартном C++ –

+0

@ JohannesSchaub-LITB не будет ли такой макрос необходимость расширения для определения класса ? Это не будет использоваться внутри функции 'constexpr'. – Potatoswatter

+0

@pot, зачем ему нужно расширять до определения класса? –

ответ

7

Явный хорошо, подразумевается плохо, в общем.

Программист всегда может попробовать static_assert.

Если условие не может быть оценено во время компиляции, это не сработает, и программисту необходимо изменить на assert.

Вы можете сделать это проще, предоставив общую форму, чтобы изменение сводилось к, например, STATIC_ASSERT(x+x == 4)DYNAMIC_ASSERT(x+x == 4), просто переименование.

Тем не менее, так как в вашем случае, если вы хотите только оптимизацию программиста ’ с Время если оптимизация возможна, т.е., так как вы, вероятно дона ’ т заботиться о том, чтобы одни и те же результаты, всегда со всеми компиляторами, вы могли бы всегда пытайтесь что-то вроде & hellip;

#include <iostream> 
using namespace std; 

void foo(void const*) { cout << "compile time constant" << endl; } 
void foo(...) { cout << "hm, run time,,," << endl; } 

#define CHECK(e) cout << #e << " is "; foo(long((e)-(e))) 

int main() 
{ 
    int x = 2134; 
    int const y  = 2134; 

    CHECK(x); 
    CHECK(y); 
} 

Если да, то сообщите нам, как это произошло.

Примечание: приведенный выше код дает разные результаты с MSVC 10.0 и g ++ 4.6.


Update: Я задавался вопросом, как комментарий о том, как код выше работ, получил так много upvotes. Я подумал, может быть, он говорит то, чего я просто не понимаю. Поэтому я приступил к работе OP, проверяя, как эта идея была достигнута.

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

Вышеуказанное, насколько я получил поддержку g ++. Это хорошо работает (решает проблему OP) для Visual C++, используя идею, которую я представил. Но не с г ++:

#include <assert.h> 
#include <iostream> 
using namespace std; 

#ifdef __GNUC__ 
    namespace detail { 
     typedef double (&Yes)[1]; 
     typedef double (&No)[2]; 

     template< unsigned n > 
     Yes foo(char const (&)[n]); 

     No foo(...); 
    } // namespace detail 

    #define CASSERT(e)          \ 
     do {             \ 
      char a[1 + ((e)-(e))];        \ 
      enum { isConstExpr = sizeof(detail::foo(a)) == sizeof(detail::Yes) }; \ 
      cout << "isConstExpr = " << boolalpha << !!isConstExpr << endl; \ 
      (void)(isConstExpr? 1/!!(e) : (assert(e), 0)); \ 
     } while(false) 
#else 
    namespace detail { 
     struct IsConstExpr 
     { 
      typedef double (&YesType)[1]; 
      typedef double (&NoType)[2]; 

      static YesType check(void const*); 
      static NoType check(...); 
     }; 
    } // namespace detail 

    #define CASSERT(e)           \ 
     do {              \ 
      enum { isConstExpr =         \ 
       (sizeof(detail::IsConstExpr::check(e - e)) == \ 
        sizeof(detail::IsConstExpr::YesType)) };  \ 
      (void)(isConstExpr? 1/!!(e) : (assert(e), 0));  \ 
     } while(false) 
#endif 

int main() 
{ 
#if defined(STATIC_TRUE) 
    enum { x = true }; 
    CASSERT(x); 
    cout << "This should be displayed, OK." << endl; 
#elif defined(STATIC_FALSE) 
    enum { x = false }; 
    CASSERT(x); 
    cerr << "!This should not even have compiled." << endl; 
#elif defined(DYNAMIC_TRUE) 
    bool x = true; 
    CASSERT(x); 
    cout << "This should be displayed, OK." << endl; 
#elif defined(DYNAMIC_FALSE) 
    bool x = false; 
    CASSERT(x); 
    cout << "!Should already have asserted." << endl; 
#else 
    #error "Hey, u must define a test case symbol." 
#endif 
} 

Пример задачи с г ++:

 
[D:\dev\test] 
> g++ foo.cpp -Werror=div-by-zero -D DYNAMIC_FALSE 

[D:\dev\test] 
> a 
isConstExpr = true 
!Should already have asserted. 

[D:\dev\test] 
> _ 

То есть, отчеты г ++ (даже через его внутренней функции, и даже WRT создание VLA или нет.), Что не переменная const, которая знает значение, является постоянной, но тогда она не может применить это знание для целочисленного деления, так что тогда оно не будет выдавать предупреждение.

Argh.


Update 2: Ну, я немой конечно макрос может просто добавить обычный assert иметь там в любом случае. Поскольку OP заинтересован только в получении статического утверждения , когда он доступен, который не предназначен для g ++ в некоторых угловых случаях. Проблема решена и была решена изначально.

+7

g ++ имеет агрессивную константу, сложенную прямо в анализатор/семантический анализ, что означает, что '(e) - (e)' немедленно оценивается как '0' и поэтому считается пригодным для использования в качестве значения нулевого указателя ... хотя семантически это 'int'. Однако Clang не сбрасывает случай 'x', а' int' не может быть неявно преобразован в значение указателя, поэтому 'x' не является. –

+0

@downvoter: объясните, пожалуйста, ваш downvote, чтобы другие могли его игнорировать. –

+0

@MatthieuM .: да, обнаружив, что здесь весь смысл, какой код предназначен для этого. Я не понимаю, почему люди поддержали ваш комментарий, если только они не являются детьми Reddit. это безумно. –

1

Как ответ на другой вопрос разочаровывает? Он практически точно описывает то, что вы сейчас описываете, за исключением того, как компилятор печатает текст в диагностическом сообщении.

Причина, по которой это необходимо сделать с throw, заключается в том, что оценка времени компиляции constexpr в контексте, который может быть оценен во время выполнения, является необязательной.Например, реализация может позволить вам пройти через код constexpr в режиме отладки.

constexpr - слабый атрибут функций (спецификатор декларации), который не может изменить полученное значение выражения с использованием функции. Это гарантия того, что семантическое значение во время выполнения фиксировано во время компиляции, но не позволяет вам указывать специальную комбинацию времени компиляции.

В помеченного недействительные условия, throw является Подвыражением, который является недействительным как постоянным выражение, за исключением того, когда скрытые в невычисленной стороне ?:, && или ||. Язык гарантирует, что это будет отмечено во время компиляции, даже если отладчик позволяет вам пройти через него во время выполнения и только если флаг действительно запущен.

Язык имеет все здесь. К сожалению, это не может быть согласовано со специальной функцией диагностического сообщения static_assert или ветвлением на constexpr -ness.

+0

Должно быть, я что-то неправильно понимаю, потому что я не понимаю, как какой-либо другой ответ решил мою проблему. Я не вижу, как использование throw решает мою проблему, потому что я все еще не могу использовать 'static_assert' здесь, что является целым. Без 'static_assert', все, что у меня есть, является обычным' assert'/'throw'. –

8

Это должно помочь вам начать

template<typename T> 
constexpr typename remove_reference<T>::type makeprval(T && t) { 
    return t; 
} 

#define isprvalconstexpr(e) noexcept(makeprval(e)) 
+0

visual C++ еще не поддерживает 'constexpr' (начиная с sep 2012, с visual C++ 11.0). таким образом, пока это немного непрактично ... –

+3

Очень красивый Йоханнес. Почему это работает? Является ли каждый constexpr ссылкой на rvalue? Почему имя? Что такое pr? сделать? Вэла? Разве noeccept возвращает bool? Хорошо, я все равно посмотрю, но любая разработка была бы с благодарностью воспринята. В нем есть преимущество:) – user2023370

+1

Это не работает с clang. Вы уверены, что это правильно? – user2023370

Смежные вопросы