Skip to content

Commit

Permalink
Minor cleanup/improvements in unique_ptr_test (#1266)
Browse files Browse the repository at this point in the history
I'd previously introduced a bunch of small wrappers around the class and
functions under test to avoid excessive cpp use, but we can achieve this
more expediently with simple using-declarations. This also cuts out some
over-specified tests (e.g. there's no reason a stateful deleter wouldn't
compile.)
  • Loading branch information
mrdomino authored Sep 1, 2024
1 parent 389d565 commit 48b703b
Showing 1 changed file with 69 additions and 60 deletions.
129 changes: 69 additions & 60 deletions test/ctl/unique_ptr_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,72 +24,56 @@
// #include <type_traits>
// #define ctl std

template<typename T, typename D = ctl::default_delete<T>>
using Ptr = ctl::unique_ptr<T, D>;
using ctl::unique_ptr;
using ctl::make_unique;
using ctl::make_unique_for_overwrite;

template<typename T, typename... Args>
Ptr<T>
Mk(Args&&... args)
{
return ctl::make_unique<T, Args...>(ctl::forward<Args>(args)...);
}

template<typename T>
Ptr<T>
MkRaw()
{
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;

struct SetsGDeleter
struct ConstructG
{
void operator()(auto* x) const noexcept
ConstructG()
{
++g;
delete x;
}
};

struct StatefulDeleter
struct DestructG
{
char state;
void operator()(auto* x) const noexcept
~DestructG()
{
++g;
}
};

struct FinalDeleter final
struct CallG
{
void operator()(auto* x) const noexcept
{
++g;
}
};

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*));
// 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 SetsGCtor
struct FinalDeleter final
{
SetsGCtor()
void operator()(auto* x) const noexcept
{
++g;
}
};

struct SetsGDtor
{
~SetsGDtor()
{
++g;
}
};
// 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
{};
Expand All @@ -100,36 +84,62 @@ struct Derived : Base
int
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, CallG> x(&a);
x.reset();
if (g != 1)
return 1;
}

{
g = 0;
Ptr<int, SetsGDeleter> x(new int());
delete x.release();
// Deleter is not called if the pointer is null when reset.
unique_ptr<int, CallG> x(&a);
x.release();
x.reset();
if (g)
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, CallG> x(&a);
}
if (!g)
return 18;
}

{
g = 0;
// Deleter is called if scope ends exceptionally.
try {
unique_ptr<int, CallG> x(&a);
throw 'a';
} catch (char) {
}
if (!g)
return 19;
}

{
unique_ptr<int> x(new int(5)), y(new int(6));
x.swap(y);
if (*x != 6 || *y != 5)
return 2;
}

{
Ptr<int> x;
unique_ptr<int> x;
if (x)
return 3;
x.reset(new int(5));
Expand All @@ -139,17 +149,17 @@ main()

{
g = 0;
Ptr<SetsGCtor> x;
unique_ptr<ConstructG> x;
if (g)
return 5;
x = Mk<SetsGCtor>();
x = make_unique<ConstructG>();
if (g != 1)
return 6;
}

{
g = 0;
auto x = Mk<SetsGDtor>();
auto x = make_unique<DestructG>();
if (g)
return 7;
x.reset();
Expand All @@ -161,9 +171,9 @@ main()

{
g = 0;
Ptr<SetsGDtor> x, y;
x = Mk<SetsGDtor>();
y = Mk<SetsGDtor>();
unique_ptr<DestructG> x, y;
x = make_unique<DestructG>();
y = make_unique<DestructG>();
#if 0
// shouldn't compile
x = y;
Expand All @@ -178,7 +188,7 @@ main()
{
g = 0;
{
auto x = Mk<SetsGDtor>();
auto x = make_unique<DestructG>();
}
if (g != 1)
return 12;
Expand All @@ -187,7 +197,7 @@ main()
{
g = 0;
{
auto x = Mk<SetsGDtor>();
auto x = make_unique<DestructG>();
delete x.release();
}
if (g != 1)
Expand All @@ -199,13 +209,13 @@ main()
// side effects it has are illegal to detect?
{
g = 0;
auto x = MkRaw<DefaultInitialized>();
auto x = make_unique_for_overwrite<DefaultInitialized>();
if (g)
return 14;
x.reset();
if (g)
return 15;
x = Mk<DefaultInitialized>();
x = make_unique<DefaultInitialized>();
if (g != 1)
return 16;
}
Expand All @@ -214,16 +224,15 @@ main()
{
int a;
// Should compile.
Ptr<int, FinalDeleter> x(&a);
Ptr<int, StatefulDeleter> y(&a);
unique_ptr<int, FinalDeleter> x(&a);
}

{
Ptr<Base> x(new Base);
unique_ptr<Base> x(new Base);
x.reset(new Derived);

Ptr<Derived> y(new Derived);
Ptr<Base> z(ctl::move(y));
unique_ptr<Derived> y(new Derived);
unique_ptr<Base> z(ctl::move(y));
}

CheckForMemoryLeaks();
Expand Down

0 comments on commit 48b703b

Please sign in to comment.