Program Club

static_warning이 있습니까?

proclub 2020. 11. 18. 21:42
반응형

static_warning이 있습니까?


저는 Boost의 "STATIC WARNING"을 언급하는 이 질문 에 대해 알고 있지만, 구체적으로 static_warning유사하게 작동 static_assert하지만 컴파일 오류가 아닌 컴파일 타임 에만 경고내는 a 구현할 수있는 방법을 다시 묻고 싶습니다 .

나는 오류의 일부로 유용한 컨텍스트 정보를 어떻게 든 인쇄 할 수 있었던 pre-C ++ 11 일의 정적 주장에 대한 Alexandrescu의 제안과 유사한 것을 원합니다.

이 구성이 작동하려면 사용자가 특정 표준 컴파일러 경고를 활성화하도록 요구하는 것이 허용됩니다 ( "잘못된 포인터 변환"또는 "엄격한 앨리어싱 규칙 위반"). 어쨌든 일반 컴파일의 일부 여야하는 모든 경고는 사용됩니다.

간단히 말해, 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; }

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 : 생성자 'static_warning1 :: static_warning1 ()'에서 :
static_warning.cpp : 1 : 1 : 경고 : 'void static_warning1 :: _ (const detail :: false_type &)' 
    더 이상 사용되지 않습니다 (static_warning.cpp : 1에 선언 됨) : 1 및 2로 실패 함 [-Wdeprecated-declarations]
static_warning.cpp : 생성자 'Foo :: static_warning6 :: static_warning6 ()'에서 :
static_warning.cpp : 6 : 3 : 경고 : 'void Foo :: static_warning6 :: _ (const detail :: false_type &)'
    더 이상 사용되지 않습니다 (static_warning.cpp : 6에 선언 됨) : 2 및 3 : 죄송합니다 [-Wdeprecated-declarations]
static_warning.cpp : 생성자 'func () :: static_warning12 :: static_warning12 ()'에서 :
static_warning.cpp : 12 : 3 : 경고 : 'void func () :: static_warning12 :: _ (const detail :: false_type &)' 
    더 이상 사용되지 않습니다 (static_warning.cpp : 12에 선언 됨) : 3과 4에서는 좋지 않음 [-Wdeprecated-declarations]
static_warning.cpp : 생성자 'wrap <T> :: static_warning19 :: static_warning19 () [with T = int]':
static_warning.cpp : 24 : 17 : 여기에서 인스턴스화 됨
static_warning.cpp : 19 : 3 : 경고 : 'void wrap <T> :: static_warning19 :: _ (const detail :: false_type &) [with T = int]' 
    더 이상 사용되지 않습니다 (static_warning.cpp : 19에 선언 됨) : 4 및 5에 적합하지 않음 [-Wdeprecated-declarations]

Visual C ++ 2010 (/ W3 이상)에서는 다음과 같이 말합니다.

warnproj.cpp (1) : 경고 C4996 : 'static_warning1 :: _': 1과 2로 실패
warnproj.cpp (1) : 'static_warning1 :: _'선언 참조
warnproj.cpp (6) : 경고 C4996 : 'Foo :: static_warning6 :: _': 2 및 3 : 죄송합니다.
warnproj.cpp (6) : 'Foo :: static_warning6 :: _'선언 참조
warnproj.cpp (12) : 경고 C4996 : 'func :: static_warning12 :: _': 3과 4에서 좋지 않음
warnproj.cpp (12) : 'func :: static_warning12 :: _'선언 참조
warnproj.cpp (19) : 경고 C4996 : 'wrap <T> :: static_warning19 :: _': 4 및 5에서 잘못됨
    [
        T = int
    ]
warnproj.cpp (19) : 'wrap <T> :: static_warning19 :: _'선언 참조
    [
        T = int
    ]
warnproj.cpp (19) : 클래스 템플릿 멤버 함수 'wrap <T> :: static_warning19 :: static_warning19 (void)'를 컴파일하는 동안
    [
        T = int
    ]
warnproj.cpp (24) : 컴파일중인 클래스 템플릿 인스턴스화 'wrap <T> :: static_warning19'에 대한 참조 참조
    [
        T = int
    ]

Linux의 Clang ++ 3.1은 틀림없이 더 좋은 출력을 생성합니다 (색상 표시되지 않음).

