We Have Mail

Letters to the editor may be sent via email to cujed@cmp.com, or via the postal service to Letters to the Editor, C/C++ Users Journal, 1601 W. 23rd St., Ste 200, Lawrence, KS 66046-2700.


I read with interest Thomas Becker's column (CUJ, February 2003) on using template metaprogramming to provide default values for non-trailing function arguments and would like to respond to his request for alternative techniques.

Defaulting function arguments is essentially an exercise in selective binding, where function arguments are bound either to a specified default value or to the supplied value.

The code as described in the column has the unfortunate property that you must supply a tuple object encapsulating the parameters to the function Foo, so the mechanism for determining the default intrudes into the client code.

I offer a sample file (available for download at <www.cuj.com/code>) that provides an alternative mechanism for generalized default function parameters. The metaprogramming content is less and is restricted to the use overload resolution and function template argument type deduction, but the impact on client code is also much less -- for a function with three args, the call at the client site is foo(arg1,arg2,arg3), where any of arg[1,2,3] can be defaultValue().

It requires thought by the writer of the wrapper function -- parameters that are passed by non-const reference must be non-const in the wrapper function, and parameters that are passed by value, or const reference, should be taken as const references in the wrapper function. Also, checkForDefaultVal and checkForDefaultRef should be used as appropriate. However, I don't think this is really any more than required by Becker's proposed solution. In most cases, I would expect the wrapper function and checkForDefaultXXX functions to be completely optimized away by the compiler, since they are very simple inline functions, and the effect would therefore be as if the caller had specified the defaults right at the call site.

Anthony Williams

Hello Anthony,

Thank you very much for your feedback on my February CUJ column. I agree with you that yours is the better solution. Clearly, the intrusion on the client code is minimal. (It's hard to see how it could be less at all.) As far as the burden on the implementer of the function is concerned, we're probably in the same ballpark. Therefore, you win in the overall rating. It seems that I was out on a limb with my idea of packaging the arguments in a tuple.

Thanks again for your feedback.
Best,
Thomas Becker


I have a couple of comments regarding Matthew Wilson's article "C/C++ Tip #10: Efficient Integer to String Conversions," which appeared in the December 2002 issue.

First, there seems to be a bit of duplicated code. Why not rewrite the signed_integer_to_string function like this:

template <class C, class I>
inline const C *signed_integer_to_string
  (C *buf, size_t ccBuf, I i)
{
  C *psz = buf + ccBuf - 1;
  *psz = 0;

  bool negative = i < 0;
  do
  {
    signed lsd = i % 10;
    i /= 10;
    --psz;
    *psz = 
      get_digit_character<C>()[lsd];
  } while (i != 0);
  if (negative)
    *(--psz) = C('-');

  return psz;
}
Or better yet, like this:

template <class C, class I>
inline const C *signed_integer_to_string
  (C *buf, size_t ccBuf, I i)
{
  C *psz = const_cast<C *>
    (unsigned_integer_to_string
      (buf, ccBuf, i));
  if (i < 0)
    *(--psz) = C('-');

  return psz;
}
Second, from a user's point of view, it seems that filling the buffer backwards from the end can cause some problems. If I dynamically allocate a buffer, I now have to keep track of two pointers: one for my buffer and one for my string. If I statically allocate a buffer on the stack, I need to make sure my buffer does not go out of scope before I am finished with the string.

Please let me know if I am missing something.

Thanks,
Shawn Odekirk

Hi Shawn,

The reason I did not elect to use the first of your two suggested implementations (in fact it is virtually identical to an early implementation) is simply that it is less efficient than the current implementation on most of the compilers supported by the STLSoft libraries.

The second solution you describe is the elegant one; the prosaic reason why I did not use it is that some compilers -- GCC being the standout miscreant -- have significantly worse performance (GCC has a stunning degradation of up to 40%!) with it, and no compilers have noticeably better performance.

Since STLSoft is all about portability, and integer_to_string is all about performance, the GCC performance dictates the current implementation. I did think about having it conditionally compile for the others, but couldn't see the point in introducing the potential maintenance headaches.

As for your second point about the buffer, you're not missing anything. In order to have maximal efficiency and be thread-safe without using proprietary extensions such as __declspec(thread) (which does not work in explicitly loaded DLLs, so one wonders why they bother...), the user must supply his own buffer. But since string representations of integral types have a fixed maximum size (4/3, 6/5, 11/10, 20/19 characters -- for signed/unsigned 8-, 16-, 32-, 64-bits), and frame memory is cheap, this seems like a relatively painless cost. As I said in the article, if you don't really need the speed, stick to sprintf(); of course, you still need to provide the memory there, so....

Thanks very much for your interest. I hope you found the functions useful. I'd be keen to hear any other thoughts on the STLSoft libraries you may have in the future.

Best regards,
Matthew Wilson


I have received several emails to point out one bug in the code included with my article "C/C++ Tip #11: Overwrite Iterator" in CUJ, December 2002. The problem relates to the assignment operator. The function as it was published:

overwrite_iterator& operator=
  (const Cont::value_type& val)
{
  iter ==
    cont.end() ? cont.push_back(val) :
    (*iter = val, ++iter);
  return *this;
}
should instead read:

overwrite_iterator& operator=
  (const Cont::value_type& val)
{
  iter ==
    cont.end() ? (cont.push_back(val),
    iter = cont.end()) : (*iter =
      val, ++iter);
  return *this;
}
The bug makes the iterator not work for vector and deque containers, but it still works for list.

Ray Virzi