How to make fixed sized transform + join be sized manually?

117 Views Asked by At

I am implementing a function that takes a range with values of type std::byte and returns a view on bits of given range. I've implemented two specializations: for random_access range and not random_access:

#include <iostream>
#include <ranges>
#include <vector>
#include <iostream>
#include <list>
#include <algorithm>

using std::views::iota;
using std::views::join;
using std::views::transform;
using std::views::reverse;

template <std::ranges::range Rng>
  requires std::ranges::random_access_range<Rng> &&
           std::is_same_v<std::ranges::range_value_t<Rng>, std::byte>
auto to_bits(const Rng &rng) {
  return iota(std::size_t{0}, rng.size() * 8) |
         transform([&rng](std::size_t i) {
           const std::size_t byteIdx = i / 8;
           const std::size_t bitIdx = 7 - i % 8;
           return (rng[byteIdx] >> bitIdx) & std::byte{1};
         });
}

template <std::ranges::range Rng>
  requires(!std::ranges::random_access_range<Rng>) &&
          std::is_same_v<std::ranges::range_value_t<Rng>, std::byte>
auto to_bits(const Rng &rng) {
  return rng | transform([](const std::byte &b) {
           return iota(0, 8)
                  | reverse
                  | transform([&b](auto i) -> std::byte {
                                return (b >> i) & std::byte{1};
                              });
         }) |
         join;
}

The second is for not random_access ranges. The usage of the function is supposed to be the following:

int main() {
  auto v = std::vector{std::byte{0}, std::byte{42}};
  auto vBits = to_bits(v);
  static_assert(std::ranges::random_access_range<decltype(vBits)>);
  static_assert(std::ranges::sized_range<decltype(vBits)>);
  static_assert(std::is_same_v<std::ranges::range_value_t<decltype(vBits)>, std::byte>);

  auto l = std::list{std::byte{0}, std::byte{42}};
  auto lBits = to_bits(l);

  static_assert(std::ranges::input_range<decltype(lBits)>);
  // static_assert(std::ranges::sized_range<decltype(lBits)>);  // Why not be sized?
  static_assert(std::is_same_v<std::ranges::range_value_t<decltype(lBits)>, std::byte>);

  std::cout << (std::ranges::equal(vBits, lBits) ? "Equal." : "Not equal.") << std::endl;

  return 0;
}

The problem with second variant is the following: if a given range is sized we can say the size of a view, but join is not sized. How to make view be sized manually? Is it a good idea to implement a view with manually given size and that requires an argument view with no size?

0

There are 0 best solutions below