Changing type without changing bits

后端 未结 2 1686
闹比i
闹比i 2020-12-12 03:03

I want to take a stack variable and reinterpret cast it into an unsigned integer type of the same size in bytes. For example, I might want to take double<

相关标签:
2条回答
  • 2020-12-12 03:39

    If you don't want to have undefined behavior due to violating the aliasing restrictions (C++11 3.10/10) then you need to access the object representations as characters:

    template <typename T>
    int_type<T> caster(const T& value) {
        int_type<T> v;
        static_assert(sizeof(value) == sizeof(v), "");
        std::copy_n(reinterpret_cast<const char*>(&value),
                    sizeof(T),
                    reinterpret_cast<char*>(&v));
        return v;
    }
    

    High quality compilers will optimize the copy away. E.g., this program:

    int main() {
        return caster(3.14f);
    }
    

    effectively optimizes to return 1078523331; on Intel processors.

    0 讨论(0)
  • 2020-12-12 03:44

    Between std::conditional_t and std::enable_if_t I believe that you can compress all your helper and int_type definitions into a self-sufficient caster function:

    template <typename T>
    auto caster(T value){return reinterpret_cast<std::conditional_t<sizeof(T) == sizeof(uint8_t),
                                                                    uint8_t,
                                                                    conditional_t<sizeof(T) == sizeof(uint16_t),
                                                                                  uint16_t,
                                                                                  conditional_t<sizeof(T) == sizeof(uint32_t),
                                                                                                uint32_t,
                                                                                                enable_if_t<sizeof(T) == sizeof(uint64_t),
                                                                                                            uint64_t>>>>&>(value);}
    

    I've validated that this works on both gcc 4.9.2 and Visual Studio 2015, if you only have C++11 support though you can still get this into a self-sufficient caster function:

    template <typename T>
    typename std::conditional<sizeof(T) == sizeof(uint8_t),
                              uint8_t,
                              typename conditional<sizeof(T) == sizeof(uint16_t),
                                                   uint16_t,
                                                   typename conditional<sizeof(T) == sizeof(uint32_t),
                                                                        uint32_t,
                                                                        typename enable_if<sizeof(T) == sizeof(uint64_t),
                                                                                           uint64_t>::type>::type>::type>::type caster(T value){return reinterpret_cast<decltype(caster(value))&>(value);}
    

    This will pick the uint* that has the same sizeof as the type you pass to it and use that.

    I have an explaination of std::enable_if over here that may be helpful to you.

    Obviously this is just useful on types that are 8, 16, 32, or 64-bits in size, but if you feel like expanding it to handle other stuff, just add another conditional_t!


    If you are only ever going to pass in 8, 16, 32, or 64-bit types you can get away with less protection in your template:

    template <typename T>
    auto caster(T value){return reinterpret_cast<std::tuple_element_t<size_t(log2(sizeof(T))), std::tuple<uint8_t,
                                                                                                          uint16_t,
                                                                                                          uint32_t,
                                                                                                          uint64_t>>&>(value);}
    

    This works for C++14, the C++11 equivalent is:

    template <typename T>
    typename std::tuple_element<size_t(log2(sizeof(T))), std::tuple<uint8_t,
                                                                    uint16_t,
                                                                    uint32_t,
                                                                    uint64_t>>::type caster(T value){return reinterpret_cast<decltype(caster(value))&>(value);}
    

    This is less forgiving than the conditional_t/enable_if_t template because of how I am indexing the std::tupple. size_t is an integral type so any type of any size less than 128-bits will cast to a valid std::tuple index. So for example a struct that was 3-bits in size would be cast to a uint16_t, while the desired result would probably have been for it to fail to compile.

    0 讨论(0)
提交回复
热议问题