Skip to content

Commit e4dac7a

Browse files
committed
Introduce concepts header to libfly
Concepts in general are a much nicer alternative to using std::enable_if (less verbose, and generally provide nicer compilation error messages). Note this includes a few numeric concepts that are defined in the STL by C++20, but Apple's Clang (as of version 13.0.0) does not support these concepts yet.
1 parent 294189f commit e4dac7a

File tree

6 files changed

+204
-0
lines changed

6 files changed

+204
-0
lines changed

build/win/libfly/libfly.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@
209209
<ClInclude Include="..\..\..\fly\task\task_manager.hpp" />
210210
<ClInclude Include="..\..\..\fly\task\task_runner.hpp" />
211211
<ClInclude Include="..\..\..\fly\task\task_types.hpp" />
212+
<ClInclude Include="..\..\..\fly\traits\concepts.hpp" />
212213
<ClInclude Include="..\..\..\fly\traits\traits.hpp" />
213214
<ClInclude Include="..\..\..\fly\types\bit_stream\bit_stream_reader.hpp" />
214215
<ClInclude Include="..\..\..\fly\types\bit_stream\bit_stream_types.hpp" />

build/win/libfly/libfly.vcxproj.filters

+3
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@
220220
<ClInclude Include="..\..\..\fly\task\task_types.hpp">
221221
<Filter>task</Filter>
222222
</ClInclude>
223+
<ClInclude Include="..\..\..\fly\traits\concepts.hpp">
224+
<Filter>concepts</Filter>
225+
</ClInclude>
223226
<ClInclude Include="..\..\..\fly\traits\traits.hpp">
224227
<Filter>traits</Filter>
225228
</ClInclude>

build/win/libfly_unit_tests/libfly_unit_tests.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@
241241
<ClCompile Include="..\..\..\test\system\system.cpp" />
242242
<ClCompile Include="..\..\..\test\system\system_monitor.cpp" />
243243
<ClCompile Include="..\..\..\test\task\task.cpp" />
244+
<ClCompile Include="..\..\..\test\traits\concepts.cpp" />
244245
<ClCompile Include="..\..\..\test\traits\traits.cpp" />
245246
<ClCompile Include="..\..\..\test\types\bit_stream\bit_stream.cpp" />
246247
<ClCompile Include="..\..\..\test\types\concurrency\concurrent_container.cpp" />

build/win/libfly_unit_tests/libfly_unit_tests.vcxproj.filters

+3
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@
116116
<ClCompile Include="..\..\..\test\task\task.cpp">
117117
<Filter>task</Filter>
118118
</ClCompile>
119+
<ClCompile Include="..\..\..\test\traits\concepts.cpp">
120+
<Filter>concepts</Filter>
121+
</ClCompile>
119122
<ClCompile Include="..\..\..\test\traits\traits.cpp">
120123
<Filter>traits</Filter>
121124
</ClCompile>

fly/traits/concepts.hpp

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#pragma once
2+
3+
#include <concepts>
4+
#include <type_traits>
5+
6+
namespace fly {
7+
8+
/**
9+
* Concept that is satisfied when any type in a sequence of types are the same as a specific type.
10+
* Examples:
11+
*
12+
* fly::SameAsAny<int, int, int> // Satisfied.
13+
* fly::SameAsAny<int, int, const int &> // Satisfied.
14+
* fly::SameAsAny<int, int, bool> // Satisfied.
15+
* fly::SameAsAny<int, bool, bool> // Unsatisfied.
16+
*/
17+
template <typename T, typename... Ts>
18+
concept SameAsAny = (std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<Ts>> || ...);
19+
20+
/**
21+
* Concept that is satisfied when all types in a sequence of types are the same as a specific type.
22+
* Examples:
23+
*
24+
* fly::SameAsAll<int, int, int> // Satisfied.
25+
* fly::SameAsAll<int, int, const int &> // Satisfied.
26+
* fly::SameAsAll<int, int, bool> // Unsatisfied.
27+
* fly::SameAsAll<int, bool, bool> // Unsatisfied.
28+
*/
29+
template <typename T, typename... Ts>
30+
concept SameAsAll = (std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<Ts>> && ...);
31+
32+
/**
33+
* Concept that is satisified if the given type is a signed integral type.
34+
*
35+
* This concept will be removed once Apple's Clang supports std::signed_integral.
36+
*/
37+
template <typename T>
38+
concept SignedIntegral = std::is_integral_v<T> && std::is_signed_v<T>;
39+
40+
/**
41+
* Concept that is satisified if the given type is an unsigned integral type.
42+
*
43+
* This concept will be removed once Apple's Clang supports std::unsigned_integral.
44+
*/
45+
template <typename T>
46+
concept UnsignedIntegral = std::is_integral_v<T> && std::is_unsigned_v<T>;
47+
48+
/**
49+
* Concept that is satisified if the given type is a floating-point type.
50+
*
51+
* This concept will be removed once Apple's Clang supports std::floating_point.
52+
*/
53+
template <typename T>
54+
concept FloatingPoint = std::is_floating_point_v<T>;
55+
56+
} // namespace fly

