c++c++23std-span

Extracting data from a buffer using ispanstream doesn´t copy the value across when streaming to a bigger variable


I was trying to understand if ispanstream could be used extract data from a buffer.

Let's say that I have a char buffer containing a series of different variables of different types. When I extract from the stream to a variable with a size other than the one of the underlying buffer I get that the destination variable is set to 0; if instead the destination variable is of the same size of the buffer, it gets populated as I would expect.

Here a MWE:

#include <iostream>
#include <array>
#include <spanstream>
#include <cstdint>

union S
{
    int32_t int32_;
    uint64_t uint64_;
    std::array<uint8_t, sizeof(uint64_t)> arr;
};

int main()
{
    char underlyingBuf[15];
    for (uint8_t i = 0; i < 15; i++)
        underlyingBuf[i] = i + 100;
    std::span<char, sizeof(underlyingBuf)> span{ underlyingBuf };
    std::ispanstream iss(span);
    S read_var{ 0LL };
    for (size_t i = 0; i < sizeof(read_var.int32_); ++i)
        iss >> read_var.arr[i];
    //iss >> read_var.int32_;
    std::cout.setf(std::cout.hex, std::cout.basefield);
    std::cout.setf(std::cout.showbase);
    std::cout << read_var.uint64_ << std::endl;
}

Here, the output of this code is 0x67666564, but if I uncomment the line iss >> read_var.int32_; (or if I run only that instead of the for loop) the output is 0.

I was expecting the code to output 0x67666564 if I ran the commented line instead of the for loop, and 0x6B6A6968 if I ran it after the for loop.

  1. Have I misinterpreted how ispanstream works?
  2. If this is the normal behaviour, what is the benefit compared to simply iterating through it with a span?
  3. Why is it setting read_var.int32_ to 0 when calling iss >> read_var.int32_; instead of leaving it unchanged?

I'm relatively new to C++ (especially in its more modern fashions) and I struggle to understand what I got wrong.

I get the same behaviour both with msvc and gcc, so I assume this is how it's supposed to behave. Thanks!

Edit:

as @some programmer dude pointed out, my MWE was a bit naughty in the way I used the union. Here a revised example:

#include <iostream>
#include <array>
#include <spanstream>
#include <cstdint>

int main()
{
    char underlyingBuf[15];
    for (uint8_t i = 0; i < 15; i++)
        underlyingBuf[i] = i + 100;
    std::span<char, sizeof(underlyingBuf)> span{ underlyingBuf };
    std::ispanstream iss(span);
    
    int8_t var1{ 0L };
    int32_t var2{ 0L };
    iss >> var1 >> var2;
    std::cout.setf(std::cout.hex, std::cout.basefield);
    std::cout.setf(std::cout.showbase);
    std::cout << var1 << "\t" << var2 << std::endl;
}

In this case the output is var1 == d and var2 == 0. Why is var2 not being assigned to the content of the buffer?


Solution

  • The buffer contains "defghijklmnopqr".

    iss >> var1 >> var2;
    

    The type of var1 is int8_t which is commonly signed char. Therefore, 'd' (or 100) is read into var1 and an integer is attempted to be read from the string efghijklmnopqr. This isn't integral, so the value of var2 isn't changed and the failbit is set: https://godbolt.org/z/ME7ozdTGY