Преобразование числа в дополненную строку - PullRequest
4 голосов
/ 16 февраля 2020

Я пытаюсь преобразовать числовые c типы в строковые представления фиксированной длины с начальными нулями (используется в GUI, который может иметь дело только со строками - и лексикографическая c сортировка чисел действительно неудобна).

Вместо создания строки в том месте, где она требуется, я полагаю, что можно создать отличный шаблон для использования другими людьми, который также работает с различными типами арифметики c.

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;
}

Пока все хорошо. Он компилируется, и я могу его использовать. НО:

Когда я попытался использовать его, я получил сюрприз. Вызов этой функции с int32_t, равным

typedef int int32_t;

, который имеет значение 100, в результате получается строка "100", длина 3.

Я почти уверен, что нет другого шаблона с такое плохое имя существует в кодовой базе (у меня есть поиск по всем этим и всем связанным библиотекам), но я не могу понять, что не так с кодом, который я создал. Чтобы проверить правильность кода, я просто скопировал его в место, где он вызывается, что теперь в основном выглядит следующим образом:

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;
}

Теперь, насколько я понимаю, шаблон "arithmeticToMaxLengthString" вызывается с int32_t как тип аргумента, который является нормальным int, где «numeric_limits :: digits10 + 1» дает мне 10. Но когда эта функция теперь вызывается с количеством, скажем, 100, результаты отличаются.

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

Я что-то упускаю из-за шаблонов, enable_if или numeric_limits? Чем шаблон отличается (кроме очевидного enable_if)?

Ответы [ 2 ]

2 голосов
/ 17 февраля 2020

Ваша проблема возникает из-за передачи ссылки на функцию arithmeticToMaxLengthString. По причинам, перечисленным в другом ответе здесь , std::numeric_limits не работает со ссылками.

Поэтому я предлагаю вам использовать тип шаблона T для получения числового предела вместо использования decltype(value):

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

Мало того, что это решает проблему, это также выглядит намного элегантнее.

1 голос
/ 17 февраля 2020

Это на самом деле не ответ на ваш вопрос, а предложение о том, как реализовать ту же функциональность, используя STL.

#include <iomanip>
#include <sstream>

template <typename T, unsigned N = 5>
std::string to_string_with_leading_0(T element) {
    std::stringstream ss;
    ss << std::setw(N) << std::setfill('0') << element;
    return ss.str();
}
...