//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// template<class T>
// class enable_shared_from_this
// {
// protected:
// enable_shared_from_this();
// enable_shared_from_this(enable_shared_from_this const&);
// enable_shared_from_this& operator=(enable_shared_from_this const&);
// ~enable_shared_from_this();
// public:
// shared_ptr<T> shared_from_this();
// shared_ptr<T const> shared_from_this() const;
// weak_ptr<T> weak_from_this() noexcept; // C++17
// weak_ptr<T const> weak_from_this() const noexcept; // C++17
// };
#include <memory>
#include <cassert>
#include "test_macros.h"
#include "count_new.h"
struct T
: public std::enable_shared_from_this<T>
{
};
struct Y : T {};
struct Z : Y {};
void nullDeleter(void*) {}
struct Foo : virtual public std::enable_shared_from_this<Foo>
{
virtual ~Foo() {}
};
struct Bar : public Foo {
Bar(int) {}
};
struct PrivateBase : private std::enable_shared_from_this<PrivateBase> {
};
int main(int, char**)
{
globalMemCounter.reset();
{ // https://llvm.org/PR18843
std::shared_ptr<T const> t1(new T);
std::shared_ptr<T const> t2(std::make_shared<T>());
}
{ // https://llvm.org/PR27115
int x = 42;
std::shared_ptr<Bar> t1(new Bar(42));
assert(t1->shared_from_this() == t1);
std::shared_ptr<Bar> t2(std::make_shared<Bar>(x));
assert(t2->shared_from_this() == t2);
}
{
std::shared_ptr<Y> p(new Z);
std::shared_ptr<T> q = p->shared_from_this();
assert(p == q);
assert(!p.owner_before(q) && !q.owner_before(p)); // p and q share ownership
}
{
std::shared_ptr<Y> p = std::make_shared<Z>();
std::shared_ptr<T> q = p->shared_from_this();
assert(p == q);
assert(!p.owner_before(q) && !q.owner_before(p)); // p and q share ownership
}
{
typedef std::shared_ptr<PrivateBase> APtr;
APtr a1 = std::make_shared<PrivateBase>();
assert(a1.use_count() == 1);
}
// Test LWG issue 2529. Only reset '__weak_ptr_' when it's already expired.
// https://cplusplus.github.io/LWG/lwg-defects.html#2529
// Test two different ways:
// * Using 'weak_from_this().expired()' in C++17.
// * Using 'shared_from_this()' in all dialects.
{
assert(globalMemCounter.checkOutstandingNewEq(0));
T* ptr = new T;
std::shared_ptr<T> s(ptr);
{
// Don't re-initialize the "enable_shared_from_this" base
// because it already references a non-expired shared_ptr.
std::shared_ptr<T> s2(ptr, &nullDeleter);
}
#if TEST_STD_VER > 14
// The enable_shared_from_this base should still be referencing
// the original shared_ptr.
assert(!ptr->weak_from_this().expired());
#endif
#ifndef TEST_HAS_NO_EXCEPTIONS
{
try {
std::shared_ptr<T> new_s = ptr->shared_from_this();
assert(new_s == s);
} catch (std::bad_weak_ptr const&) {
assert(false);
} catch (...) {
assert(false);
}
}
#endif
s.reset();
assert(globalMemCounter.checkOutstandingNewEq(0));
}
// Test LWG issue 2529 again. This time check that an expired pointer
// is replaced.
{
assert(globalMemCounter.checkOutstandingNewEq(0));
T* ptr = new T;
std::weak_ptr<T> weak;
{
std::shared_ptr<T> s(ptr, &nullDeleter);
assert(ptr->shared_from_this() == s);
weak = s;
assert(!weak.expired());
}
assert(weak.expired());
weak.reset();
#ifndef TEST_HAS_NO_EXCEPTIONS
try {
TEST_IGNORE_NODISCARD ptr->shared_from_this();
assert(false);
} catch (std::bad_weak_ptr const&) {
} catch (...) { assert(false); }
#endif
{
std::shared_ptr<T> s2(ptr, &nullDeleter);
assert(ptr->shared_from_this() == s2);
}
delete ptr;
assert(globalMemCounter.checkOutstandingNewEq(0));
}
// Test weak_from_this_methods
#if TEST_STD_VER > 14
{
T* ptr = new T;
const T* cptr = ptr;
static_assert(noexcept(ptr->weak_from_this()), "Operation must be noexcept");
static_assert(noexcept(cptr->weak_from_this()), "Operation must be noexcept");
std::weak_ptr<T> my_weak = ptr->weak_from_this();
assert(my_weak.expired());
std::weak_ptr<T const> my_const_weak = cptr->weak_from_this();
assert(my_const_weak.expired());
// Enable shared_from_this with ptr.
std::shared_ptr<T> sptr(ptr);
my_weak = ptr->weak_from_this();
assert(!my_weak.expired());
assert(my_weak.lock().get() == ptr);
}
#endif
return 0;
}