Skip to content

Commit 16a6019

Browse files
beinhaerterWerner Henze
andauthored
strict_not_null for unique_ptr (#1179)
- `strict_not_null<std::unique_ptr<int>>{ std::make_unique<int>()}` failed to compile - `strict_not_null` ctor needs to move the passed `unique_ptr`, not copy - Copied `not_null` `TestNotNullConstructors` for `strict_not_null` - The `noexcept` specifiers on the `strict_not_null` and `not_null` constructors were out of sync. - Added unit test for `not_null<unique_ptr<T>>` and for `strict_not_null<unique_ptr<T>>` - Added unit test for `gsl::swap` for two `strict_not_null` - Added unit test for `gsl::swap` for `not_null` and `strict_not_null` Co-authored-by: Werner Henze <[email protected]>
1 parent fcd55ee commit 16a6019

File tree

4 files changed

+166
-17
lines changed

4 files changed

+166
-17
lines changed

include/gsl/pointers

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,19 +275,19 @@ class strict_not_null : public not_null<T>
275275
{
276276
public:
277277
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
278-
constexpr explicit strict_not_null(U&& u) : not_null<T>(std::forward<U>(u))
278+
constexpr explicit strict_not_null(U&& u) noexcept(std::is_nothrow_move_constructible<T>::value) : not_null<T>(std::forward<U>(u))
279279
{}
280280

281281
template <typename = std::enable_if_t<!std::is_same<std::nullptr_t, T>::value>>
282-
constexpr explicit strict_not_null(T u) : not_null<T>(u)
282+
constexpr explicit strict_not_null(T u) noexcept(std::is_nothrow_move_constructible<T>::value) : not_null<T>(std::move(u))
283283
{}
284284

285285
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
286-
constexpr strict_not_null(const not_null<U>& other) : not_null<T>(other)
286+
constexpr strict_not_null(const not_null<U>& other) noexcept(std::is_nothrow_move_constructible<T>::value) : not_null<T>(other)
287287
{}
288288

289289
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
290-
constexpr strict_not_null(const strict_not_null<U>& other) : not_null<T>(other)
290+
constexpr strict_not_null(const strict_not_null<U>& other) noexcept(std::is_nothrow_move_constructible<T>::value) : not_null<T>(other)
291291
{}
292292

293293
// To avoid invalidating the "not null" invariant, the contained pointer is actually copied

tests/notnull_tests.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,14 @@ TEST(notnull_tests, TestNotNullConstructors)
178178
EXPECT_DEATH((not_null<decltype(pi)>(pi)), expected);
179179
}
180180

181+
{
182+
// from unique pointer
183+
not_null<std::unique_ptr<int>> x(
184+
std::make_unique<int>(10)); // unique_ptr<int> is nullptr assignable
185+
186+
EXPECT_DEATH((not_null<std::unique_ptr<int>>(std::unique_ptr<int>{})), expected);
187+
}
188+
181189
{
182190
// from pointer to local
183191
int t = 42;

tests/pointers_tests.cpp

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,50 @@ struct NotMoveAssignableCustomPtr
2525
TEST(pointers_test, swap)
2626
{
2727
// taken from gh-1129:
28-
gsl::not_null<std::unique_ptr<int>> a(std::make_unique<int>(0));
29-
gsl::not_null<std::unique_ptr<int>> b(std::make_unique<int>(1));
28+
{
29+
gsl::not_null<std::unique_ptr<int>> a(std::make_unique<int>(0));
30+
gsl::not_null<std::unique_ptr<int>> b(std::make_unique<int>(1));
3031

31-
EXPECT_TRUE(*a == 0);
32-
EXPECT_TRUE(*b == 1);
32+
EXPECT_TRUE(*a == 0);
33+
EXPECT_TRUE(*b == 1);
3334

34-
gsl::swap(a, b);
35+
gsl::swap(a, b);
3536

36-
EXPECT_TRUE(*a == 1);
37-
EXPECT_TRUE(*b == 0);
37+
EXPECT_TRUE(*a == 1);
38+
EXPECT_TRUE(*b == 0);
3839

39-
// Make sure our custom ptr can be used with not_null. The shared_pr is to prevent "unused"
40-
// compiler warnings.
41-
const auto shared_custom_ptr{std::make_shared<NotMoveAssignableCustomPtr>()};
42-
gsl::not_null<NotMoveAssignableCustomPtr> c{*shared_custom_ptr};
43-
EXPECT_TRUE(c.get() != nullptr);
40+
// Make sure our custom ptr can be used with not_null. The shared_pr is to prevent "unused"
41+
// compiler warnings.
42+
const auto shared_custom_ptr{std::make_shared<NotMoveAssignableCustomPtr>()};
43+
gsl::not_null<NotMoveAssignableCustomPtr> c{*shared_custom_ptr};
44+
EXPECT_TRUE(c.get() != nullptr);
45+
}
46+
47+
{
48+
gsl::strict_not_null<std::unique_ptr<int>> a{std::make_unique<int>(0)};
49+
gsl::strict_not_null<std::unique_ptr<int>> b{std::make_unique<int>(1)};
50+
51+
EXPECT_TRUE(*a == 0);
52+
EXPECT_TRUE(*b == 1);
53+
54+
gsl::swap(a, b);
55+
56+
EXPECT_TRUE(*a == 1);
57+
EXPECT_TRUE(*b == 0);
58+
}
59+
60+
{
61+
gsl::not_null<std::unique_ptr<int>> a{std::make_unique<int>(0)};
62+
gsl::strict_not_null<std::unique_ptr<int>> b{std::make_unique<int>(1)};
63+
64+
EXPECT_TRUE(*a == 0);
65+
EXPECT_TRUE(*b == 1);
66+
67+
gsl::swap(a, b);
68+
69+
EXPECT_TRUE(*a == 1);
70+
EXPECT_TRUE(*b == 0);
71+
}
4472
}
4573

4674
#if __cplusplus >= 201703l

tests/strict_notnull_tests.cpp

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@
2121

2222
using namespace gsl;
2323

24+
// stand-in for a user-defined ref-counted class
25+
template <typename T>
26+
struct RefCounted
27+
{
28+
RefCounted(T* p) : p_(p) {}
29+
operator T*() { return p_; }
30+
T* p_;
31+
};
32+
2433
namespace
2534
{
2635
// clang-format off
@@ -43,12 +52,116 @@ GSL_SUPPRESS(f.4) // NO-FORMAT: attribute
4352
// clang-format on
4453
bool strict_helper_const(strict_not_null<const int*> p) { return *p == 12; }
4554

46-
#ifdef CONFIRM_COMPILATION_ERRORS
4755
int* return_pointer() { return nullptr; }
56+
#ifdef CONFIRM_COMPILATION_ERRORS
4857
const int* return_pointer_const() { return nullptr; }
4958
#endif
5059
} // namespace
5160

61+
TEST(strict_notnull_tests, TestStrictNotNullConstructors)
62+
{
63+
{
64+
#ifdef CONFIRM_COMPILATION_ERRORS
65+
strict_not_null<int*> p = nullptr; // yay...does not compile!
66+
strict_not_null<std::vector<char>*> p1 = 0; // yay...does not compile!
67+
strict_not_null<int*> p2; // yay...does not compile!
68+
std::unique_ptr<int> up = std::make_unique<int>(120);
69+
strict_not_null<int*> p3 = up;
70+
71+
// Forbid non-nullptr assignable types
72+
strict_not_null<std::vector<int>> f(std::vector<int>{1});
73+
strict_not_null<int> z(10);
74+
strict_not_null<std::vector<int>> y({1, 2});
75+
#endif
76+
}
77+
78+
const auto terminateHandler = std::set_terminate([] {
79+
std::cerr << "Expected Death. TestNotNullConstructors";
80+
std::abort();
81+
});
82+
const auto expected = GetExpectedDeathString(terminateHandler);
83+
84+
{
85+
// from shared pointer
86+
int i = 12;
87+
auto rp = RefCounted<int>(&i);
88+
strict_not_null<int*> p(rp);
89+
EXPECT_TRUE(p.get() == &i);
90+
91+
strict_not_null<std::shared_ptr<int>> x(
92+
std::make_shared<int>(10)); // shared_ptr<int> is nullptr assignable
93+
94+
int* pi = nullptr;
95+
EXPECT_DEATH((strict_not_null<decltype(pi)>(pi)), expected);
96+
}
97+
98+
{
99+
// from unique pointer
100+
strict_not_null<std::unique_ptr<int>> x(
101+
std::make_unique<int>(10)); // unique_ptr<int> is nullptr assignable
102+
103+
EXPECT_DEATH((strict_not_null<std::unique_ptr<int>>(std::unique_ptr<int>{})), expected);
104+
}
105+
106+
{
107+
// from pointer to local
108+
int t = 42;
109+
110+
strict_not_null<int*> x{&t};
111+
helper(&t);
112+
helper_const(&t);
113+
114+
EXPECT_TRUE(*x == 42);
115+
}
116+
117+
{
118+
// from raw pointer
119+
// from strict_not_null pointer
120+
121+
int t = 42;
122+
int* p = &t;
123+
124+
strict_not_null<int*> x{p};
125+
helper(p);
126+
helper_const(p);
127+
helper(x);
128+
helper_const(x);
129+
130+
EXPECT_TRUE(*x == 42);
131+
}
132+
133+
{
134+
// from raw const pointer
135+
// from strict_not_null const pointer
136+
137+
int t = 42;
138+
const int* cp = &t;
139+
140+
strict_not_null<const int*> x{cp};
141+
helper_const(cp);
142+
helper_const(x);
143+
144+
EXPECT_TRUE(*x == 42);
145+
}
146+
147+
{
148+
// from strict_not_null const pointer, using auto
149+
int t = 42;
150+
const int* cp = &t;
151+
152+
auto x = strict_not_null<const int*>{cp};
153+
154+
EXPECT_TRUE(*x == 42);
155+
}
156+
157+
{
158+
// from returned pointer
159+
160+
EXPECT_DEATH(helper(return_pointer()), expected);
161+
EXPECT_DEATH(helper_const(return_pointer()), expected);
162+
}
163+
}
164+
52165
TEST(strict_notnull_tests, TestStrictNotNull)
53166
{
54167
{

0 commit comments

Comments
 (0)