test/traits/concepts.cpp

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#include "fly/traits/concepts.hpp"
2+
3+
#include "catch2/catch_test_macros.hpp"
4+
5+
#include <string>
6+
7+
namespace {
8+
9+
class FooClass
10+
{
11+
};
12+
13+
} // namespace
14+
15+
CATCH_TEST_CASE("Concepts", "[traits]")
16+
{
17+
CATCH_SECTION("Concept: SameAsAny")
18+
{
19+
CATCH_CHECK(fly::SameAsAny<int, int>);
20+
CATCH_CHECK(fly::SameAsAny<int, const int>);
21+
CATCH_CHECK(fly::SameAsAny<const int, int>);
22+
CATCH_CHECK(fly::SameAsAny<const int, const int>);
23+
24+
CATCH_CHECK(fly::SameAsAny<int, int>);
25+
CATCH_CHECK(fly::SameAsAny<int, int &>);
26+
CATCH_CHECK(fly::SameAsAny<int &, int>);
27+
CATCH_CHECK(fly::SameAsAny<int &, int &>);
28+
29+
CATCH_CHECK(fly::SameAsAny<int, int>);
30+
CATCH_CHECK(fly::SameAsAny<int, const int &>);
31+
CATCH_CHECK(fly::SameAsAny<const int &, int>);
32+
CATCH_CHECK(fly::SameAsAny<const int &, const int &>);
33+
34+
CATCH_CHECK(fly::SameAsAny<int, int, int>);
35+
CATCH_CHECK(fly::SameAsAny<int, int, const int>);
36+
CATCH_CHECK(fly::SameAsAny<int, const int, int>);
37+
CATCH_CHECK(fly::SameAsAny<int, const int, const int>);
38+
39+
CATCH_CHECK(fly::SameAsAny<const int, int, int>);
40+
CATCH_CHECK(fly::SameAsAny<const int, int, const int>);
41+
CATCH_CHECK(fly::SameAsAny<const int, const int, int>);
42+
CATCH_CHECK(fly::SameAsAny<const int, const int, const int>);
43+
44+
CATCH_CHECK(fly::SameAsAny<bool, bool, bool>);
45+
CATCH_CHECK(fly::SameAsAny<float, float, float, float>);
46+
CATCH_CHECK(fly::SameAsAny<FooClass, FooClass, FooClass>);
47+
CATCH_CHECK(fly::SameAsAny<std::string, std::string, std::string>);
48+
49+
CATCH_CHECK(fly::SameAsAny<bool, bool, char>);
50+
CATCH_CHECK(fly::SameAsAny<FooClass, FooClass, std::string>);
51+
52+
CATCH_CHECK_FALSE(fly::SameAsAny<int, char>);
53+
CATCH_CHECK_FALSE(fly::SameAsAny<int *, int>);
54+
CATCH_CHECK_FALSE(fly::SameAsAny<bool, char>);
55+
CATCH_CHECK_FALSE(fly::SameAsAny<FooClass, std::string>);
56+
}
57+
58+
CATCH_SECTION("Concept: SameAsAll")
59+
{
60+
CATCH_CHECK(fly::SameAsAll<int, int>);
61+
CATCH_CHECK(fly::SameAsAll<int, const int>);
62+
CATCH_CHECK(fly::SameAsAll<const int, int>);
63+
CATCH_CHECK(fly::SameAsAll<const int, const int>);
64+
65+
CATCH_CHECK(fly::SameAsAll<int, int>);
66+
CATCH_CHECK(fly::SameAsAll<int, int &>);
67+
CATCH_CHECK(fly::SameAsAll<int &, int>);
68+
CATCH_CHECK(fly::SameAsAll<int &, int &>);
69+
70+
CATCH_CHECK(fly::SameAsAll<int, int>);
71+
CATCH_CHECK(fly::SameAsAll<int, const int &>);
72+
CATCH_CHECK(fly::SameAsAll<const int &, int>);
73+
CATCH_CHECK(fly::SameAsAll<const int &, const int &>);
74+
75+
CATCH_CHECK(fly::SameAsAll<int, int, int>);
76+
CATCH_CHECK(fly::SameAsAll<int, int, const int>);
77+
CATCH_CHECK(fly::SameAsAll<int, const int, int>);
78+
CATCH_CHECK(fly::SameAsAll<int, const int, const int>);
79+
80+
CATCH_CHECK(fly::SameAsAll<const int, int, int>);
81+
CATCH_CHECK(fly::SameAsAll<const int, int, const int>);
82+
CATCH_CHECK(fly::SameAsAll<const int, const int, int>);
83+
CATCH_CHECK(fly::SameAsAll<const int, const int, const int>);
84+
85+
CATCH_CHECK(fly::SameAsAll<bool, bool, bool>);
86+
CATCH_CHECK(fly::SameAsAll<float, float, float, float>);
87+
CATCH_CHECK(fly::SameAsAll<FooClass, FooClass, FooClass>);
88+
CATCH_CHECK(fly::SameAsAll<std::string, std::string, std::string>);
89+
90+
CATCH_CHECK_FALSE(fly::SameAsAll<int, char>);
91+
CATCH_CHECK_FALSE(fly::SameAsAll<int *, int>);
92+
CATCH_CHECK_FALSE(fly::SameAsAll<bool, bool, char>);
93+
CATCH_CHECK_FALSE(fly::SameAsAll<FooClass, FooClass, std::string>);
94+
}
95+
96+
CATCH_SECTION("Concept: SignedIntegral")
97+
{
98+
CATCH_CHECK(fly::SignedIntegral<char>);
99+
CATCH_CHECK(fly::SignedIntegral<int>);
100+
101+
CATCH_CHECK_FALSE(fly::SignedIntegral<bool>);
102+
CATCH_CHECK_FALSE(fly::SignedIntegral<unsigned char>);
103+
CATCH_CHECK_FALSE(fly::SignedIntegral<unsigned int>);
104+
CATCH_CHECK_FALSE(fly::SignedIntegral<float>);
105+
CATCH_CHECK_FALSE(fly::SignedIntegral<double>);
106+
CATCH_CHECK_FALSE(fly::SignedIntegral<long double>);
107+
CATCH_CHECK_FALSE(fly::SignedIntegral<std::string>);
108+
CATCH_CHECK_FALSE(fly::SignedIntegral<FooClass>);
109+
}
110+
111+
CATCH_SECTION("Concept: UnsignedIntegral")
112+
{
113+
CATCH_CHECK(fly::UnsignedIntegral<bool>);
114+
CATCH_CHECK(fly::UnsignedIntegral<unsigned char>);
115+
CATCH_CHECK(fly::UnsignedIntegral<unsigned int>);
116+
117+
CATCH_CHECK_FALSE(fly::UnsignedIntegral<char>);
118+
CATCH_CHECK_FALSE(fly::UnsignedIntegral<int>);
119+
CATCH_CHECK_FALSE(fly::UnsignedIntegral<float>);
120+
CATCH_CHECK_FALSE(fly::UnsignedIntegral<double>);
121+
CATCH_CHECK_FALSE(fly::UnsignedIntegral<long double>);
122+
CATCH_CHECK_FALSE(fly::UnsignedIntegral<std::string>);
123+
CATCH_CHECK_FALSE(fly::UnsignedIntegral<FooClass>);
124+
}
125+
126+
CATCH_SECTION("Concept: FloatingPoint")
127+
{
128+
CATCH_CHECK(fly::FloatingPoint<float>);
129+
CATCH_CHECK(fly::FloatingPoint<double>);
130+
CATCH_CHECK(fly::FloatingPoint<long double>);
131+
132+
CATCH_CHECK_FALSE(fly::FloatingPoint<bool>);
133+
CATCH_CHECK_FALSE(fly::FloatingPoint<char>);
134+
CATCH_CHECK_FALSE(fly::FloatingPoint<unsigned char>);
135+
CATCH_CHECK_FALSE(fly::FloatingPoint<int>);
136+
CATCH_CHECK_FALSE(fly::FloatingPoint<unsigned int>);
137+
CATCH_CHECK_FALSE(fly::FloatingPoint<std::string>);
138+
CATCH_CHECK_FALSE(fly::FloatingPoint<FooClass>);
139+
}
140+
}

0 commit comments

Comments
 (0)