Skip to content

Commit 2cfecc5

Browse files
committed
Added bit_cast
1 parent 02985f4 commit 2cfecc5

File tree

3 files changed

+193
-73
lines changed

3 files changed

+193
-73
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
//
2+
// Copyright (c) 2022 Slaven Falandys
3+
//
4+
// This software is provided 'as-is', without any express or implied
5+
// warranty. In no event will the authors be held liable for any damages
6+
// arising from the use of this software.
7+
//
8+
// Permission is granted to anyone to use this software for any purpose,
9+
// including commercial applications, and to alter it and redistribute it
10+
// freely, subject to the following restrictions:
11+
//
12+
// 1. The origin of this software must not be misrepresented; you must not
13+
// claim that you wrote the original software. If you use this software
14+
// in a product, an acknowledgment in the product documentation would be
15+
// appreciated but is not required.
16+
// 2. Altered source versions must be plainly marked as such, and must not be
17+
// misrepresented as being the original software.
18+
// 3. This notice may not be removed or altered from any source distribution.
19+
//
20+
21+
#ifndef SFL_DETAIL_BIT_CAST_HPP_INCLUDED
22+
#define SFL_DETAIL_BIT_CAST_HPP_INCLUDED
23+
24+
#include <sfl/detail/cpp.hpp>
25+
26+
#include <cstring> // memcpy
27+
#include <type_traits> // is_trivially_copyable
28+
29+
#if SFL_CPP_VERSION >= SFL_CPP_20
30+
#include <bit> // bit_cast
31+
#endif
32+
33+
namespace sfl
34+
{
35+
36+
namespace dtl
37+
{
38+
39+
///////////////////////////////////////////////////////////////////////////////
40+
///////////////////////////////////////////////////////////////////////////////
41+
// C++11 implementation
42+
///////////////////////////////////////////////////////////////////////////////
43+
///////////////////////////////////////////////////////////////////////////////
44+
45+
template <class To, class From>
46+
To bit_cast_impl_11(const From& src) noexcept
47+
{
48+
static_assert(sizeof(To) == sizeof(From), "Size mismatch");
49+
static_assert(std::is_trivially_copyable<From>::value, "From must be trivially copyable");
50+
static_assert(std::is_trivially_copyable<To>::value, "To must be trivially copyable");
51+
52+
To dst;
53+
std::memcpy(&dst, &src, sizeof(To));
54+
return dst;
55+
}
56+
57+
///////////////////////////////////////////////////////////////////////////////
58+
///////////////////////////////////////////////////////////////////////////////
59+
// C++20 implementation
60+
///////////////////////////////////////////////////////////////////////////////
61+
///////////////////////////////////////////////////////////////////////////////
62+
63+
#if SFL_CPP_VERSION >= SFL_CPP_20
64+
65+
template <class To, class From>
66+
constexpr To bit_cast_impl_20(const From& from) noexcept
67+
{
68+
return std::bit_cast<To>(from);
69+
}
70+
71+
#endif // SFL_CPP_VERSION >= SFL_CPP_20
72+
73+
///////////////////////////////////////////////////////////////////////////////
74+
///////////////////////////////////////////////////////////////////////////////
75+
// BIT CAST
76+
///////////////////////////////////////////////////////////////////////////////
77+
///////////////////////////////////////////////////////////////////////////////
78+
79+
template <class To, class From>
80+
SFL_CONSTEXPR_20
81+
To bit_cast(const From& from) noexcept
82+
{
83+
#if SFL_CPP_VERSION >= SFL_CPP_20
84+
return sfl::dtl::bit_cast_impl_20<To>(from);
85+
#else
86+
return sfl::dtl::bit_cast_impl_11<To>(from);
87+
#endif
88+
}
89+
90+
} // namespace dtl
91+
92+
} // namespace sfl
93+
94+
#endif // SFL_DETAIL_BIT_CAST_HPP_INCLUDED

include/sfl/hash.hpp

Lines changed: 8 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#ifndef SFL_HASH_HPP_INCLUDED
2222
#define SFL_HASH_HPP_INCLUDED
2323

24+
#include <sfl/detail/bit/bit_cast.hpp>
2425
#include <sfl/detail/type_traits/enable_if_t.hpp>
2526
#include <sfl/detail/cpp.hpp>
2627

@@ -30,10 +31,6 @@
3031
#include <limits> // numeric_limits
3132
#include <type_traits> // is_enum, is_integral, is_floating_point
3233

33-
#if SFL_CPP_VERSION >= SFL_CPP_20
34-
#include <bit> // bit_cast
35-
#endif
36-
3734
namespace sfl
3835
{
3936

@@ -84,22 +81,7 @@ struct hash_impl<T, sfl::dtl::enable_if_t<std::is_floating_point<T>::value && si
8481
value = std::numeric_limits<T>::quiet_NaN();
8582
}
8683

87-
#if SFL_CPP_VERSION >= SFL_CPP_20
88-
if (std::is_constant_evaluated())
89-
{
90-
return std::bit_cast<std::uint32_t>(value);
91-
}
92-
else
93-
#endif
94-
{
95-
union
96-
{
97-
T t;
98-
std::uint32_t a;
99-
} u;
100-
u.t = value;
101-
return u.a;
102-
}
84+
return sfl::dtl::bit_cast<std::uint32_t>(value);
10385
}
10486
};
10587

@@ -121,22 +103,7 @@ struct hash_impl<T, sfl::dtl::enable_if_t<std::is_floating_point<T>::value && si
121103
value = std::numeric_limits<T>::quiet_NaN();
122104
}
123105

