0
template<typename T>
std::string bit_representation(T &&type) {
    uint8_t data[sizeof(T)];
    if constexpr (std::is_copy_constructible_v<T>) {
        T tmp(type);
        std::memcpy(&data, &tmp, sizeof(T));
    } else if (std::is_move_constructible_v<T>) {
        T tmp(std::move(type));
        std::memcpy(&data, &tmp, sizeof(T));
    }

    auto get_byte_repr = [](uint8_t byte) -> std::string {
        std::string s;
        constexpr size_t byte_size = sizeof(uint8_t) * 8;
        s.resize(byte_size);
        for (int i = byte_size - 1; i >= 0; --i) {
            s[i] = ((byte & 1) == 1) ? '1' : '0';
            byte >>= 1;
        }
        return s;
    };
    std::string result;

    for (int i = sizeof(T) - 1; i >= 0; --i) {
        result += get_byte_repr(data[i]);
    }
    return result;
}

I wrote simple bit_representation function, for simple types all good but i want to write uniform template function for pass std::initializer_list<T> too.

    uint8_t u8 = 0xAF;
    std::cout << bit_representation(u8) << std::endl; // 10101111 ok
    std::cout << bit_representation((uint8_t) 0xAF) << std::endl; // 10101111 ok
    std::cout << bit_representation((short) 0xAF) << std::endl; // 0000000010101111 ok
    double d = 2.56;
    // 0100000000000100011110101110000101000111101011100001010001111011 = d its ok iee-754
    std::cout << bit_representation(d) << std::endl;
#pragma pack(1)
    struct {
        bool c = true;
        int a = 0x00FF00FF;
    } name;
#pragma pop()
    // 0000000011111111000000001111111100000001 // really ok
    std::cout << bit_representation(name) << std::endl; //  ok
    std::cout << bit_representation(true) << std::endl; // 00000001 ok
    std:cout << bit_representation({1,2,3,4}) << std::endl; /* error candidate template ignored: couldn't infer template argument 'T'
std::string bit_representation(T &&type) {*/

But bit_representation({1,2,3,4}) not working.. I thing need write SFINAE wrapper for compile time detecting if type is initializer_list<T>.

I expecting bit_representation({1,2,3,4}) -> std::initializer_list to memory repr -> 00000000000000000000000000000001000000000000000000000000000000100000000000000000000000000000001100000000000000000000000000000100

How to deduce std::inititalizer_list parameters and write special logic for this.

vanyabeat
  • 1
  • 1

2 Answers2

1

std::initializer_list is somewhat special. In particular (from cppreference):

A std::initializer_list object is automatically constructed when:

  • a braced-init-list is used to list-initialize an object, where the corresponding constructor accepts an std::initializer_list parameter
  • a braced-init-list is used as the right operand of assignment or as a function call argument, and the corresponding assignment operator/function accepts an std::initializer_list parameter
  • a braced-init-list is bound to auto, including in a ranged for loop

bit_representation({1,2,3,4}) is none of this. {1,2,3,4} is not a std::initializer_list. Only in certain contexts {1,2,3,4} causes a std::initializer_list to be automatically constructed. In all other contexts {1,2,3,4} has no type that could be deduced.

You can call the function with a std::initializer_list like this:

bit_representation(std::initializer_list<int>{1,2,3,4});

Moreover, consider that any object can be viewed as an array of bytes. No copying to an array is needed. This is due to the exception for char, byte and unsigned char for strict aliasing, as explained here: https://en.cppreference.com/w/cpp/language/reinterpret_cast. It is also unclear why your function does not use its argument type (rather misleading name) directly but makes another unnecessary copy tmp before copying to the array.

This might give you a better start (untested):

