Minor cleanup/improvements in unique_ptr_test

- Uses the same name as the namespaced type for the declarations.

- Removes the stateful deleter test case and corresponding type.

- Explains the reasoning behind the instrumented lifecycle types thad it
  didn't seem practical to remove.

- Uncomments the #undef ctl since it is at worst a no-op.

- Adds tests for the very basic functionality of a unique_ptr, i.e. that
  it is deleted on leaving scope.

- SetsGDeleter now doesn't delete the object, and the tests that rely on
  it use pointers to memory that doesn't need to be deleted.

- Comments a few of the basic test cases.
This commit is contained in:
Steven Dee (Jōshin) 2024-08-27 18:30:03 -07:00
parent 884d89235f
commit a525345db4
No known key found for this signature in database

View file

@ -25,56 +25,30 @@
// #define ctl std // #define ctl std
template<typename T, typename D = ctl::default_delete<T>> template<typename T, typename D = ctl::default_delete<T>>
using Ptr = ctl::unique_ptr<T, D>; using unique_ptr = ctl::unique_ptr<T, D>;
template<typename T, typename... Args> template<typename T, typename... Args>
Ptr<T> unique_ptr<T>
Mk(Args&&... args) make_unique(Args&&... args)
{ {
return ctl::make_unique<T, Args...>(ctl::forward<Args>(args)...); return ctl::make_unique<T, Args...>(ctl::forward<Args>(args)...);
} }
template<typename T> template<typename T>
Ptr<T> unique_ptr<T>
MkRaw() make_unique_for_overwrite()
{ {
return ctl::make_unique_for_overwrite<T>(); return ctl::make_unique_for_overwrite<T>();
} }
// #undef ctl #undef ctl
// The following few definitions are used to get observability into aspects of
// an object's lifecycle, to make sure that e.g. constructing a unique_ptr of a
// type does not construct an object, and that make_unique does construct an
// object.
static int g = 0; static int g = 0;
struct SetsGDeleter
{
void operator()(auto* x) const noexcept
{
++g;
delete x;
}
};
struct StatefulDeleter
{
char state;
void operator()(auto* x) const noexcept
{
}
};
struct FinalDeleter final
{
void operator()(auto* x) const noexcept
{
}
};
static_assert(sizeof(Ptr<int, SetsGDeleter>) == sizeof(int*));
// not everyone uses [[no_unique_address]]...
static_assert(!ctl::is_same_v<Ptr<int>, ctl::unique_ptr<int>> ||
sizeof(Ptr<int, FinalDeleter>) == sizeof(int*));
struct SetsGCtor struct SetsGCtor
{ {
SetsGCtor() SetsGCtor()
@ -91,6 +65,29 @@ struct SetsGDtor
} }
}; };
struct SetsGDeleter
{
void operator()(auto* x) const noexcept
{
++g;
}
};
// A unique_ptr with an empty deleter should be the same size as a raw pointer.
static_assert(sizeof(unique_ptr<int, decltype([] {})>) == sizeof(int*));
struct FinalDeleter final
{
void operator()(auto* x) const noexcept
{
}
};
// ctl::unique_ptr does not need to inherit from its deleter for this property;
// the STL often does, though, so we don't hold them to the following.
static_assert(!ctl::is_same_v<unique_ptr<int>, ctl::unique_ptr<int>> ||
sizeof(unique_ptr<int, FinalDeleter>) == sizeof(int*));
struct Base struct Base
{}; {};
@ -100,13 +97,16 @@ struct Derived : Base
int int
main() main()
{ {
int a;
{ {
Ptr<int> x(new int(5)); // Shouldn't cause any memory leaks.
unique_ptr<int> x(new int(5));
} }
{ {
Ptr<int, SetsGDeleter> x(new int()); // Deleter is called if the pointer is non-null when reset.
unique_ptr<int, SetsGDeleter> x(&a);
x.reset(); x.reset();
if (g != 1) if (g != 1)
return 1; return 1;
@ -114,22 +114,45 @@ main()
{ {
g = 0; g = 0;
Ptr<int, SetsGDeleter> x(new int()); // Deleter is not called if the pointer is null when reset.
delete x.release(); unique_ptr<int, SetsGDeleter> x(&a);
x.release();
x.reset(); x.reset();
if (g) if (g)
return 17; return 17;
} }
{ {
Ptr<int> x(new int(5)), y(new int(6)); g = 0;
// Deleter is called when the pointer goes out of scope.
{
unique_ptr<int, SetsGDeleter> x(&a);
}
if (!g)
return 18;
}
{
g = 0;
// Deleter is called if scope ends exceptionally.
try {
unique_ptr<int, SetsGDeleter> x(&a);
throw 'a';
} catch (char) {
}
if (!g)
return 19;
}
{
unique_ptr<int> x(new int(5)), y(new int(6));
x.swap(y); x.swap(y);
if (*x != 6 || *y != 5) if (*x != 6 || *y != 5)
return 2; return 2;
} }
{ {
Ptr<int> x; unique_ptr<int> x;
if (x) if (x)
return 3; return 3;
x.reset(new int(5)); x.reset(new int(5));
@ -139,17 +162,17 @@ main()
{ {
g = 0; g = 0;
Ptr<SetsGCtor> x; unique_ptr<SetsGCtor> x;
if (g) if (g)
return 5; return 5;
x = Mk<SetsGCtor>(); x = make_unique<SetsGCtor>();
if (g != 1) if (g != 1)
return 6; return 6;
} }
{ {
g = 0; g = 0;
auto x = Mk<SetsGDtor>(); auto x = make_unique<SetsGDtor>();
if (g) if (g)
return 7; return 7;
x.reset(); x.reset();
@ -161,9 +184,9 @@ main()
{ {
g = 0; g = 0;
Ptr<SetsGDtor> x, y; unique_ptr<SetsGDtor> x, y;
x = Mk<SetsGDtor>(); x = make_unique<SetsGDtor>();
y = Mk<SetsGDtor>(); y = make_unique<SetsGDtor>();
#if 0 #if 0
// shouldn't compile // shouldn't compile
x = y; x = y;
@ -178,7 +201,7 @@ main()
{ {
g = 0; g = 0;
{ {
auto x = Mk<SetsGDtor>(); auto x = make_unique<SetsGDtor>();
} }
if (g != 1) if (g != 1)
return 12; return 12;
@ -187,7 +210,7 @@ main()
{ {
g = 0; g = 0;
{ {
auto x = Mk<SetsGDtor>(); auto x = make_unique<SetsGDtor>();
delete x.release(); delete x.release();
} }
if (g != 1) if (g != 1)
@ -199,13 +222,13 @@ main()
// side effects it has are illegal to detect? // side effects it has are illegal to detect?
{ {
g = 0; g = 0;
auto x = MkRaw<DefaultInitialized>(); auto x = make_unique_for_overwrite<DefaultInitialized>();
if (g) if (g)
return 14; return 14;
x.reset(); x.reset();
if (g) if (g)
return 15; return 15;
x = Mk<DefaultInitialized>(); x = make_unique<DefaultInitialized>();
if (g != 1) if (g != 1)
return 16; return 16;
} }
@ -214,16 +237,15 @@ main()
{ {
int a; int a;
// Should compile. // Should compile.
Ptr<int, FinalDeleter> x(&a); unique_ptr<int, FinalDeleter> x(&a);
Ptr<int, StatefulDeleter> y(&a);
} }
{ {
Ptr<Base> x(new Base); unique_ptr<Base> x(new Base);
x.reset(new Derived); x.reset(new Derived);
Ptr<Derived> y(new Derived); unique_ptr<Derived> y(new Derived);
Ptr<Base> z(ctl::move(y)); unique_ptr<Base> z(ctl::move(y));
} }
CheckForMemoryLeaks(); CheckForMemoryLeaks();