Как правильно соблюдать std :: setw и std :: fill при переопределении оператора << в C ++ - PullRequest
1 голос
/ 19 апреля 2020

У меня есть пользовательский тип данных, представляющий строку:

struct ByteArray {
   uint32_t len;
   uint8_t* ptr;
}

и оператор переопределения << для вывода его на экран. </p>

Версия 1:

ostream &operator<<(ostream &os, const ByteArray &dt) {
    os << string((const char*)dt.ptr,dt.len);
    return os;
}

Версия 2:

ostream &operator<<(ostream &os, const ByteArray &dt) {
    os.write((const char *) dt.ptr, dt.len);
    auto fill = os.width() - dt.len;
    fill_n(std::ostream_iterator<char>(os), fill, os.fill());
    return os;
}

Мой код для вывода выглядит примерно так:

ByteArray& value = ...; // a string of length 25
cout << '|' << left << setw(40) << value;

Когда я использую версию 1, она работает хорошо и отображает:

|gAaNbDxVTyFjjhgGodAKyy9uk               

Но версия 2 отображает 17 ведущих пробелов:

|                 gAaNbDxVTyFjjhgGodAKyy9uk    

Я хочу использовать версию 2, поскольку она не создает ненужных строковых объектов. Но как мне правильно обрабатывать setw и заполнять?

Спасибо!

Обновление:

Когда я вывожу несколько строк, они выровнены по левому краю, но имеют 17 пробелов перед фактическими данными. Пожалуйста, смотрите пример ниже. Этот вывод генерируется кодом версии 2 выше. Фактическая ширина составляет 95, когда я устанавливаю ширину 40.

|                 gAaNbDxVTyFjjhgGodAKyy9uk                                                      |
|                 wMEzrsX2KKpTaJGE3uGEUibymG                                                     |                                                   
|                 8cRzJOxCG7z qpfkXKgrQs6ubfOTK                                                  |                                               
|                 A5a1lovY,yQoSHaYon5cGgo1l                                                      |                                            
|                 f1mPa2ts2TUCbZ9UVmuDuu2lXLgfYTP                                                |           

1 Ответ

2 голосов
/ 19 апреля 2020

В коде есть несколько проблем.

  1. Вы должны соблюдать флаг left при обработке width. setw() по существу совпадает с width(), который обычно выравнивается по левому или по правому краю в зависимости от флага left. См. operator << для строк:

    • Если str.size() не меньше os.width(), используется диапазон [str.begin(), str.end()) как есть
    • В противном случае, если (os.flags() & ios_base::adjustfield) == ios_base::left, ставит os.width()-str.size(), копии символа os.fill() после последовательности символов
    • В противном случае, размещает os.width()-str.size() копий символа os.fill() перед последовательностью символов
  2. Обычно, после однократного использования, очистка отступа: os.width(0);.

  3. ostream_iterator делегаты для operator << для char типов. Этот оператор, в свою очередь, попытается выполнить заполнение, что приведет к путанице.

    Вместо этого используйте ostreambuf_iterator.

Примерно так

ostream& operator<<(ostream& os, const ByteArray& dt) {
    size_t fill = os.width() > dt.len ? os.width() - dt.len : 0;
    if ((os.flags() & ios_base::adjustfield) == ios_base::left) {
        os.write((const char*)dt.ptr, dt.len);
        fill_n(std::ostreambuf_iterator<char>(os), fill, os.fill());
    }
    else {
        fill_n(std::ostreambuf_iterator<char>(os), fill, os.fill());
        os.write((const char*)dt.ptr, dt.len);
    }
    os.width(0);
    return os;
}

Примечание: при тестировании заполнения целесообразно напечатать что-нибудь до и после него.

...