GCC Code Coverage Report


Directory: ./
File: TestStrongType.cpp
Date: 2026-03-21 20:51:59
Exec Total Coverage
Lines: 55 55 100.0%
Functions: 19 19 100.0%
Branches: 51 240 21.2%

Line Branch Exec Source
1 #include "StrongOperators.hpp"
2
3 namespace Inherited {
4 #include "StrongType.hpp"
5 } // namespace Inherited
6
7 namespace Tagged {
8 #include "TaggedStrongType.hpp"
9 } // namespace Tagged
10
11 #include <gtest/gtest.h>
12
13 // Good: Use a simple struct for a strong type, comment expresses intent.
14 // Use simple [aggregate initialization](https://en.cppreference.com/w/cpp/language/aggregate_initialization)
15 struct Mass {
16 double asDouble{0.0};
17 }; // Strong type.
18
19 // Better: Use inheritance so that code expresses intent.
20 // [Express ideas directly in code](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#p1-express-ideas-directly-in-code)
21 #if __cplusplus >= 201703L // since C++17
22 struct Length : Inherited::StrongType<double> {};
23 struct Width : Length {};
24 struct Height : Length {};
25 using Area = Tagged::StrongType<double, struct AreaTag>;
26 #else // until C++17
27 struct Length : Inherited::StrongType<double> {
28 using base::base;
29 };
30 struct Width : Length {
31 using Length::Length;
32 };
33 struct Height : Length {
34 using Length::Length;
35 };
36 using Area = Tagged::StrongType<double, struct AreaTag>;
37 #endif // C++17
38
39 ////////////////////////////////////////////////////////////////////////////////
40
41 // Bad: Use opaque scoped enum for strong identifier type, comment expresses intent.
42 enum class RedId : uintmax_t { null = 0 }; // Strong identifier.
43
44 // Bad: Use MACRO because scoped enum does not allow inheritance.
45 // [Don’t use macros for constants or “functions”](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es31-dont-use-macros-for-constants-or-functions)
46 // NOLINTNEXTLINE(*-macro-usage)
47 #define STRONG_IDENTIFIER(type) enum class type : uintmax_t { null = 0 }
48
49 STRONG_IDENTIFIER(BlueId);
50
51 // Generate the next ID.
52 template <typename E>
53 8 typename std::enable_if<std::is_enum<E>::value, E>::type next() {
54 static auto previous =
55 static_cast<typename std::underlying_type<E>::type>(E::null);
56 8 return static_cast<E>(++previous);
57 }
58
59 // Better: Use inheritance so that code expresses intent.
60 // [Express ideas directly in code](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#p1-express-ideas-directly-in-code)
61 #if __cplusplus >= 201703L // since C++17
62 struct GreenId : Inherited::StrongIdentifier<int> {};
63 #else // until C++17
64 struct GreenId : Inherited::StrongIdentifier<int> {
65 using base::base;
66 };
67 #endif // C++17
68
69 // Generate the next ID.
70 template <typename T>
71 3 typename std::enable_if<not std::is_enum<T>::value, T>::type next() {
72 static auto previous = T::null;
73 3 return T{++(previous.value)};
74 }
75
76 32 STRONG_FLOATING_POINT_OPERATORS(Length)
77
78 // NOLINTBEGIN(*-avoid-magic-numbers)
79
80 8 TEST(StrongType, initialization) {
81 2 auto m = Mass{2.0};
82
2/10
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 EXPECT_EQ(2.0, m.asDouble);
83
84 2 auto w = Width{3.0};
85 2 auto h = Height{4.0};
86 2 auto area = Area{Length{w} * Length{h}};
87 2 auto expected = Area{12.0};
88
2/10
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
2 EXPECT_EQ(expected, area);
89 2 }
90
91 // NOLINTEND(*-avoid-magic-numbers)
92
93 8 TEST(StrongIdentifier, initialization) {
94 2 GreenId zero{0};
95 2 GreenId default1;
96 2 GreenId default2{};
97
98
2/10
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
2 EXPECT_EQ(zero, GreenId::null);
99
2/10
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
2 EXPECT_EQ(default1, GreenId::null);
100
2/10
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
2 EXPECT_EQ(default2, GreenId::null);
101
2/10
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
2 EXPECT_EQ(default1, default2);
102 2 }
103
104 8 TEST(StrongIdentifier, equal) {
105 #if __cplusplus >= 201703L // since C++17
106 2 auto red = RedId{1};
107 2 auto rojo = RedId{1};
108 2 auto rouge = RedId::null;
109 2 auto blue = BlueId{2};
110 2 auto azul = BlueId{2};
111 2 auto bleu = BlueId::null;
112 #else // until C++17
113 auto red = static_cast<RedId>(1);
114 auto rojo = static_cast<RedId>(1);
115 auto rouge = RedId::null;
116 auto blue = static_cast<BlueId>(2);
117 auto azul = static_cast<BlueId>(2);
118 auto bleu = BlueId::null;
119 #endif // C++17
120
121
2/10
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
2 EXPECT_EQ(red, rojo);
122
2/10
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
2 EXPECT_EQ(blue, azul);
123
124
2/10
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 EXPECT_NE(red, RedId::null);
125
2/10
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 EXPECT_NE(rojo, RedId::null);
126
2/10
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 EXPECT_EQ(rouge, RedId::null);
127
128
2/10
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 EXPECT_NE(blue, BlueId::null);
129
2/10
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 EXPECT_NE(azul, BlueId::null);
130
2/10
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
2 EXPECT_EQ(bleu, BlueId::null);
131
132
2/10
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
2 EXPECT_EQ(red, next<RedId>()); // 1
133
2/10
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
2 EXPECT_NE(red, next<RedId>()); // 2
134
135
2/10
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
2 EXPECT_NE(blue, next<BlueId>()); // 1
136
2/10
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
2 EXPECT_EQ(blue, next<BlueId>()); // 2
137
138 // Cannot compare different types.
139 // EXPECT_EQ(RedId::null, BlueId::null); // compile time error
140 // EXPECT_NE(red, blue); // compile time error
141 // EXPECT_EQ(blue, 2); // compile time error
142 2 }
143
144 8 TEST(StrongIdentifier, container) {
145 2 auto collection = std::set<GreenId>{};
146
2/10
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
2 EXPECT_EQ(0, collection.size());
147
148
1/2
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
2 collection.insert(next<GreenId>()); // 1
149
1/2
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
2 collection.insert(next<GreenId>()); // 2
150
1/2
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
2 collection.insert(next<GreenId>()); // 3
151
2/10
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
2 EXPECT_EQ(3, collection.size());
152
153
1/2
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 collection.insert(GreenId{3}); // duplicate
154
2/10
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
2 EXPECT_EQ(3, collection.size()); // size unchanged
155
156
3/12
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
2 EXPECT_EQ(1, collection.erase(GreenId{1})); // remove smallest
157
2/10
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
2 EXPECT_EQ(2, collection.cbegin()->value); // new smallest
158 4 }
159