Ker*_* SB 65 c++ static compiler-warnings
我知道这个问题提到了Boost的"静态警告",但我想再问一次,具体来说,我是如何实现一个static_warning操作方式类似static_assert但只在编译时发出警告而不是中止编译错误.
我想要类似于Alexandrescu关于在C++之前的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; }
Run Code Online (Sandbox Code Playgroud)
Man*_*agu 46
播放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__)
Run Code Online (Sandbox Code Playgroud)
可以在命名空间,结构和函数范围调用宏.鉴于输入:
#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>;
Run Code Online (Sandbox Code Playgroud)
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 C++ 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
]
Linux上的Clang ++ 3.1产生了可以说是更好的输出(颜色未显示):
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.
GMa*_*ckG 14
这是迄今为止我提出的最好的.它是基本的,并不完全符合您的要求,而是走的路线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__)
Run Code Online (Sandbox Code Playgroud)
并测试:
STATIC_WARNING(sizeof(int) == 2);
int main()
{
STATIC_WARNING_MSG(sizeof(char) != 1, JUST_KIDDING_ALL_IS_WELL);
}
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
在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'
Run Code Online (Sandbox Code Playgroud)
这是使用 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__)
Run Code Online (Sandbox Code Playgroud)
它与 GMan 的解决方案具有相同的限制:消息必须是有效的标识符。这是两个测试
static_warning(sizeof(int) == 4, size_of_int_is_not_4);
Run Code Online (Sandbox Code Playgroud)
和
static_warning(sizeof(int) == 2, size_of_int_is_not_2);
Run Code Online (Sandbox Code Playgroud)
对于 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>
]
Run Code Online (Sandbox Code Playgroud)
该代码使用 boost::mpl::print。来自 D. Abrahams 和 A. Gurtovoy 所著的《C++ 模板元编程》一书,第 171 页:
要生成编译时执行日志,我们需要一种方法来生成诊断消息 - 警告。因为没有单一的构造会导致所有编译器生成警告(事实上,大多数编译器都允许您完全禁用警告),所以 MPL 有一个元函数,除了它被print调整identity为在各种流行的编译器上生成警告之外,MPL 的元函数与他们通常的设置。
| 归档时间: |
|
| 查看次数: |
8703 次 |
| 最近记录: |