Changes between Initial Version and Version 1 of Guidelines/IntegralConstants


Ignore:
Timestamp:
Feb 14, 2010, 12:08:30 PM (13 years ago)
Author:
Daniel James
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Guidelines/IntegralConstants

    v1 v1  
     1[[PageOutline]]
     2
     3= Coding Guidelines for Integral Constant Expressions = #intro
     4
     5Integral Constant Expressions are used in many places in
     6C++; as array bounds, as bit-field lengths, as enumerator
     7initialisers, and as arguments to non-type template parameters.
     8However many compilers have problems handling integral constant
     9expressions; as a result of this, programming using non-type
     10template parameters in particular can be fraught with
     11difficulty, often leading to the incorrect assumption that
     12non-type template parameters are unsupported by a particular
     13compiler. This short article is designed to provide a set of
     14guidelines and workarounds that, if followed, will allow
     15integral constant expressions to be used in a manner portable
     16to all the compilers currently supported by boost. Although
     17this article is mainly targeted at boost library authors, it
     18may also be useful for users who want to understand why boost
     19code is written in a particular way, or who want to write
     20portable code themselves.
     21
     22== What is an Integral Constant Expression? == #whatis
     23
     24Integral constant expressions are described in section 5.19
     25of the standard, and are sometimes referred to as "compile time
     26constants". An integral constant expression can be one of the
     27following:
     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    {{{
     33const int my_INTEGRAL_CONSTANT = 3;
     34    }}}
     35 1. Static member constants, for example:
     36    {{{
     37struct myclass
     38{ static const int value = 0; };
     39    }}}
     40 1. Member enumerator values, for example:
     41    {{{
     42struct 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    {{{
     48sizeof(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    {{{
     57INTEGRAL_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    {{{
     63op INTEGRAL_CONSTANT1
     64    }}}
     65    provided that the operator is not the increment or decrement operator.
     66
     67== Coding Guidelines == #guidelines
     68
     69The following guidelines are declared in no particular order
     70(in other words you need to obey all of them - sorry!), and may
     71also be incomplete, more guidelines may be added as compilers
     72change 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{{{
     76template <class T>
     77struct myclass
     78{
     79    BOOST_STATIC_CONSTANT(int, value = sizeof(T));
     80};
     81}}}
     82
     83Rationale: not all compilers support inline initialisation
     84of member constants, others treat member enumerators in strange
     85ways (they're not always treated as integral constant
     86expressions). The BOOST_STATIC_CONSTANT macro uses the most
     87appropriate method for the compiler in question.
     88
     89=== Don't declare integral constant expressions whose type is wider than int. === #wider-int
     90
     91Rationale: while in theory all integral types are usable in
     92integral constant expressions, in practice many compilers limit
     93integral 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
     98The header `<boost/type_traits/ice.hpp>`
     99contains a number of workaround templates, that fulfil the role
     100of logical operators, for example instead of:
     101{{{
     102INTEGRAL_CONSTANT1 || INTEGRAL_CONSTANT2
     103}}}
     104
     105Use:
     106{{{
     107::boost::type_traits::ice_or<INTEGRAL_CONSTANT1,INTEGRAL_CONSTANT2>::value
     108}}}
     109
     110Rationale: A number of compilers (particularly the Borland
     111and Microsoft compilers), tend to not to recognise integral
     112constant expressions involving logical operators as genuine
     113integral constant expressions. The problem generally only shows
     114up when the integral constant expression is nested deep inside
     115template 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
     119Rather than:
     120{{{
     121typedef myclass<INTEGRAL_CONSTANT1 == INTEGRAL_CONSTANT2> mytypedef;
     122}}}
     123
     124Use:
     125{{{
     126typedef myclass< some_symbol> mytypedef;
     127}}}
     128
     129Where `some_symbol` is the symbolic name of a an
     130integral constant expression whose value is
     131`(INTEGRAL_CONSTANT1 == INTEGRAL_CONSTANT2).`
     132
     133Rationale: the older EDG based compilers (some of which are
     134used in the most recent version of that platform's compiler),
     135don't recognise expressions containing operators as non-type
     136template parameters, even though such expressions can be used
     137as integral constant expressions elsewhere.
     138
     139=== Always use a fully qualified name to refer to an integral constant expression. === #fully-qualified-name
     140
     141For example:
     142{{{
     143typedef myclass< ::boost::is_integral<some_type>::value> mytypedef;
     144}}}
     145
     146Rationale: at least one compiler (Borland's), doesn't
     147recognise the name of a constant as an integral constant
     148expression unless the name is fully qualified (which is to say
     149it starts with `::`).
     150
     151=== Always leave a space after a '`<`' and before '`::`' === #spaces
     152
     153For example:
     154{{{
     155typedef myclass< ::boost::is_integral<some_type>::value> mytypedef;
     156                ^
     157                ensure there is space here!
     158}}}
     159
     160Rationale: `<:` is a legal digraph in it's own
     161right, so `<::` is interpreted as the same as
     162`[:`.
     163
     164=== Don't use local names as integral constant expressions === #local-names
     165
     166Example:
     167{{{
     168template <class T>
     169struct foobar
     170{
     171    BOOST_STATIC_CONSTANT(int, temp = computed_value);
     172    typedef myclass<temp> mytypedef;  // error
     173};
     174}}}
     175
     176Rationale: At least one compiler (Borland's) doesn't accept
     177this.
     178
     179Although it is possible to fix this by using:
     180{{{
     181template <class T>
     182struct 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
     190This breaks at least one other compiler (VC6), it is better
     191to move the integral constant expression computation out into a
     192separate traits class:
     193{{{
     194template <class T>
     195struct foobar_helper
     196{
     197    BOOST_STATIC_CONSTANT(int, value = computed_value);
     198};
     199
     200template <class T>
     201struct 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
     209For example:
     210{{{
     211template <class T, int I = ::boost::is_integral<T>::value>  // Error can't deduce value of I in some cases.
     212struct foobar;
     213}}}
     214
     215Rationale: this kind of usage fails for Borland C++. Note
     216that this is only an issue where the default value is dependent
     217upon a previous template parameter, for example the following
     218is fine:
     219{{{
     220template <class T, int I = 3>  // OK, default value is not dependent
     221struct foobar;
     222}}}
     223
     224== Unresolved Issues == #unresolved
     225
     226The following issues are either unresolved or have fixes
     227that are compiler specific, and/or break one or more of the
     228coding guidelines.
     229
     230=== Be careful of numeric_limits === #numeric-limits
     231
     232There 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{{{
     253template <class T>
     254struct limits_test
     255{
     256    BOOST_STATIC_ASSERT(::std::numeric_limits<T>::is_specialized);
     257};
     258}}}
     259
     260This code fails to compile with VC6 even though no instances
     261of the template are ever created; for some bizarre reason
     262`::std::numeric_limits<T>::is_specialized`
     263always evaluates to false, irrespective of what the template
     264parameter T is. The problem seems to be confined to expressions
     265which depend on std::numeric_limts: for example if you replace
     266`::std::numeric_limits<T>::is_specialized`
     267with `::boost::is_arithmetic<T>::value`, then
     268everything is fine. The following workaround also works but
     269conflicts with the coding guidelines:
     270{{{
     271template <class T>
     272struct limits_test
     273{
     274    BOOST_STATIC_CONSTANT(bool, check = ::std::numeric_limits<T>::is_specialized);
     275    BOOST_STATIC_ASSERT(check);
     276};
     277}}}
     278
     279So it is probably best to resort to something like this:
     280{{{
     281template <class T>
     282struct 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
     295As far as I can tell, all compilers treat sizeof expressions
     296correctly when the argument is the name of a type (or a
     297template-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
     310Since is_convertible is implemented in terms of the sizeof
     311operator, it consistently gives the wrong value when used with
     312the Metroworks compiler, and may not compile with the Borland's
     313compiler (depending upon the template arguments used).