Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 31.  Return Value Lifetime


31.6. Solution 4—Static Array Size Determination

By now you may be despairing that these incremental solutions all represent retrograde steps. Thankfully that's not the case, and I suggest that solution 4 represents the optimal solution to the problem, given the current state of the language and the majority of its supporting compilers.

Getting back to the original integer_to_string() functions, the only aspect of them that drew any significant criticism is the ability for a caller to supply an invalid value for the buffer length. If the length is too small, then at least an assertion will fire in debug builds. If the length value stipulated is sufficient, but does not accurately represent the actual, undersized buffer length, then underrun will occur, which will result in corruption of either the stack or the heap.

But as we saw in section 14.3, most modern compilers can deduce the static size of arrays. Hence we can define overloads of the original integer_to_string() functions that take an array parameter rather than the pointer + size parameters:



template< typename C


        , size_t   N


        >


C const *integer_to_string(C (&buf)[N], int8_t i)


{


  return integer_to_string(buf, N, i); // Safely call ptr form


}



This eliminates the possibility of an erroneous buffer length being passed to the function. Even better, we can use compile-time checking, in the form of a static-assertion (see section 1.4.7), to ensure that the buffer length is sufficient.[11]

[11] printf_traits is an STLSoft traits class that evaluates, at compile time, the maximum "printf-width" of a given integral type.



template< typename C


        , size_t   N


        >


C const *integer_to_string(C (&buf)[N], int8_t i)


{


  STATIC_ASSERT(!(N < printf_traits<int8_t>::size));


  return integer_to_string(buf, N, i);


}



Now there're no concerns about whether we'll run into a release-build bug that was not located via the assertion in debug-mode testing. If the given parameter is not sufficient, the code will not compile. Pretty spiffy, don't you think?

This solution is thread safe, is not susceptible to erroneous length specification, works with arbitrary character encodings, and does not require explicit instantiation. It is readily inlined into the function it is implemented in terms of, so it does not sacrifice efficiency. Furthermore, it is pleasingly simple and does not rely on any platform-specific functionality. And, finally, it leads to more succinct code, since we no longer have to specify the buffer size.



uint64_t      i = . . .


wchar_t       buff[12];


wchar_t const *result = integer_to_string(buff, i);



The only downside is that one must still supply a buffer.

31.6.1 RVL

The RVL ramifications of this are the same as for Solution 1, which is to say it is susceptible to RVL-LV.


      Previous section   Next section