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

Существует ли static_warning?

Я знаю этот вопрос, в котором упоминается Boost "STATIC WARNING", но я хотел бы снова спросить, в частности, как я мог бы реализовать static_warning, который работает аналогично static_assert, но только генерирует предупреждение во время компиляции, а не прерывает ошибку компиляции.

Я хотел бы что-то подобное предложению Alexandrescu для статического утверждения в pre-С++ 11 дней, которое каким-то образом смогло напечатать некоторую полезную контекстуальную информацию как часть ошибки.

Было бы приемлемо потребовать, чтобы пользователь включил определенные стандартные предупреждения компилятора, чтобы эта конструкция работала (возможно, "неверное преобразование указателя" или "нарушает строгие правила псевдонимов" ) - любое предупреждение, которое должно быть частью нормального компиляция в любом случае может быть использована.

Короче говоря, я хочу, чтобы static_warning(false, "Hello world"); создавал предупреждение компилятора, которое должно каким-то образом включать строку "hello world" в предупреждающее сообщение. Возможно ли это, скажем, в GCC и MSVC, и как?

Я бы с радостью отдал небольшую награду за вознаграждение за любое особенно умное решение.


В качестве небольшого объяснения: у меня возникла идея подумать о этом вопросе: статическое предупреждение было бы полезным способом проследить процесс компиляции сложные специализированные шаблоны, которые в противном случае довольно сложно отлаживать. Статическое предупреждение можно использовать в качестве простого маяка для компилятора, чтобы испустить "Я сейчас компилирую эту часть кода".


Обновление. В идеале предупреждение будет запускаться в следующей настройке:

template <typename T> struct Foo
{
    static_warning(std::is_pointer<T>::value, "Attempting to use pointer type.");
    // ...
};

int main() { Foo<int> a; Foo<int*> b; }
4b9b3361

Ответ 1

Воспроизведение комментария Michael E:

#if defined(__GNUC__)
#define DEPRECATE(foo, msg) foo __attribute__((deprecated(msg)))
#elif defined(_MSC_VER)
#define DEPRECATE(foo, msg) __declspec(deprecated(msg)) foo
#else
#error This compiler is not supported
#endif

#define PP_CAT(x,y) PP_CAT1(x,y)
#define PP_CAT1(x,y) x##y

namespace detail
{
    struct true_type {};
    struct false_type {};
    template <int test> struct converter : public true_type {};
    template <> struct converter<0> : public false_type {};
}

#define STATIC_WARNING(cond, msg) \
struct PP_CAT(static_warning,__LINE__) { \
  DEPRECATE(void _(::detail::false_type const& ),msg) {}; \
  void _(::detail::true_type const& ) {}; \
  PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \
}

// Note: using STATIC_WARNING_TEMPLATE changes the meaning of a program in a small way.
// It introduces a member/variable declaration.  This means at least one byte of space
// in each structure/class instantiation.  STATIC_WARNING should be preferred in any 
// non-template situation.
//  'token' must be a program-wide unique identifier.
#define STATIC_WARNING_TEMPLATE(token, cond, msg) \
    STATIC_WARNING(cond, msg) PP_CAT(PP_CAT(_localvar_, token),__LINE__)

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

#line 1
STATIC_WARNING(1==2, "Failed with 1 and 2");
STATIC_WARNING(1<2, "Succeeded with 1 and 2");

struct Foo
{
  STATIC_WARNING(2==3, "2 and 3: oops");
  STATIC_WARNING(2<3, "2 and 3 worked");
};

void func()
{
  STATIC_WARNING(3==4, "Not so good on 3 and 4");
  STATIC_WARNING(3<4, "3 and 4, check");
}

template <typename T> struct wrap
{
  typedef T type;
  STATIC_WARNING(4==5, "Bad with 4 and 5");
  STATIC_WARNING(4<5, "Good on 4 and 5");
  STATIC_WARNING_TEMPLATE(WRAP_WARNING1, 4==5, "A template warning");
};

template struct wrap<int>;

GCC 4.6 (при уровне предупреждения по умолчанию) производит:

static_warning.cpp: In constructor ‘static_warning1::static_warning1()’:
static_warning.cpp:1:1: warning: ‘void static_warning1::_(const detail::false_type&)’ 
    is deprecated (declared at static_warning.cpp:1): Failed with 1 and 2 [-Wdeprecated-declarations]
