1

I'm trying to convert any object to std::vector<bool> (representing bits set in memory to strore the object).

So, for uint16_t(0x1234), I want to get std::vector<bool> ‭0001001000110100‬. And for float(3.14f), which is ‭0x4048F5C3‬ in memory, I want to get std::vector<bool> ‭‭01000000010010001111010111000011‬.

Based on this post, I tried this:

template<typename T>
std::vector<bool> val2Array(T val)
{
    size_t count = sizeof(T)*CHAR_BIT;
    std::vector<bool> result( count );
    for (size_t i = 0; i < count; i++)
    {
        T temp = 1 << (count - i - 1);
        if ( val & temp )
            result[i] = true;
        else
            result[i] = false;
    }

    return result;
}

This works fine, but I get an error when T is not numerical (float), due to << operator.

Is there any other way to convert any value to a std::vector<bool>? I found lots of code showing how to convert std::vector<bool> to a value but not the way around.

xskxzr
  • 12,442
  • 12
  • 37
  • 77
jpo38
  • 20,821
  • 10
  • 70
  • 151
  • `memcpy` does it for you preserving the byte order. – Maxim Egorushkin Mar 23 '20 at 14:29
  • You can use `std::bit_cast` if you have C++20. – Adrian Mole Mar 23 '20 at 14:31
  • 1
    if you know the size of the object why not using `std::bitset` ? – yayg Mar 23 '20 at 14:31
  • 1
    @MaximEgorushkin: What would the code look like? I don't believe std::vector is compatible with memcpy. – jpo38 Mar 23 '20 at 14:34
  • @yayg: Because I actually want to store many values in the vector (many uint16, floats....). – jpo38 Mar 23 '20 at 14:35
  • @yayg: I tried to use std::bitset and later copy it to std::vector but then the bits order was reverted. And also std::bitset constructor taked a numerical value, can't be a float value. – jpo38 Mar 23 '20 at 14:36
  • 1
    Do you want to preserve the native byte order, or convert to big-endian representation (your example shows big-endian representation)? – Maxim Egorushkin Mar 23 '20 at 14:42
  • @AdrianMole: "*You can use std::bit_cast if you have C++20.*" No, you can't; even if `vector` was trivially copyable (which it is not), it allocates an array of its values outside of the object. So even if you did some kind of bit-cast, it wouldn't somehow manifest itself into the `vector` allocated storage. – Nicol Bolas Mar 23 '20 at 14:42
  • @NicolBolas Actually, I was thinking of using `bit_cast` to copy the given data type into a local variable that *does* have the bit-shift operator, rather than directly into the `vector` container itself. But I wasn't clear, so your point is valid. – Adrian Mole Mar 23 '20 at 14:46
  • @MaximEgorushkin: In the end I'll need big and little endian support (will add a parameter), so any would be OK (I will swap data when it needs to). – jpo38 Mar 23 '20 at 14:48
  • 2
    it doesn't sound that you need bit access. Just use `vector` – Sopel Mar 23 '20 at 14:50
  • @Sopel, I may also push some individual bits in the vector at some point. – jpo38 Mar 23 '20 at 14:53
  • The internal representation of `vector` is undocumented. Due to this all you can do with it is access it bit-by-bit. If you want good performance, roll your own container, based on `vector` or something like that. – rustyx Mar 23 '20 at 15:38
  • @rustyx: I may end up with more than 64bits in the end (I may have up to 100 bytes), so I really need a vector if bits... – jpo38 Mar 23 '20 at 15:42
  • What do you need that `std::vector` for? – Maxim Egorushkin Mar 23 '20 at 15:43
  • @MaximEgorushkin: Pushing data (each data being a few bits, possibly less than 8 and possibly not a multiple of 8) to a vector of bits. When done, I'll complete it with zeros to end up with a multiple of 8. Will finally convert to an array of bytes to be send to another module who will unpack that. – jpo38 Mar 23 '20 at 15:46

1 Answers1

-1

What you described reminds me of reinterpret_cast. You just want to see the bits, regardless of their underlying interpretation, so reinterpret those bits as something that std::bitset can accept and put them in your vector.

#include <bitset>
#include <vector>
#include <iostream>

constexpr int CHAR_BIT = 8;

template<typename T>
std::vector<bool> val2Array(T val)
{
    uintmax_t i = reinterpret_cast<uintmax_t&>(val);
    auto bits = std::bitset<sizeof(uintmax_t)*CHAR_BIT>(i);

    size_t count = sizeof(T)*CHAR_BIT;
    std::vector<bool> result( count );

    for(size_t i = 0; i < count; ++i)
        result[count-i-1] = bits[i];

    return result;
}

int main()
{
    auto i = uint16_t(0x1234);
    auto iv = val2Array(i);
    for(auto&& e : iv) std::cout << e;
    std::cout << std::endl;


    auto f = float(3.14f);
    auto fv = val2Array(f);
    for(auto&& e : fv) std::cout << e;
    std::cout << std::endl;
}

output

0001001000110100
01000000010010001111010111000011

For converting the bitset to a vector, I found this post that cites a blogpost for a bitset_iterator that might be usefull

Philip Nelson
  • 1,027
  • 12
  • 28
  • Tried that but I was missing the reinterpret_cast. Will test this deeply tomorrow. By the way, wouldn't uintmax_t be better than uint64_t? – jpo38 Mar 23 '20 at 15:48
  • yeah, that makes sense – Philip Nelson Mar 23 '20 at 15:49
  • `reinterpret_cast(val)` breaks aliasing rules - UB. – Maxim Egorushkin Mar 23 '20 at 15:56
  • What does it mean to "break aliasing rules"? What does that imply beyond UB? – Philip Nelson Mar 23 '20 at 16:10
  • You can find answers about C++ strict aliasing on this very website. A fix is to simply copy the object representation into `unsigned char[sizeof(T)]` with `memcpy` and access it instead. – Maxim Egorushkin Mar 23 '20 at 16:18
  • Strict aliasing aside, this won't work at all on a big-endian machine when `sizeof(T) < sizeof(uintmax_t)` You'll end up missing `T` bytes entirely – rustyx Mar 23 '20 at 16:50
  • Would using a union fix the aliasing issue? [this](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) post describes it as a way around the problem. – Philip Nelson Mar 23 '20 at 20:15
  • @PhilipNelson No, type punning through unions like that is only allowed in C, not in C++. That post is for C. – Hatted Rooster Mar 23 '20 at 20:21
  • cpp core guidelines says you can use reinterpret cast [here](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Ru-pun) as a way of type punning without a union – Philip Nelson Mar 23 '20 at 20:34
  • yet, compiling with `-fstrict-aliasing` yields the following warning: `dereferencing type-punned pointer will break strict-aliasing rules` – Philip Nelson Mar 23 '20 at 20:39
  • @PhilipNelson: If this code has UB, I wn't test it, as I'm targetting different platforms... – jpo38 Mar 24 '20 at 09:27
  • let me know if you find a solution. I'm still curious how to resolve this. – Philip Nelson Mar 26 '20 at 17:28