C++: попытка устранить необработанный цикл с помощью эквивалентного алгоритма STL

Я пытаюсь модернизировать некоторый код C++, придерживаясь основных рекомендаций и публикуя рекомендации ++11. Конкретное руководство, к которому я обращаюсь здесь, состоит в том, чтобы использовать <algorithm> средства вместо необработанных циклов, применяя статические операции к последовательности с целью создания новой последовательности.

Этот первый пример иллюстрирует успех (как я определяю его в этом контексте). Два входных вектора std::byte приходят и один выходит, представляя попарное побитовое XOR для каждого входного вектора, оставляя входные векторы неизмененными. Функция в духе этого вопроса std::transform.

vector<byte> XORSmash(const vector<byte>& first, const vector<byte>& second)
{
    if (first.size() != second.size())
        throw std::invalid_argument("XORSMASH: input vectors were not of equal length\n");

    vector<byte> convolution; convolution.reserve(first.size());

    transform(first.cbegin(), first.cend(), second.cbegin(), back_inserter(convolution),
        [](const byte byte1, const byte byte2) {return byte1 ^ byte2;} );

    return convolution;
}

Однако есть еще одна функция, для которой у меня возникли проблемы с разработкой решения без цикла, которое ничуть не хуже, чем решение с циклом. Эта функция принимает string HexChars (каждый char из которых в конечном итоге передает 4 бита значения) и генерирует vector<byte>, каждый элемент которого содержит содержимое двух HexChars, один в старших 4 битах, один в младших. То, что именно делает функция CharToHexByte, не имеет значения (я включу, если это будет необходимо), просто она принимает совместимый шестнадцатеричный символ и возвращает std::byte с числовым значением шестнадцатеричного символа, т.е. 0-15, загружая только 4 бита. Проблема заключается в том, что входная строка содержит пары шестнадцатеричных символов (каждый из которых представляет собой фрагмент значения), каждый из которых объединяется в один шестнадцатеричный байт. Насколько мне известно, я не могу использовать std::transform, так как итераторы ввода должны будут прыгать на 2 (2 * sizeof(char)//aka container_const_iterator += 2 in this case) каждую итерацию, чтобы извлечь следующую пару символов во входной строке.

TLDR: существует ли алгоритмический способ реализовать следующую функцию без открытого цикла for, который не будет дороже/многословнее решения, приведенного ниже?

vector<byte> UnifyHexNibbles(const string& hexStr)
{
    if (hexStr.size() % 2)
        throw std::invalid_argument("UnfyHxNbl: Input String Indivisible by 8bits. Pad if applicable.\n");

    vector<byte> hexBytes; hexBytes.reserve(hexStr.size() >> 1);
    //can I be eliminated elegantly?
    for (size_t left(0), right(1); right < hexStr.size(); left += 2, right += 2)
        hexBytes.push_back( CharToHexByte(hexStr[left]) << 4 | CharToHexByte(hexStr[right]) );

    return hexBytes;
}

person schulmaster    schedule 10.10.2018    source источник


Ответы (2)


С range-v3 это будет

std::vector<std::byte>
UnifyHexNibbles(const std::string& hexStr)
{
    if (hexStr.size() % 2)
        throw std::invalid_argument("size indivisible by 2.");


    return hexStr
        | ranges::view::chunk(2)
        | ranges::view::transform([](const auto& r)
           {
              return std::byte(CharToHexByte(r[0]) << 4 | CharToHexByte(r[1]));
           });
}

Демо

person Jarod42    schedule 10.10.2018
comment
Я чувствую, что это сродни повороту налево три раза только для того, чтобы не идти направо. Это все еще имеет уязвимости, связанные с циклами, подпиской на константу, основанную на размере (представление фрагмента), с возможностью изменения одного без изменения другого. Несколько педантично, это также больше текста. Наконец, несмотря на то, что адаптеры диапазона реализуют отложенное выполнение, что отлично с точки зрения минимизации копий/промежуточных звеньев, трудно поверить, что эта цепочка адаптеров диапазона будет близка к производительности необработанного цикла (на основе впечатлений, а не знаний о реализации). - person schulmaster; 11.10.2018
comment
С другой стороны, этот ответ увеличивает мою уверенность в том, что ответ на мой первоначальный вопрос просто нет, конкретный случай слишком специфичен для обработки любым алгоритмом stl, которые оптимизированы для общего случая, последовательно потребляя последовательность. - person schulmaster; 11.10.2018
comment
Несколько педантично, это также больше текста.. Да, так как я добавил много пробелов, но символов/символов меньше, чем в оригинале :-) (и я полностью определил имя) (честно говоря, я считаю, что версия с диапазоном легче читается). Тогда я бы хотел версию chunk<2>, которая позволяла бы проверять r[0]/r[1]get<0>(r)). Я думаю, что для производительности время компиляции должно быть хуже, но для времени выполнения я ожидаю, что оно будет аналогичным (возможно, диапазон затрудняет оптимизацию кода). - person Jarod42; 11.10.2018
comment
Я понимаю вашу точку зрения о количестве символов. Тем не менее, петля for хорошо знакома; каждое выражение из трех требуемых хорошо известно и передает хорошо установленный порядок оценки: for(scoped init only once; pre loop bool_stop; post_loop actions) . Благо и проклятие конструкции — независимый контроль пользователя над всеми тремя этапами. Я считаю, что непредвиденные обстоятельства ответственны за парадигму «исключения необработанных петель». Решения, которые не требуют пользовательского цикла, но допускают ту же степень ошибки пользователя, не соответствуют этому настроению. - person schulmaster; 11.10.2018
comment
@schulmaster Все последовательные алгоритмы должны продвигать только итераторы. Затем вы пишете адаптеры итераторов, чтобы делать подобные вещи. Предложение диапазонов просто упрощает вывод последовательности (чтобы она могла быть входными данными для следующего шага). - person Caleth; 11.10.2018

Не существует <algorithm>, который допускает преобразование через непоследовательное потребление ввода с использованием неспециализированных итераторов. Помимо специализации итератора, существуют сторонние и (надеюсь) скоро станут стандартными альтернативами/улучшениями для представления основного STL, такими как диапазоны (репозиторий диапазонов). См. ответ пользователя @Jarod42 для рабочего примера с диапазонами.

person schulmaster    schedule 12.10.2018