124-
#if SFL_CPP_VERSION >= SFL_CPP_20
125-
if (std::is_constant_evaluated())
126-
{
127-
return std::bit_cast<std::uint32_t>(value);
128-
}
129-
else
130-
#endif
131-
{
132-
union
133-
{
134-
T t;
135-
std::uint32_t a;
136-
} u;
137-
u.t = value;
138-
return u.a;
139-
}
106+
return sfl::dtl::bit_cast<std::uint32_t>(value);
140107
}
141108
};
142109

@@ -158,27 +125,10 @@ struct hash_impl<T, sfl::dtl::enable_if_t<std::is_floating_point<T>::value && si
158125
value = std::numeric_limits<T>::quiet_NaN();
159126
}
160127

161-
#if SFL_CPP_VERSION >= SFL_CPP_20
162-
if (std::is_constant_evaluated())
163-
{
164-
std::uint64_t bits = std::bit_cast<std::uint64_t>(value);
165-
std::size_t h1 = static_cast<std::size_t>(bits);
166-
std::size_t h2 = static_cast<std::size_t>(bits >> 32);
167-
return h1 ^ (h2 + 0x9e3779b9u + (h1 << 6) + (h1 >> 2));
168-
}
169-
else
170-
#endif
171-
{
172-
union
173-
{
174-
T t;
175-
std::uint64_t a;
176-
} u;
177-
u.t = value;
178-
std::size_t h1 = static_cast<std::size_t>(u.a);
179-
std::size_t h2 = static_cast<std::size_t>(u.a >> 32);
180-
return h1 ^ (h2 + 0x9e3779b9u + (h1 << 6) + (h1 >> 2));
181-
}
128+
std::uint64_t bits = sfl::dtl::bit_cast<std::uint64_t>(value);
129+
std::size_t h1 = static_cast<std::size_t>(bits);
130+
std::size_t h2 = static_cast<std::size_t>(bits >> 32);
131+
return h1 ^ (h2 + 0x9e3779b9u + (h1 << 6) + (h1 >> 2));
182132
}
183133
};
184134

@@ -200,22 +150,7 @@ struct hash_impl<T, sfl::dtl::enable_if_t<std::is_floating_point<T>::value && si
200150
value = std::numeric_limits<T>::quiet_NaN();
201151
}
202152

203-
#if SFL_CPP_VERSION >= SFL_CPP_20
204-
if (std::is_constant_evaluated())
205-
{
206-
return std::bit_cast<std::uint64_t>(value);
207-
}
208-
else
209-
#endif
210-
{
211-
union
212-
{
213-
T t;
214-
std::uint64_t a;
215-
} u;
216-
u.t = value;
217-
return u.a;
218-
}
153+
return sfl::dtl::bit_cast<std::uint64_t>(value);
219154
}
220155
};
221156

test/src/bit/bit_cast.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#undef NDEBUG // This is very important. Must be in the first line.
2+
3+
#include "sfl/detail/bit/bit_cast.hpp"
4+
5+
#include <cstdint>
6+
#include <cstdlib>
7+
#include <iostream>
8+
#include <limits>
9+
#include <random>
10+
#include <type_traits>
11+
12+
#if SFL_CPP_VERSION >= SFL_CPP_20
13+
14+
template <typename T>
15+
void test(std::size_t n)
16+
{
17+
using unsigned_t = std::conditional_t
18+
<
19+
sizeof(T) == 4,
20+
std::uint32_t,
21+
std::conditional_t
22+
<
23+
sizeof(T) == 8,
24+
std::uint64_t,
25+
std::false_type
26+
>
27+
>;
28+
29+
static_assert(!std::is_same_v<unsigned_t, std::false_type>);
30+
31+
std::random_device rd;
32+
33+
std::mt19937 gen(rd());
34+
35+
std::uniform_int_distribution<unsigned_t> dist
36+
(
37+
std::numeric_limits<unsigned_t>::min(),
38+
std::numeric_limits<unsigned_t>::max()
39+
);
40+
41+
for (std::size_t i = 0; i < 10; ++i)
42+
{
43+
for (std::size_t j = 0; j < (n / 10); ++j)
44+
{
45+
const T value = static_cast<T>(dist(gen));
46+
47+
const unsigned_t h_11 = sfl::dtl::bit_cast_impl_11<unsigned_t>(value);
48+
const unsigned_t h_20 = sfl::dtl::bit_cast_impl_20<unsigned_t>(value);
49+
50+
if (h_11 != h_20)
51+
{
52+
std::cout << "ERROR: Hash mismatch\n"
53+
<< " - value: " << value << "\n"
54+
<< " - h_11: " << h_11 << "\n"
55+
<< " - h_20: " << h_20 << std::endl;
56+
std::abort();
57+
}
58+
}
59+
60+
std::cout << " " << ((i + 1) * 10) << "%" << std::endl;
61+
}
62+
}
63+
64+
int main()
65+
{
66+
constexpr std::size_t n = 1'000'000;
67+
68+
std::cout << "Testing with `int`..." << std::endl;
69+
test<int>(n);
70+
71+
std::cout << "Testing with `long`..." << std::endl;
72+
test<long>(n);
73+
74+
std::cout << "Testing with `long long`..." << std::endl;
75+
test<long long>(n);
76+
77+
std::cout << "Testing with `float`..." << std::endl;
78+
test<float>(n);
79+
80+
std::cout << "Testing with `double`..." << std::endl;
81+
test<double>(n);
82+
}
83+
84+
#else // before C++20
85+
86+
int main()
87+
{
88+
// Nothing to do
89+
}
90+
91+
#endif // before C++20

0 commit comments

Comments
 (0)