c++c++11templatesenable-ifnumeric-limits

Converting number to a padded string


I am trying to convert numeric types to fixed length string representations with leading '0's (used in a GUI that can only deal with strings - and lexicographic sorting of numbers is really awkward).

Instead of creating the string on the spot where it is required, I figure it should be possible to make a nice template for others to use that also works with different arithmetic types.

template <typename T, typename std::enable_if<std::is_arithmetic<T>::value, T>::type* = nullptr>
std::string arithmeticToMaxLengthString(const T& value)
{
  std::string number, result;
  number = std::to_string( value );
  result = lpad( '0', std::numeric_limits<decltype(value)>::digits10 + 1, number );
  return result;
}

std::string lpad( char filler, std::string::size_type length, const std::string& input)
{
  std::string result;
  if( input.length() < length )
  {
     result.append( lenght - input.lenght(), filler ).append( input );
     return result;
  }
  return input;
}

So far so good. It compiles and I can use it. BUT:

When I tried using it, I got a surprise. Calling this function with an int32_t which is

typedef int int32_t;

that has a value of 100 the result is the string "100", length 3.

I am pretty certain that no other template with such a bad name exists in the codebase (I have search through all of it, and all linked libraries), but I can not seem to understand what is wrong with the code I have produced. To verify the code to be correct I have just copy-pasted it to the place it is called, which now basically looks like this:

myDataClass{
  int32_t     quantity;
  std::string numberAsString;
}

void setNumberString( myDataClass data )
{
   std::string result    = arithmeticToMaxLengthString( data.quantity );
   std::string reference = lpad( '0', std::numeric_limits<decltype(value)>::digits10 + 1, std::to_string(data.quantity) );
   std::cout << "quantity<" << data.quantity << "> numericLimits<" << std::numeric_limits<decltype(value)>::digits10 + 1 << ">" << std::endl;
   std::cout << "result  template: length<" << result.lenght()    << "> content<" << result    << ">" << std::endl;
   std::cout << "result reference: length<" << reference.lenght() << "> content<" << reference << ">" << std::endl;
}

Now to my understanding the template "arithmeticToMaxLengthString" is called with int32_t as the type of the argument, which is a normal int, where "numeric_limits::digits10 + 1" gives me 10. But when this funtion is now called with a quantity of say 100 the results differ.

quantity<100> numericLimits<10>
result  template: length<3> content<100>
result reference: length<10> content<0000000100>

Am I missing something about templates, enable_if or numeric_limits? In what way does the template differ (aside from the obvious enable_if) ?


Solution

  • Your problem happens because you are passing a reference to arithmeticToMaxLengthString function. For reasons listed in another answer here, std::numeric_limits does not work with references.

    Therefore, I am suggesting you to use template type T for getting the numerical limit instead of using decltype(value):

    result = lpad('0', std::numeric_limits<T>::digits10 + 1, number);
    

    Not only does it solve the problem, it looks much more elegant too.