schulmaster
schulmaster

Reputation: 443

C++: attempt to eliminate raw loop with equivalent STL algorithm

I'm trying to modernize some C++ code, adhering to core guidelines and post ++11 advice. The specific guideline I'm addressing here is to use <algorithm> facilities in lieu of raw loops applying static operations across a sequence with the aim of producing a new sequence.

This first example is illustrates success(as I define it in this context). Two input vectors of std::byte come in , and one comes out, representing the pairwise bitwise XORing of each input vector, leaving the input vectors unmodified. The function in the spirit of this question is 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;
}

However, there's another function for which I'm having trouble devising a non loop solution that isn't handedly worse than the loop one. This function takes in a string of HexChars(each char of which ultimately conveys 4 bits of value), and generates a vector<byte>, each element of which contains the contents of two HexChars, one in the high 4 bits, one in the low. What the CharToHexByte function does exactly isnt pertinent(I'll include if it becomes necessary), just that it takes a compliant hex character ,and returns an std::byte, with the hex character's numeric value, ie 0-15, loading only 4 bits. The issue is the input string has pairs of hex chars(each a nibble of value), each of which unify into a single hex byte. I cannot use std::transform, to my knowledge, since the input iterators would have to jump by 2 (2 * sizeof(char)//aka container_const_iterator += 2 in this case) each iteration, to extract the next pair of chars in the input string.

TLDR: Is there an algorithmic way to implement the following function w/o the exposed for loop, that isn't handedly more expensive/verbose than the solution below?

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

Upvotes: 3

Views: 356

Answers (2)

schulmaster
schulmaster

Reputation: 443

There is no <algorithm> that allows for transformation via non-consecutive input consumption, using non-specialized iterators. Aside from specializing an iterator, there are third-party, and (hopefully) soon to be standard alternatives/enhancements to present core STL, such as ranges(ranges repository). See User @Jarod42's answer for a working example with ranges.

Upvotes: 0

Jarod42
Jarod42

Reputation: 217235

With range-v3, it would be

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

Demo

Upvotes: 4

Related Questions