※※※quyanbao的专栏※※※

——学无止境——

Boost.StaticAssert

 

                     Boost.StaticAssert

boost/static_assert.hpp
Table of Contents
Overview and Tutorial
Use at namespace scope.
Use at function scope
Use at class scope
Use in templates
How it works
Test Programs
Overview and Tutorial
The header <boost/static_assert.hpp> supplies a single macro BOOST_STATIC_ASSERT(x), which generates a compile time error message if the integral-constant-expression x is not true. In other words it is the compile time equivalent of the assert macro; this is sometimes known as a "compile-time-assertion", but will be called a "static assertion" throughout these docs. Note that if the condition is true, then the macro will generate neither code nor data - and the macro can also be used at either namespace, class or function scope. When used in a template, the static assertion will be evaluated at the time the template is instantiated; this is particularly useful for validating template parameters.
头文件boost/static_assert.hpp提供了唯一的一个宏——BOOST_STATIC_ASSERT(x),如果整型常量表达式x不为true, 该宏将产生编译时错误信息。换句话说,编译时等价于断言,有时也称为编译时断言,本文中均称为静态断言。注意:如果条件为true,该宏既不会产生代码也不会生成数据。并且该宏可以用于namespaceclassfunction域中。静态断言在模板中使用时,是在实例化模板时确定值,这对于确定模板参数是十分有用的。
One of the aims of BOOST_STATIC_ASSERT is to generate readable error messages. These immediately tell the user that a library is being used in a manner that is not supported. While error messages obviously differ from compiler to compiler, but you should see something like: Error: use of undefined type boost::STATIC_ASSERTION_FAILURE<false>Which is intended to at least catch the eye!
BOOST_STATIC_ASSERT的目标之一就是产生易读的错误信息。从而可以立刻告诉用户不支持某个库的使用方式。虽然产生的错误信息取决于编译器,但是你至少能够看到类似于这样的错误信息:Error: use of undefined type boost::STATIC_ASSERTION_FAILURE<false>
You can use BOOST_STATIC_ASSERT at any place where you can place a declaration, that is at class, function or namespace scope, this is illustrated by the following examples:
你能够在任何可以设置声明的地方(如类、函数、名字空间作用域中)使用BOOST_STATIC_ASSERT。下面的例子是对上述的证明:
Use at namespace scope.
The macro can be used at namespace scope, if there is some requirement must always be true; generally this means some platform specific requirement. Suppose we require that int be at least a 32-bit integral type, and that wchar_t be an unsigned type. We can verify this at compile time as follows:
如果有一些必要条件(一般是指平台一些特殊的要求必备的条件)必须为true这个宏可以在名字空间域中使用。假设我们要求int至少是32位整型,wchar_t是无符号类型。我们可以在编译时检测它:
#include <climits>
#include <cwchar>
#include <limits>
#include <boost/static_assert.hpp>
namespace my_conditions
{
BOOST_STATIC_ASSERT (std::numeric_limits<int>::digits >= 32);
BOOST_STATIC_ASSERT (WCHAR_MIN >= 0);
}  // namespace my_conditions
The use of the namespace my_conditions here requires some comment. The macro BOOST_STATIC_ASSERT works by generating an typedef declaration, and since the typedef must have a name, the macro generates one automatically by mangling a stub name with the value of __LINE__. When BOOST_STATIC_ASSERT is used at either class or function scope then each use of BOOST_STATIC_ASSERT is guaranteed to produce a name unique to that scope (provided you only use the macro once on each line). However when used in a header at namespace scope, that namespace can be continued over multiple headers, each of which may have their own static assertions, and on the "same" lines, thereby generating duplicate declarations. In theory the compiler should silently ignore duplicate typedef declarations, however many do not do so (and even if they do they are entitled to emit warnings in such cases). To avoid potential problems, if you use BOOST_STATIC_ASSERT in a header and at namespace scope, then enclose them in a namespace unique to that header.
这里命名空间my_conditions的使用有一些限制要求。宏BOOST_STATIC_ASSERT根据一个声明的新类型来工作,并且新类型必须有一个名子,宏BOOST_STATIC_ASSERT会立刻以__LINE__接一个简短的名字的形式产生一个名字。宏BOOST_STATIC_ASSERT无论是在类作用域还是在函数作用域中使用时,都会保证在其作用域中有唯一的名字(如果你在每行仅使用一次这个宏的话)。但是,当BOOST_STATIC_ASSERT用在命名空间作用域的一个头文件中,则该命名空间可以延伸到多个头文件,使每个头文件都可以具有自己的静态断言,并且同时产生完全相同的断言副本。理论上说,编译器应该忽略断言副本,但是许多编译器并不这样做(即使这些编译器能够对此给出警告信息)。为了避免潜在的危险,如果你在的一个头文件中使用宏BOOST_STATIC_ASSERT,并且在某个命名空间里,那么应该把命名空间封装在头文件里。
Use at function scope
The macro is typically used at function scope inside template functions, when the template arguments need checking. Imagine that we have an iterator-based algorithm that requires random access iterators. If the algorithm is instantiated with iterators that do not meet our requirements then an error will be generated eventually, but this may be nested deep inside several templates, making it hard for the user to determine what went wrong. One option is to add a static assertion at the top level of the template, in that case if the condition is not met, then an error will be generated in a way that makes it reasonably obvious to the user that the template is being misused.
模板函数的函数域中使用这个宏,当模板参数需要检查时,宏的使用是是具有代表性的。假设我们有一个基于迭代器的算法,要求是随机访问迭代器。如果使用该算法时迭代器不符合算法要求,最终将产生错误,但是这可能是深层嵌套在几个独立的模板中,使得用户很难找出哪里出的错。一种解决方案就是在嵌套模板的最顶层添加静态断言,那么,如果条件不符合,则会中途出现错误,相当清楚的告知用户模板使用错误。
#include <iterator>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
template <class RandomAccessIterator>
RandomAccessIterator foo(RandomAccessIterator from, RandomAccessIterator to)
{
// this template can only be used with
// random access iterators...
typedef typename std::iterator_traits<RandomAccessIterator>::
iterator_category cat;
BOOST_STATIC_ASSERT((boost::is_convertible<cat, const
std::random_access_iterator_tag&>::value));
// detail goes here...
return from;
}
A couple of footnotes are in order here: the extra set of parenthesis around the assert, is to prevent the comma inside the is_convertible template being interpreted by the preprocessor as a macro argument separator; the target type for is_convertible is a reference type, as some compilers have problems using is_convertible when the conversion is via a user defined constructor (in any case there is no guarantee that the iterator tag classes are copy-constructible).
断言周围设置的注释,是为了防止is_convertible模板里的逗号被预处理器解释成宏的参数分离器。is_convertible的目标类型是引用,当变换要通过用户自定义的构造器时,一些编译器使用is_convertible会有一些问题(无论怎样,迭代器作用在拷贝构造的类中是没有保障的)。
Use at class scope
The macro is typically used inside classes that are templates. Suppose we have a template-class that requires an unsigned integral type with at least 16-bits of precision as a template argument, we can achieve this using something like this:
BOOST_STATIC_ASSERT在类中使用也是典型的。假设我们有一个模板类,要求一个至少16位的无符号整型作为模板参数,我们可以这样实现:
#include <climits>
#include <boost/static_assert.hpp>
template <class UnsignedInt>
class myclass
{
private:
BOOST_STATIC_ASSERT((std::numeric_limits<UnsignedInt>::digits >= 16)
&& std::numeric_limits<UnsignedInt>::is_specialized
&& std::numeric_limits<UnsignedInt>::is_integer
&&!std::numeric_limits<UnsignedInt>::is_signed);
public:
/* details here */
};
Use in templates
Normally static assertions when used inside a class or function template, will not be instantiated until the template in which it is used is instantiated. However, there is one potential problem to watch out for: if the static assertion is not dependent upon one or more template parameters, then the compiler is permitted to evaluate the static assertion at the point it is first seen, irrespective of whether the template is ever instantiated, for example:
通常静态断言在类或模板函数中使用时,模板实例后,静态断言才能在实例的模板中实例。但是外部有一个潜在的问题:如果静态断言不依赖模板一个或多个参数,那么编译器允许在静态断言一开始就求值,而不顾模板是否实例,例如:
template <class T>
struct must_not_be_instantiated
{
BOOST_STATIC_ASSERT(false);
};
Will produce a compiler error with some compilers (for example Intel 8.1 or gcc 3.4), regardless of whether the template is ever instantiated. A workaround in cases like this is to force the assertion to be dependent upon a template parameter:
不管模板是否实例,一些编译器会产生编译错误(例如Intel 8.1 or gcc 3.4),这种情况下断言就必须依赖于模板参数:
template <class T>
struct must_not_be_instantiated
{
// this will be triggered if this type is instantiated
BOOST_STATIC_ASSERT(sizeof(T) == 0);
};
How it works
BOOST_STATIC_ASSERT works as follows. There is class STATIC_ASSERTION_FAILURE which is defined as:
namespace boost
{
template <bool> struct STATIC_ASSERTION_FAILURE;
template <> struct STATIC_ASSERTION_FAILURE<true> {};
}
The key feature is that the error message triggered by the undefined expression sizeof (STATIC_ASSERTION_FAILURE<0>), tends to be consistent across a wide variety of compilers. The rest of the machinery of BOOST_STATIC_ASSERT is just a way to feed the sizeof expression into a typedef. The use of a macro here is somewhat ugly; however boost members have spent considerable effort trying to invent a static assert that avoided macros, all to no avail. The general conclusion was that the good of a static assert working at namespace, function, and class scope outweighed the ugliness of a macro.
关键特征是:大多数编译器都一致的以无符号表达式sizeof (STATIC_ASSERTION_FAILURE<0>)来触发产生错误消息框。接下来的是BOOST_STATIC_ASSERT依靠sizeof表达式用在类型定义的方法。这里宏的使用稍微有点难看,但是创造静态断言时,boost成员花费了相当大的努力来避免使用更多的宏,不是所有的宏都是无益的。一般,得到工作于名字空间、函数、类作用域的优秀的静态断言要比多使用一个难看的宏要值得。
有以下几种情形可使用BOOST_STATIC_ASSERT
n         条件可以在编译时表示
n         类型的需求可以在编译时表示
n         需要对两个或多个整型常量之间的关系进行断言时
Test Programs
Test programs provided with static_assert
Test Program
Expected to Compile
Description
Yes
Illustrates usage, and should always compile, really just tests compiler compatibility.
static_assert_example_1.cpp
Platform dependent.
Namespace scope test program, may compile depending upon the platform.
static_assert_example_2.cpp
Yes
Function scope test program.
static_assert_example_3.cpp
Yes
Class scope test program.
static_assert_test_fail_1.cpp
No
Illustrates failure at namespace scope.
static_assert_test_fail_2.cpp
No
Illustrates failure at non-template function scope.
static_assert_test_fail_3.cpp
No
Illustrates failure at non-template class scope.
static_assert_test_fail_4.cpp
No
Illustrates failure at non-template class scope.
static_assert_test_fail_5.cpp
No
Illustrates failure at template class scope.
static_assert_test_fail_6.cpp
No
Illustrates failure at template class member function scope.
static_assert_test_fail_7.cpp
No
Illustrates failure of class scope example.
static_assert_test_fail_8.cpp
No
Illustrates failure of function scope example.
static_assert_test_fail_9.cpp
No
Illustrates failure of function scope example (part 2).
 