static_warning.cpp: In constructor ‘Foo::static_warning6::static_warning6()’:
static_warning.cpp:6:3: warning: ‘void Foo::static_warning6::_(const detail::false_type&)’
    is deprecated (declared at static_warning.cpp:6): 2 and 3: oops [-Wdeprecated-declarations]
static_warning.cpp: In constructor ‘func()::static_warning12::static_warning12()’:
static_warning.cpp:12:3: warning: ‘void func()::static_warning12::_(const detail::false_type&)’ 
    is deprecated (declared at static_warning.cpp:12): Not so good on 3 and 4 [-Wdeprecated-declarations]
static_warning.cpp: In constructor ‘wrap<T>::static_warning19::static_warning19() [with T = int]’:
static_warning.cpp:24:17:   instantiated from here
static_warning.cpp:19:3: warning: ‘void wrap<T>::static_warning19::_(const detail::false_type&) [with T = int]’ 
    is deprecated (declared at static_warning.cpp:19): Bad with 4 and 5 [-Wdeprecated-declarations]

В то время как Visual С++ 2010 (at/W3 или выше) говорит:

warnproj.cpp(1): warning C4996: 'static_warning1::_': Failed with 1 and 2
warnproj.cpp(1) : see declaration of 'static_warning1::_'
warnproj.cpp(6): warning C4996: 'Foo::static_warning6::_': 2 and 3: oops
warnproj.cpp(6) : see declaration of 'Foo::static_warning6::_'
warnproj.cpp(12): warning C4996: 'func::static_warning12::_': Not so good on 3 and 4
warnproj.cpp(12) : see declaration of 'func::static_warning12::_'
warnproj.cpp(19): warning C4996: 'wrap<T>::static_warning19::_': Bad with 4 and 5
    with
    [
        T=int
    ]
warnproj.cpp(19) : see declaration of 'wrap<T>::static_warning19::_'
    with
    [
        T=int
    ]
warnproj.cpp(19) : while compiling class template member function 'wrap<T>::static_warning19::static_warning19(void)'
    with
    [
        T=int
    ]
warnproj.cpp(24) : see reference to class template instantiation 'wrap<T>::static_warning19' being compiled
    with
    [
        T=int
    ]

Clang++ 3.1 в Linux дает более приятный результат (цвет не показан):

tst3.cpp:1:1: warning: '_' is deprecated: Failed with 1 and 2
      [-Wdeprecated-declarations]
STATIC_WARNING(1==2, "Failed with 1 and 2");
^
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING'
  PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \
                                     ^
tst3.cpp:6:3: warning: '_' is deprecated: 2 and 3: oops
      [-Wdeprecated-declarations]
  STATIC_WARNING(2==3, "2 and 3: oops");
  ^
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING'
  PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \
                                     ^
tst3.cpp:12:3: warning: '_' is deprecated: Not so good on 3 and 4
      [-Wdeprecated-declarations]
  STATIC_WARNING(3==4, "Not so good on 3 and 4");
  ^
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING'
  PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \
                                     ^
tst3.cpp:19:3: warning: '_' is deprecated: Bad with 4 and 5
      [-Wdeprecated-declarations]
  STATIC_WARNING(4==5, "Bad with 4 and 5");
  ^
tst3.cpp:24:38: note: expanded from macro 'STATIC_WARNING'
  PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} \
                                     ^
tst3.cpp:23:17: note: in instantiation of member function
      'wrap<int>::static_warning19::static_warning19' requested here
template struct wrap<int>
                ^
4 warnings generated.

Ответ 2

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

Для этого требуется, чтобы предупреждение о неиспользуемой переменной включалось. В g++ это -Wunused-variable (включено -Wall), а в MSVC это предупреждение C4101, которое включено на уровне предупреждения 3.

Он явно не очень тестировался и может быть расширен несколькими способами (используйте __COUNTER__ вместо __LINE__ для поддерживаемых компиляторов, более красивую печать сообщений, используйте Boost для упрощения и т.д.), но, похоже, выполняет эту работу, Здесь котельная плита:

namespace detail
{
    template <bool Condition>
    struct static_warning;

    template <>
    struct static_warning<true>
    {
        template <typename Message>
        static void warn() {}
    };

    template <>
    struct static_warning<false>
    {
        // If you're here because of a warning, please see where the
        // template was instantiated for the source of the warning.
        template <typename Message>
        static void warn() { Message STATIC_WARNING_FAILED; }
    };
}

