Changes between Version 2 and Version 3 of Guidelines/IntegralConstants


Ignore:
Timestamp:
Aug 18, 2010, 10:54:38 PM (12 years ago)
Author:
Daniel James
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Guidelines/IntegralConstants

    v2 v3  
    1 [[PageOutline]]
    2 
    3 = Coding Guidelines for Integral Constant Expressions = #intro
    4 
    5 Integral Constant Expressions are used in many places in
    6 C++; as array bounds, as bit-field lengths, as enumerator
    7 initialisers, and as arguments to non-type template parameters.
    8 However many compilers have problems handling integral constant
    9 expressions; as a result of this, programming using non-type
    10 template parameters in particular can be fraught with
    11 difficulty, often leading to the incorrect assumption that
    12 non-type template parameters are unsupported by a particular
    13 compiler. This short article is designed to provide a set of
    14 guidelines and workarounds that, if followed, will allow
    15 integral constant expressions to be used in a manner portable
    16 to all the compilers currently supported by boost. Although
    17 this article is mainly targeted at boost library authors, it
    18 may also be useful for users who want to understand why boost
    19 code is written in a particular way, or who want to write
    20 portable code themselves.
    21 
    22 == What is an Integral Constant Expression? == #whatis
    23 
    24 Integral constant expressions are described in section 5.19
    25 of the standard, and are sometimes referred to as "compile time
    26 constants". An integral constant expression can be one of the
    27 following:
    28 
    29  1. A literal integral value, for example `0u` or `3L`.
    30  1. An enumerator value.
    31  1. Global integral constants, for example:
    32     {{{
    33 const int my_INTEGRAL_CONSTANT = 3;
    34     }}}
    35  1. Static member constants, for example:
    36     {{{
    37 struct myclass
    38 { static const int value = 0; };
    39     }}}
    40  1. Member enumerator values, for example:
    41     {{{
    42 struct myclass
    43 { enum{ value = 0 }; };
    44     }}}
    45  1. Non-type template parameters of integral or enumerator type.
    46  1. The result of a `sizeof` expression, for example:
    47     {{{
    48 sizeof(foo(a, b, c))
    49     }}}
    50  1. The result of a `static_cast`, where the
    51     target type is an integral or enumerator type, and the
    52     argument is either another integral constant expression, or a
    53     floating-point literal.
    54  1. The result of applying a binary operator to two integral
    55     constant expressions:
    56     {{{
    57 INTEGRAL_CONSTANT1 op INTEGRAL_CONSTANT2
    58     }}}
    59     provided that the operator is not an assignment operator, or comma operator.
    60  1. The result of applying a unary operator to an integral
    61     constant expression:
    62     {{{
    63 op INTEGRAL_CONSTANT1
    64     }}}
    65     provided that the operator is not the increment or decrement operator.
    66 
    67 == Coding Guidelines == #guidelines
    68 
    69 The following guidelines are declared in no particular order
    70 (in other words you need to obey all of them - sorry!), and may
    71 also be incomplete, more guidelines may be added as compilers
    72 change and/or more problems are encountered.
    73 
    74 === When declaring constants that are class members always use the macro `BOOST_STATIC_CONSTANT.` === #boost-static-constant
    75 {{{
    76 template <class T>
    77 struct myclass
    78 {
    79     BOOST_STATIC_CONSTANT(int, value = sizeof(T));
    80 };
    81 }}}
    82 
    83 Rationale: not all compilers support inline initialisation
    84 of member constants, others treat member enumerators in strange
    85 ways (they're not always treated as integral constant
    86 expressions). The BOOST_STATIC_CONSTANT macro uses the most
    87 appropriate method for the compiler in question.
    88 
    89 === Don't declare integral constant expressions whose type is wider than int. === #wider-int
    90 
    91 Rationale: while in theory all integral types are usable in
    92 integral constant expressions, in practice many compilers limit
    93 integral constant expressions to types no wider than
    94 `int`.
    95 
    96 === Don't use logical operators in integral constant expressions; use template meta-programming instead. === #logical-ops
    97 
    98 The header `<boost/type_traits/ice.hpp>`
    99 contains a number of workaround templates, that fulfil the role
    100 of logical operators, for example instead of:
    101 {{{
    102 INTEGRAL_CONSTANT1 || INTEGRAL_CONSTANT2
    103 }}}
    104 
    105 Use:
    106 {{{
    107 ::boost::type_traits::ice_or<INTEGRAL_CONSTANT1,INTEGRAL_CONSTANT2>::value
    108 }}}
    109 
    110 Rationale: A number of compilers (particularly the Borland
    111 and Microsoft compilers), tend to not to recognise integral
    112 constant expressions involving logical operators as genuine
    113 integral constant expressions. The problem generally only shows
    114 up when the integral constant expression is nested deep inside
    115 template code, and is hard to reproduce and diagnose.
    116 
    117 === Don't use any operators in an integral constant expression used as a non-type template parameter === #non-type-parameter
    118 
    119 Rather than:
    120 {{{
    121 typedef myclass<INTEGRAL_CONSTANT1 == INTEGRAL_CONSTANT2> mytypedef;
    122 }}}
    123 
    124 Use:
    125 {{{
    126 typedef myclass< some_symbol> mytypedef;
    127 }}}
    128 
    129 Where `some_symbol` is the symbolic name of a an
    130 integral constant expression whose value is
    131 `(INTEGRAL_CONSTANT1 == INTEGRAL_CONSTANT2).`
    132 
    133 Rationale: the older EDG based compilers (some of which are
    134 used in the most recent version of that platform's compiler),
    135 don't recognise expressions containing operators as non-type
    136 template parameters, even though such expressions can be used
    137 as integral constant expressions elsewhere.
    138 
    139 === Always use a fully qualified name to refer to an integral constant expression. === #fully-qualified-name
    140 
    141 For example:
    142 {{{
    143 typedef myclass< ::boost::is_integral<some_type>::value> mytypedef;
    144 }}}
    145 
    146 Rationale: at least one compiler (Borland's), doesn't
    147 recognise the name of a constant as an integral constant
    148 expression unless the name is fully qualified (which is to say
    149 it starts with `::`).
    150 
    151 === Always leave a space after a '`<`' and before '`::`' === #spaces
    152 
    153 For example:
    154 {{{
    155 typedef myclass< ::boost::is_integral<some_type>::value> mytypedef;
    156                 ^
    157                 ensure there is space here!
    158 }}}
    159 
    160 Rationale: `<:` is a legal digraph in it's own
    161 right, so `<::` is interpreted as the same as
    162 `[:`.
    163 
    164 === Don't use local names as integral constant expressions === #local-names
    165 
    166 Example:
    167 {{{
    168 template <class T>
    169 struct foobar
    170 {
    171     BOOST_STATIC_CONSTANT(int, temp = computed_value);
    172     typedef myclass<temp> mytypedef;  // error
    173 };
    174 }}}
    175 
    176 Rationale: At least one compiler (Borland's) doesn't accept
    177 this.
    178 
    179 Although it is possible to fix this by using:
    180 {{{
    181 template <class T>
    182 struct foobar
    183 {
    184     BOOST_STATIC_CONSTANT(int, temp = computed_value);
    185     typedef foobar self_type;
    186     typedef myclass<(self_type::temp)> mytypedef;  // OK
    187 };
    188 }}}
    189 
    190 This breaks at least one other compiler (VC6), it is better
    191 to move the integral constant expression computation out into a
    192 separate traits class:
    193 {{{
    194 template <class T>
    195 struct foobar_helper
    196 {
    197     BOOST_STATIC_CONSTANT(int, value = computed_value);
    198 };
    199 
    200 template <class T>
    201 struct foobar
    202 {
    203     typedef myclass< ::foobar_helper<T>::value> mytypedef;  // OK
    204 };
    205 }}}
    206 
    207 === Don't use dependent default parameters for non-type template parameters. === #dependent
    208 
    209 For example:
    210 {{{
    211 template <class T, int I = ::boost::is_integral<T>::value>  // Error can't deduce value of I in some cases.
    212 struct foobar;
    213 }}}
    214 
    215 Rationale: this kind of usage fails for Borland C++. Note
    216 that this is only an issue where the default value is dependent
    217 upon a previous template parameter, for example the following
    218 is fine:
    219 {{{
    220 template <class T, int I = 3>  // OK, default value is not dependent
    221 struct foobar;
    222 }}}
    223 
    224 == Unresolved Issues == #unresolved
    225 
    226 The following issues are either unresolved or have fixes
    227 that are compiler specific, and/or break one or more of the
    228 coding guidelines.
    229 
    230 === Be careful of numeric_limits === #numeric-limits
    231 
    232 There are three issues here:
    233 
    234  1. The header <limits> may be absent - it is
    235     recommended that you never include <limits> directly
    236     but use <boost/pending/limits.hpp> instead. This header
    237     includes the "real" <limits> header if it is available,
    238     otherwise it supplies it's own std::numeric_limits
    239     definition. Boost also defines the macro BOOST_NO_LIMITS if
    240     <limits> is absent.
    241  1. The implementation of std::numeric_limits may be defined
    242     in such a way that its static-const members may not be usable
    243     as integral constant expressions. This contradicts the
    244     standard but seems to be a bug that affects at least two
    245     standard library vendors; boost defines
    246     BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS in
    247     <boost/config.hpp> when this is the case.
    248  1. There is a strange bug in VC6, where the members of
    249     std::numeric_limits can be "prematurely evaluated" in
    250     template code, for example:
    251 
    252 {{{
    253 template <class T>
    254 struct limits_test
    255 {
    256     BOOST_STATIC_ASSERT(::std::numeric_limits<T>::is_specialized);
    257 };
    258 }}}
    259 
    260 This code fails to compile with VC6 even though no instances
    261 of the template are ever created; for some bizarre reason
    262 `::std::numeric_limits<T>::is_specialized`
    263 always evaluates to false, irrespective of what the template
    264 parameter T is. The problem seems to be confined to expressions
    265 which depend on std::numeric_limts: for example if you replace
    266 `::std::numeric_limits<T>::is_specialized`
    267 with `::boost::is_arithmetic<T>::value`, then
    268 everything is fine. The following workaround also works but
    269 conflicts with the coding guidelines:
    270 {{{
    271 template <class T>
    272 struct limits_test
    273 {
    274     BOOST_STATIC_CONSTANT(bool, check = ::std::numeric_limits<T>::is_specialized);
    275     BOOST_STATIC_ASSERT(check);
    276 };
    277 }}}
    278 
    279 So it is probably best to resort to something like this:
    280 {{{
    281 template <class T>
    282 struct limits_test
    283 {
    284 #ifdef BOOST_MSVC
    285    BOOST_STATIC_CONSTANT(bool, check = ::std::numeric_limits<T>::is_specialized);
    286    BOOST_STATIC_ASSERT(check);
    287 #else
    288    BOOST_STATIC_ASSERT(::std::numeric_limits<T>::is_specialized);
    289 #endif
    290 };
    291 }}}
    292 
    293 === Be careful how you use the sizeof operator === #sizeof
    294 
    295 As far as I can tell, all compilers treat sizeof expressions
    296 correctly when the argument is the name of a type (or a
    297 template-id), however problems can occur if:
    298 
    299 
    300  1. The argument is the name of a member-variable, or a local
    301     variable (code may not compile with VC6).
    302  1. The argument is an expression which involves the creation
    303     of a temporary (code will not compile with Borland C++).
    304  1. The argument is an expression involving an overloaded
    305     function call (code compiles but the result is a garbage
    306     value with Metroworks C++).
    307 
    308 === Don't use boost::is_convertible unless you have to === #is_convertible
    309 
    310 Since is_convertible is implemented in terms of the sizeof
    311 operator, it consistently gives the wrong value when used with
    312 the Metroworks compiler, and may not compile with the Borland's
    313 compiler (depending upon the template arguments used).
    314 
    315 Copyright Dr John Maddock 2001.
     1This has been move back to the [http://www.boost.org/development/int_const_guidelines.html main site]