//--------------------------------------------------------------------------
// static_assert_test.cpp
//--------------------------------------------------------------------------
// all these tests should succeed. some of these tests are rather simplistic (ie
// useless) in order to ensure that they compile on all platforms. Namespace scope
//--------------------------------------------------------------------------
#include <boost/static_assert.hpp>
BOOST_STATIC_ASSERT(sizeof(int) >= sizeof(short));
BOOST_STATIC_ASSERT(sizeof(char) == 1);
// Function (block) scope
void f()
{
BOOST_STATIC_ASSERT(sizeof(int) >= sizeof(short));
BOOST_STATIC_ASSERT(sizeof(char) == 1);
}
struct Bob
{
private: // can be in private, to avoid namespace pollution
BOOST_STATIC_ASSERT(sizeof(int) >= sizeof(short));
BOOST_STATIC_ASSERT(sizeof(char) == 1);
public:    // Member function scope: provides access to member variables
int x;
char c;
int f()
{
#ifndef _MSC_VER // broken sizeof in VC6
BOOST_STATIC_ASSERT(sizeof(x) >= sizeof(short));
BOOST_STATIC_ASSERT(sizeof(c) == 1);
#endif
return x;
}
};
// Template class scope
template <class Int, class Char>
struct Bill
{
private:  // can be in private, to avoid namespace pollution
BOOST_STATIC_ASSERT(sizeof(Int) > sizeof(char));
public:
// Template member function scope: provides access to member variables
Int x;
Char c;
template <class Int2, class Char2>
void f(Int2, Char2)
{
BOOST_STATIC_ASSERT(sizeof(Int) == sizeof(Int2));
BOOST_STATIC_ASSERT(sizeof(Char) == sizeof(Char2));
}
};
void test_Bill() // BOOST_CT_ASSERTs are not triggerred until instantiated
{
Bill<int, char> z;
//Bill<int, int> bad; // will not compile
int i = 3;
char ch = 'a';
z.f(i, ch);
//z.f(i, i); // should not compile
}
int main()
{
test_Bill();
return 0;
}
//--------------------------------------------------------------------------
// static_assert_example_1.cpp
//--------------------------------------------------------------------------
#include <climits>
#include <cwchar>
#include <limits>
#include <boost/static_assert.hpp>
#if !defined(WCHAR_MIN)
#define WCHAR_MIN 0
#endif
namespace boost
{
namespace my_conditions
{
BOOST_STATIC_ASSERT(std::numeric_limits<int>::digits >= 32);
BOOST_STATIC_ASSERT(WCHAR_MIN >= 0);
} // namespace my_conditions
} // namespace boost
int main()
{
return 0;
}
 