#define STATIC_WARNING_DETAIL_EX(cond, msg, line)                   \
        struct static_warning ## line                               \
        {                                                           \
            class msg {};                                           \
                                                                    \
            static_warning ## line()                                \
            {                                                       \
                ::detail::static_warning<(cond)>::                  \
                    warn<void************ (msg::************)()>(); \
            }                                                       \
        }

#define STATIC_WARNING_DETAIL(cond, msg, line) \
        STATIC_WARNING_DETAIL_EX(cond, msg, line)

// Use these:
#define STATIC_WARNING_MSG(cond, msg) \
        STATIC_WARNING_DETAIL(cond, msg, __LINE__)

#define STATIC_WARNING(cond) \
        STATIC_WARNING_DETAIL(cond, STATIC_WARNING_FAILED, __LINE__)

И тест:

STATIC_WARNING(sizeof(int) == 2);

int main()
{
    STATIC_WARNING_MSG(sizeof(char) != 1, JUST_KIDDING_ALL_IS_WELL);
}

В MSVC это производит:

>main.cpp(19): warning C4101: 'STATIC_WARNING_FAILED' : unreferenced local variable
>          main.cpp(45) : see reference to function template instantiation 'void detail::static_warning<false>::warn<void************(__thiscall static_warning45::STATIC_WARNING_FAILED::* ***********)(void)>(void)' being compiled
>main.cpp(19): warning C4101: 'STATIC_WARNING_FAILED' : unreferenced local variable
>          main.cpp(49) : see reference to function template instantiation 'void detail::static_warning<false>::warn<void************(__thiscall main::static_warning49::JUST_KIDDING_ALL_IS_WELL::* ***********)(void)>(void)' being compiled

И в GCC он производит:

main.cpp: In static member function 'static void detail::static_warning<false>::warn() [with Message = void************ (static_warning39::STATIC_WARNING_FAILED::************)()]':
main.cpp:39:1:   instantiated from here
main.cpp:19:38: warning: unused variable 'STATIC_WARNING_FAILED'
main.cpp: In static member function 'static void detail::static_warning<false>::warn() [with Message = void************ (main()::static_warning43::JUST_KIDDING_ALL_IS_WELL::************)()]':
main.cpp:43:5:   instantiated from here
main.cpp:19:38: warning: unused variable 'STATIC_WARNING_FAILED'

Ответ 3

Вот решение, использующее библиотеку Boost MPL:

#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/print.hpp>

#define static_warning_impl2(cond, msg, line) \
    struct static_warning_ ## line { \
        struct msg {}; \
        typedef typename boost::mpl::eval_if_c< \
            cond, \
            boost::mpl::identity<msg>, \
            boost::mpl::print<msg> \
        >::type msg ## _; \
    }

#define static_warning_impl1(cond, msg, line) \
    static_warning_impl2(cond, msg, line)

#define static_warning(cond, msg) \
    static_warning_impl1(cond, msg, __LINE__)

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

static_warning(sizeof(int) == 4, size_of_int_is_not_4);

и

static_warning(sizeof(int) == 2, size_of_int_is_not_2);

С MSVS 2010 первый тест компилируется без предупреждений, второй компилируется с предупреждением

C:\Libraries\Boost\boost_1_48_0\boost/mpl/print.hpp(51): warning C4308: negative integral constant converted to unsigned type
    C:\Libraries\Boost\boost_1_48_0\boost/mpl/eval_if.hpp(63) : see reference to class template instantiation 'boost::mpl::print<T>' being compiled
    with
    [
        T=static_warning_28::size_of_int_is_not_2
    ]
    Test.cpp(28) : see reference to class template instantiation 'boost::mpl::eval_if_c<C,F1,F2>' being compiled
    with
    [
        C=false,
        F1=boost::mpl::identity<static_warning_28::size_of_int_is_not_2>,
        F2=boost::mpl::print<static_warning_28::size_of_int_is_not_2>
    ]

В коде используется boost:: mpl:: print. Из книги С++ Template Metaprogramming Д. Абрахама и А. Гуртовой, стр. 171:

Чтобы создать журнал выполнения времени компиляции, нам нужен способ генерации диагностического сообщения - предупреждение. Поскольку нет единой конструкции, которая заставит все компиляторы генерировать предупреждение (действительно, большинство компиляторов позволяют вам вообще отключать предупреждения), MPL имеет метафункцию print, которая похожа на identity, за исключением того, что она настроена для создания предупреждения о множество популярных компиляторов с их обычными настройками.