是否存在 static_warning?

2022-01-05 00:00:00 static c++ compiler-warnings

我知道 这个问题 提到了 Boost 的静态警告",但我会想再问一次,具体来说,我如何实现一个 static_warning,它的操作与 static_assert 类似,但只在编译时发出 warning 而不是中止编译错误.

我想要类似于 Alexandrescu 在 C++11 之前的几天提出的静态断言提案,它以某种方式设法打印了一些有用的上下文信息作为错误的一部分.

要求用户启用某些标准编译器警告以便此构造工作是可以接受的(可能是无效的指针转换"或违反严格的别名规则")――任何应该是正常的警告的一部分无论如何编译都可以使用.

简而言之,我希望 static_warning(false, "Hello world"); 创建一个编译器警告,该警告应该以某种方式在警告消息中包含字符串hello world".这在 GCC 和 MSVC 中可能吗,以及如何实现?

我很乐意为任何特别聪明的解决方案提供小额奖励.

<小时>

作为一点解释:我在考虑这个问题时有了这个想法:静态警告会很有用跟踪复杂模板特化的编译时过程的方法,否则很难调试.静态警告可以用作编译器的简单信标,以发出我现在正在编译这部分代码."

<小时>

更新.理想情况下,警告将在以下设置中触发:

template 结构体{static_warning(std::is_pointer<T>::value, "尝试使用指针类型.");//...};int main() { Foo一个;Foo乙;}

解决方案

播放 Michael E 的评论:

#if 已定义(__GNUC__)#define DEPRECATE(foo, msg) foo __attribute__((deprecated(msg)))#elif 定义(_MSC_VER)#define DEPRECATE(foo, msg) __declspec(deprecated(msg)) foo#别的#error 不支持此编译器#万一#define PP_CAT(x,y) PP_CAT1(x,y)#define PP_CAT1(x,y) x##y命名空间细节{结构真类型{};结构 false_type {};模板<int test>结构转换器:public true_type {};模板 <>结构转换器<0>:公共false_type {};}#define STATIC_WARNING(cond, msg) struct PP_CAT(static_warning,__LINE__) { 弃用(void _(::detail::false_type const&),msg) {};void _(::detail::true_type const& ) {};PP_CAT(static_warning,__LINE__)() {_(::detail::converter<(cond)>());} }//注意:使用 STATIC_WARNING_TEMPLATE 会以很小的方式改变程序的含义.//它引入了成员/变量声明.这意味着至少有一个字节的空间//在每个结构/类实例化中.STATIC_WARNING 应该是首选//非模板情况.//'token' 必须是程序范围内的唯一标识符.#define STATIC_WARNING_TEMPLATE(token, cond, msg) STATIC_WARNING(cond, msg) PP_CAT(PP_CAT(_localvar_, token),__LINE__)

宏可以在命名空间、结构和函数范围内调用.给定输入:

#line 1STATIC_WARNING(1==2, "1 和 2 失败");STATIC_WARNING(1<2, "1 和 2 成功");结构体{STATIC_WARNING(2==3, "2 和 3: 哎呀");STATIC_WARNING(2<3, "2 和 3 工作");};空函数(){STATIC_WARNING(3==4, "3 和 4 不太好");STATIC_WARNING(3<4, "3 and 4, check");}模板 结构包裹{typedef T 型;STATIC_WARNING(4==5, "4 和 5 不好");STATIC_WARNING(4<5, "4 和 5 很好");STATIC_WARNING_TEMPLATE(WRAP_WARNING1, 4==5, "模板警告");};模板结构包装;

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 中声明):Failed with 1 and 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 中声明):Bad with 4 and 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 and 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=整数]warnproj.cpp(19) :请参阅 'wrap::static_warning19::_' 的声明和[T=整数]warnproj.cpp(19) : 在编译类模板成员函数时'wrap<T>::static_warning19::static_warning19(void)'和[T=整数]warnproj.cpp(24) :请参阅对正在编译的类模板实例化 'wrap<T>::static_warning19' 的引用和[T=整数]

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' 在这里请求模板结构包装^生成了 4 个警告.

I'm aware of this question which mentions Boost's "STATIC WARNING", but I'd like to ask again, specifically, how I could implement a static_warning which operates similarly to static_assert but only emits a warning at compile time rather than an aborting compilation error.

I'd like something similar to Alexandrescu's proposal for a static assert in pre-C++11 days which somehow managed to print some useful contextual information as part of the error.

It would be acceptable to require that the user enable certain standard compiler warnings in order for this construction to work (perhaps "invalid pointer conversion" or "breaks strict aliasing rules") -- any warning that should be part of a normal compilation anyway can be used.

In short, I want static_warning(false, "Hello world"); to create a compiler warning that should somehow include the string "hello world" in the warning message. Is this possible, say in GCC and MSVC, and how?

I'd happily give out a small reward bounty for any particularly clever solution.


As a bit of explanation: I got the idea when thinking about this question: A static warning would be a useful way to trace through the compile-time process of complex template specializations, which are otherwise fairly hard to debug. A static warning could be used as a simple beacon for the compiler to emit "I'm now compiling this part of the code."


Update. Ideally, the warning would be triggered in the following setup:

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; }

解决方案

Playing off of Michael E's comment:

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

The macro can be invoked at namespace, structure, and function scope. Given the input:

#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 (at default warning level) produces:

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]

While Visual C++ 2010 (at /W3 or above) says:

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 on Linux produces the arguably nicer output (color not shown):

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.

相关文章