//--------------------------------------------------------------------------
// static_assert_example_2.cpp
//--------------------------------------------------------------------------
#include <iterator>
#include <list>
#include <deque>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
template <class RandomAccessIterator >
RandomAccessIterator foo(RandomAccessIterator from, RandomAccessIterator to)
{
// this template can only be used with random access iterators...
typedef typename std::iterator_traits<RandomAccessIterator>
::iterator_category cat;
BOOST_STATIC_ASSERT((boost::is_convertible<cat, const
std::random_access_iterator_tag&>::value));
// detail goes here...
return from;
}
int main()
{
std::deque<int> d;
std::list<int> l;
foo(d.begin(), d.end());        // OK
//foo(l.begin(), l.end()); // error
return 0;
}
 
//--------------------------------------------------------------------------
// static_assert_example_3.cpp
//--------------------------------------------------------------------------
#include <climits>
#include <boost/limits.hpp>
#include <boost/static_assert.hpp>
template <class UnsignedInt>
class myclass
{
private:
BOOST_STATIC_ASSERT((std::numeric_limits<UnsignedInt>::digits >= 16)
&& std::numeric_limits<UnsignedInt>::is_specialized
&& std::numeric_limits<UnsignedInt>::is_integer
&&!std::numeric_limits<UnsignedInt>::is_signed);
public: /* details here */
};
myclass<unsigned>      m1;      // this should be OK
//myclass<int>           m2; // this should fail
//myclass<unsigned char> m3;    // and so should this
int main()
{
return 0;
}
//--------------------------------------------------------------------------
// static_assert_test_fail_1.cpp
//--------------------------------------------------------------------------
// all these tests should fail:
#include <boost/static_assert.hpp>
typedef char a1[2];
typedef char a2[3];
// Namespace scope
BOOST_STATIC_ASSERT(sizeof(a1) == sizeof(a2)); // will not compile
 
