How to make a template function in C++ to define a bit representation of any type

88 Views Asked by At
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.

2

There are 2 best solutions below

9
463035818_is_not_an_ai On

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
            // ...
1
vanyabeat On

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;