std::vector<uint8_t> vector1 = { 1, 2, 3, 4 };
I would like to transform the vector above in its uint32_t version. I tried doing:
std::vector<uint32_t> vector2(vector1.begin(), vector2.end());
but this code returns the same array in 32 bit version, so the content is still { 1, 2, 3, 4 }.
What I expect to get is an array of a single value (in this case) of 0x01020304.
Is there any way to achieve that preferably without using for loop?
Thank you.
EDIT:
My solution, don't know if it's a good practice, but from what I learned about vectors it should be completely valid:
std::vector<uint32_t> vector2((uint32_t*)vector1.data(), (uint32_t*)(vector1.data() vector1.size()*sizeof(uint8_t)));
CodePudding user response:
You can use ranges, either by using the range-v3 library, or the C 20 std::ranges.
std::vector<uint8_t> vector1 = { 1, 2, 3, 4, 5, 6, 7, 8 };
std::vector<uint32_t> vec2 =
vector1
| view::chunk(4)
| views::transform(
[](auto&& range){
return accumulate(
range,
static_cast<uint32_t>(0),
[](auto acc, auto i)
{
return acc << 8 | i;
}
);
})
| to<std::vector>();
vec2
will contain {0x1020304, 0x5060708}.
See it on godbolt: https://godbolt.org/z/6z6ehfKbq
CodePudding user response:
I recommend using a loop to do this for simplicity
#include <cstdint>
#include <exception>
#include <vector>
std::uint32_t
combine(std::uint8_t a,
std::uint8_t b,
std::uint8_t c,
std::uint8_t d)
{
return (std::uint32_t(a) << 24) |
(std::uint32_t(b) << 16) |
(std::uint32_t(c) << 8) |
std::uint32_t(d);
}
std::vector<std::uint32_t>
combine_vector(const std::vector<std::uint8_t>& items)
{
if (items.size() % 4 != 0)
throw std::exception();
std::vector<std::uint32_t> ret;
ret.reserve(items.size() / 4);
for (std::size_t i = 0; i < items.size(); i = 4) {
ret.push_back(
combine(items[i 0],
items[i 1],
items[i 2],
items[i 3]));
}
return ret;
}
I suspect you would need to implement a custom iterator type to do this without using a raw loop.
I made such an iterator
#include <iterator>
class combine_iterator
{
public:
using value_type = std::uint32_t;
using reference = const value_type&;
using pointer = const value_type*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
combine_iterator(const std::uint8_t* data, std::size_t count) :
m_data(data)
{
if (count % 4 != 0)
throw std::exception();
if (count > 0)
*this;
}
reference
operator*() const
{
return m_cur_val;
}
pointer
operator->() const
{
return &m_cur_val;
}
friend combine_iterator&
operator (combine_iterator& rhs)
{
std::uint8_t a = *(rhs.m_data );
std::uint8_t b = *(rhs.m_data );
std::uint8_t c = *(rhs.m_data );
std::uint8_t d = *(rhs.m_data );
rhs.m_cur_val = combine(a, b, c, d);
return rhs;
}
friend combine_iterator
operator (combine_iterator& lhs, int)
{
auto cp = lhs;
lhs;
return cp;
}
friend bool
operator!=(const combine_iterator& lhs, const combine_iterator& rhs)
{
return (lhs.m_data != rhs.m_data);
}
private:
const std::uint8_t* m_data;
std::uint32_t m_cur_val;
};
int main()
{
std::vector<std::uint8_t> data = { 1, 2, 3, 4, 5, 6, 7, 8 };
std::vector<std::uint32_t> res(
combine_iterator(data.data(), data.size()),
combine_iterator(data.data() data.size(), 0));
}
The iterator contains at least one bug. I'm leaving the bugs in as an educational lesson why using a loop if often easier to get correct than implementing custom iterators. Custom iterators should ideally only be created if we create a container which needs it, or the mental overhead of creating a custom iterator can be justified.