//--------------------------------------------------------------------------
// static_assert_test_fail_2.cpp
//--------------------------------------------------------------------------
// all these tests should fail:
#include <boost/static_assert.hpp>
typedef char a1[2];
typedef char a2[3];
// Function (block) scope
void f()
{
BOOST_STATIC_ASSERT(sizeof(a1) == sizeof(a2)); // should not compile
}
 
//--------------------------------------------------------------------------
// static_assert_test_fail_3.cpp
//--------------------------------------------------------------------------
// this test should fail:
#include <boost/static_assert.hpp>
typedef char a1[2];
typedef char a2[3];
struct Bob
{
private: // can be in private, to avoid namespace pollution
BOOST_STATIC_ASSERT(sizeof(a1) == sizeof(a2)); // will not compile
public:    // Member function scope: provides access to member variables
int x;
char c;
int f()
{
#ifndef _MSC_VER // broken sizeof in VC6
BOOST_STATIC_ASSERT(sizeof(x) == 4);
BOOST_STATIC_ASSERT(sizeof(c) == 1);
#endif
return x;
}
};
 
//--------------------------------------------------------------------------
// static_assert_test_fail_4.cpp
//--------------------------------------------------------------------------
// this test should fail:
struct Bob
{
public:    // Member function scope: provides access to member variables
char x[4];
char c;
int f()
{
#if !defined(BOOST_MSVC) || BOOST_MSVC > 1200 // broken sizeof in VC6
BOOST_STATIC_ASSERT(sizeof(x) == 4);
BOOST_STATIC_ASSERT(sizeof(c) == 1);
BOOST_STATIC_ASSERT((sizeof(x) == sizeof(c))); // should not compile
#endif
return x;
}
};
//--------------------------------------------------------------------------
// static_assert_test_fail_5.cpp
//--------------------------------------------------------------------------
// all these tests should fail:
// Template class scope
#include <boost/static_assert.hpp>
template <class Int, class Char>
struct Bill
{
private: // can be in private, to avoid namespace pollution
BOOST_STATIC_ASSERT(sizeof(Int) == 4);
BOOST_STATIC_ASSERT(sizeof(Int) == sizeof(Char));
// should not compile when instantiated
public:
// Template member function scope: provides access to member variables
int x;
Char c;
template <class Int2, class Char2>
void f(Int2, Char2)
{
BOOST_STATIC_ASSERT(sizeof(Int) == sizeof(Int2));
BOOST_STATIC_ASSERT(sizeof(Char) == sizeof(Char2));
// should not compile when instantiated
// BOOST_STATIC_ASSERT(sizeof(Int) == sizeof(Char));
}
};
Bill<int, char> b;
//--------------------------------------------------------------------------
// static_assert_test_fail_6.cpp
//--------------------------------------------------------------------------
// all these tests should fail:
// Template class scope
#include <boost/static_assert.hpp>
template <class Int, class Char>
struct Bill
{
private:  
       // can be in private, to avoid namespace pollution
       BOOST_STATIC_ASSERT(sizeof(Int) == 4);
        // should not compile when instantiated
        // BOOST_STATIC_ASSERT(sizeof(Int) == sizeof(Char));
public:
        // Template member function scope: provides access to member variables
        Int x;
        Char c;
        template <class Int2, class Char2>
        void f(Int2, Char2)
        {
               BOOST_STATIC_ASSERT(sizeof(Int) == sizeof(Int2));
               BOOST_STATIC_ASSERT(sizeof(Char) == sizeof(Char2));
               // should not compile when instantiated
               BOOST_STATIC_ASSERT(sizeof(Int) == sizeof(Char));
       }
};
void foo()
{
        int i = 0;
        char c = 0;
        Bill<int, char> b;
        // this should fail:
        b.f(i, c);
}
 