tst3.cpp : 1 : 1 : 경고 : '_'는 더 이상 사용되지 않습니다. 1과 2로 실패했습니다.
      [-Wdeprecated-declarations]
STATIC_WARNING (1 == 2, "1과 2로 실패");
^
tst3.cpp : 24 : 38 : 참고 : 'STATIC_WARNING'매크로에서 확장 됨
  PP_CAT (static_warning, __ LINE __) () {_ (:: detail :: converter <(cond)> ());} \
                                     ^
tst3.cpp : 6 : 3 : 경고 : '_'는 더 이상 사용되지 않습니다. 2 및 3 : 죄송합니다.
      [-Wdeprecated-declarations]
  STATIC_WARNING (2 == 3, "2 및 3 : 죄송합니다");
  ^
tst3.cpp : 24 : 38 : 참고 : 'STATIC_WARNING'매크로에서 확장 됨
  PP_CAT (static_warning, __ LINE __) () {_ (:: detail :: converter <(cond)> ());} \
                                     ^
tst3.cpp : 12 : 3 : 경고 : '_'는 더 이상 사용되지 않습니다. 3과 4에서는 좋지 않습니다.
      [-Wdeprecated-declarations]
  STATIC_WARNING (3 == 4, "3과 4에서는 좋지 않음");
  ^
tst3.cpp : 24 : 38 : 참고 : 'STATIC_WARNING'매크로에서 확장 됨
  PP_CAT (static_warning, __ LINE __) () {_ (:: detail :: converter <(cond)> ());} \
                                     ^
tst3.cpp : 19 : 3 : 경고 : '_'는 더 이상 사용되지 않음 : 4 및 5에서 불량
      [-Wdeprecated-declarations]
  STATIC_WARNING (4 == 5, "4와 5에서 나쁨");
  ^
tst3.cpp : 24 : 38 : 참고 : 'STATIC_WARNING'매크로에서 확장 됨
  PP_CAT (static_warning, __ LINE __) () {_ (:: detail :: converter <(cond)> ());} \
                                     ^
tst3.cpp : 23 : 17 : 참고 : 멤버 함수 인스턴스화
      'wrap <int> :: static_warning19 :: static_warning19'요청
템플릿 구조체 wrap <int>
                ^
4 개의 경고가 생성되었습니다.

여기 내가 지금까지 생각 해낸 최고가 있습니다. 기본적이고 요구 사항에 맞지 않지만 대신 BOOST_MPL_ASSERT_MSG메시지가 유효한 식별자 형식을 취해야하는 경로로 이동합니다 . (내가 아는 한, 경고 메시지에 인쇄 된 문자열을 얻을 수있는 유일한 방법은 사용한 경고가 문자열과 관련된 경고이고 그 내용을 인쇄하는 경우입니다.)

사용하지 않는 변수에 대한 경고를 활성화해야합니다. g ++에서는 -Wunused-variable(으로 활성화 됨 -Wall)이고 MSVC에서는 경고 수준 3에서 활성화 된 경고 C4101입니다.

It's obviously not very tested and could be enhanced in a few ways (use __COUNTER__ instead of __LINE__ on supported compilers, prettier message printing, use Boost to simplify, etc.), but seems to get the job done. Here's the boiler-plate:

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__)

And a test:

STATIC_WARNING(sizeof(int) == 2);

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

In MSVC this produces:

>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

And in GCC it produces:

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'

Here is a solution that uses the Boost MPL library:

#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__)

It comes with the same restriction as GMan's solution: the message must be a valid identifier. Here are two tests

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

and

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

With MSVS 2010 the first test compiles without warnings, the second compiles with the warning

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>
    ]

The code uses boost::mpl::print. From the book C++ Template Metaprogramming by D. Abrahams and A. Gurtovoy, page 171:

To generate a compile-time execution log, we'd need a way to generate a diagnostic message - a warning. Because there's no single construct that will cause all compilers to generate a warning (indeed, most compilers let you disable warnings altogether), MPL has a print metafunction that is just like identity except that it is tuned to generate a warning on a variety of popular compilers with their usual settings.

참고URL : https://stackoverflow.com/questions/8936063/does-there-exist-a-static-warning

반응형