std::string bit_representation(const T& t) {
       unsigned char* byte_repr = reinterpret_cast<unsigned char*>(&t);
       for (size_t i=0; i< sizeof(T); ++i) {
            byte_repr[i]; // <- i-th byte of t
            // ...
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Thanx i write new function – vanyabeat Apr 24 '23 at 18:45
  • its crazy but its working, i add overloaded definition ```cpp template std::string bit_representation(std::initializer_list ilist) { std::string result; for (const auto item: ilist) { result += bit_representation(item); } return result; } ``` – vanyabeat Apr 24 '23 at 18:52
  • 1
    He could overload the stream operator to std::byte and only return a span of std::byte. The c++ standard also defines [std::as_bytes](https://en.cppreference.com/w/cpp/container/span/as_bytes) – Fabian Keßler Apr 24 '23 at 18:53
  • @vanyabeat what is crazy? – 463035818_is_not_an_ai Apr 24 '23 at 18:53
  • @FabianKeßler yes there are nicer facilites in c++20. However, I assume c++17 if nothing else tagged, also because I am not fluent with c++20. And I have the impression that OP wants something self-made, I might be wrong though – 463035818_is_not_an_ai Apr 24 '23 at 18:54
  • @vanyabeat consider the comment by Marek above. With the code in the comment you create the string representation of the integers contained in the `std::initializer_list` not the representation of the `std::initializer_list`. This is same difference as the bytes of eg a `std::string` and the bytes of the characters it contains. Your function will do the former for the `std::string` – 463035818_is_not_an_ai Apr 24 '23 at 18:57
  • @FabianKeßler @463035818_is_not_a_number after simple overload, `bit_representation({1, 2, 3, 4})` working and `bit_representation({uint8_t(0xAB), uint8_t(0x0F), uint8_t(0xAA)})` -> `101010110000111110101010` – vanyabeat Apr 24 '23 at 18:59
  • @yanyabeat you must understand the differnce between "byte resprentation of `std::initalizer_list{1,2,3,4}`" and "byte representation of the 4 integers 1,2,3 and 4". Your overload does the latter. Me and others aassumed the first because that would consistent with what your function does eg to a `std::string("abc")`. It will not produce the byte representation of a, b and c – 463035818_is_not_an_ai Apr 24 '23 at 19:03
  • @yanyabeat concering "crazy" consider that before c++11 it was impossible to make `bit_representation({1,2,3,4})` work at all. The language had to change, and it was made possible by the automatic construction of a `std::initializer_list` as described in the quote in the answer. – 463035818_is_not_an_ai Apr 24 '23 at 19:07
0

In the end, this solution turned out, but I'm not sure if it's right SOLUTION

template<typename T>
std::string bit_representation(T &&type) {
    auto ptr = reinterpret_cast<const uint8_t *>(&type);
    auto get_byte_repr = [](uint8_t byte) -> std::string {
        std::string s;
        constexpr size_t byte_size = sizeof(uint8_t) * 8;
        s.resize(byte_size);
        for (int i = byte_size - 1; i >= 0; --i) {
            s[i] = ((byte & 1) == 1) ? '1' : '0';
            byte >>= 1;
        }
        return s;
    };
    std::string result;
    for (int i = sizeof(T) - 1; i >= 0; --i) {
        result += get_byte_repr(ptr[i]);
    }
    return result;
}

template<typename T>
std::string bit_representation(std::initializer_list<T> ilist) {
    std::string result;
    for (const auto item: ilist) {
        result += bit_representation(item);
    }
    return result;
}

Outputs

    unsigned short carr[] = {0xDEAD, 0xBEEF};
    struct mZ {
        mZ(uint8_t value) {
            auto ptr = reinterpret_cast<uint8_t *>(this);
            std::fill(ptr, ptr + 1, value);
        }

        mZ() = delete;

        mZ(const mZ &o) = delete;

        mZ(mZ &&o) = default;

        uint8_t a: 1;
        uint8_t b: 1;
        uint8_t c: 1;
        uint8_t d: 1;
        uint8_t e: 1;
        uint8_t f: 1;
        uint8_t g: 1;
        uint8_t h: 1;
    };
    
    /*1111111110101010*/
    std::cout << bit_representation((short) 0xFFAA) << std::endl;
    /*11111110*/
    std::cout << bit_representation(mZ(254)) << std::endl;
    /*00000000000000000000000000000001000000000000000000000000000000100000000000000000000000000000001100000000000000000000000000000100*/
    std::cout << bit_representation({1, 2, 3, 4}) << std::endl;
    /*10111110111011111101111010101101*/
    std::cout << bit_representation(carr) << std::endl;
vanyabeat
  • 1
  • 1