//--------------------------------------------------------------------------
// static_assert_test_fail_7.cpp
//--------------------------------------------------------------------------
#include <climits>
#include <boost/limits.hpp>
#include <boost/static_assert.hpp>
template <class UnsignedInt>
class myclass
{
private:
                BOOST_STATIC_ASSERT(sizeof(UnsignedInt) * CHAR_BIT >= 16);
                BOOST_STATIC_ASSERT(std::numeric_limits<UnsignedInt>::is_specialized
                                                                 && std::numeric_limits<UnsignedInt>::is_integer
                                                                 &&!std::numeric_limits<UnsignedInt>::is_signed);
public:
                /* details here */
};
myclass<int>           m2;       // this should fail
myclass<unsigned char> m3; // and so should this
int main()
{
        return 0;
}
 
//--------------------------------------------------------------------------
// static_assert_test_fail_8.cpp
//--------------------------------------------------------------------------
#include <iterator>
#include <list>
#include <deque>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
template <class RandomAccessIterator >
RandomAccessIterator foo(RandomAccessIterator from, RandomAccessIterator)
{
        // this template can only be used with random access iterators...
        typedef typename std::iterator_traits<RandomAccessIterator >
::iterator_category cat;
        BOOST_STATIC_ASSERT((boost::is_convertible<cat*,
std::random_access_iterator_tag*>::value));
        // detail goes here...
        return from;
}
// ensure that delayed instantiation compilers like Comeau see the failure early
// enough for "compile-fail" testing with the Boost.Build testing framework.
template std::list<int>::iterator foo(std::list<int>::iterator, std::list<int>::iterator);
int main()
{
       std::deque<int> d;
       std::list<int> l;
       foo(d.begin(), d.end());     // OK
       foo(l.begin(), l.end());       // error
       return 0;
}
 
//--------------------------------------------------------------------------
// static_assert_test_fail_9.cpp
//--------------------------------------------------------------------------
#include <climits>
#include <boost/limits.hpp>
#include <boost/static_assert.hpp>
template <class UnsignedInt>
class myclass
{
private:
                BOOST_STATIC_ASSERT(sizeof(UnsignedInt) * CHAR_BIT >= 16);
                BOOST_STATIC_ASSERT(std::numeric_limits<UnsignedInt>::is_specialized
                                                                 && std::numeric_limits<UnsignedInt>::is_integer
                                                                 &&!std::numeric_limits<UnsignedInt>::is_signed);
public: /* details here */
};
myclass<unsigned>           m1; // this should be OK
//myclass<int>                      m2; // this should fail
myclass<unsigned char>  m3; // and so should this
int main()
{
        return 0;
}

 

阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