forked from open-telemetry/opentelemetry-collector
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a type-erased shared pointer (open-telemetry#47)
- Loading branch information
Johannes Tax
authored
Mar 10, 2020
1 parent
97ea4d7
commit 7b4dd48
Showing
4 changed files
with
374 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
#pragma once | ||
|
||
#include <memory> | ||
|
||
#include "opentelemetry/version.h" | ||
|
||
OPENTELEMETRY_BEGIN_NAMESPACE | ||
namespace nostd | ||
{ | ||
/** | ||
* Provide a type-erased version of std::shared_ptr that has ABI stability. | ||
*/ | ||
template <class T> | ||
class shared_ptr | ||
{ | ||
public: | ||
using element_type = T; | ||
using pointer = element_type *; | ||
|
||
private: | ||
static constexpr size_t kMaxSize = 32; | ||
static constexpr size_t kAlignment = 8; | ||
|
||
struct alignas(kAlignment) PlacementBuffer | ||
{ | ||
char data[kMaxSize]; | ||
}; | ||
|
||
class shared_ptr_wrapper | ||
{ | ||
public: | ||
shared_ptr_wrapper() noexcept = default; | ||
|
||
shared_ptr_wrapper(std::shared_ptr<T> &&ptr) noexcept : ptr_{std::move(ptr)} {} | ||
|
||
virtual ~shared_ptr_wrapper() {} | ||
|
||
virtual void CopyTo(PlacementBuffer &buffer) const noexcept | ||
{ | ||
new (buffer.data) shared_ptr_wrapper{*this}; | ||
} | ||
|
||
virtual void MoveTo(PlacementBuffer &buffer) noexcept | ||
{ | ||
new (buffer.data) shared_ptr_wrapper{std::move(this->ptr_)}; | ||
} | ||
|
||
template <class U, | ||
typename std::enable_if<std::is_convertible<pointer, U *>::value>::type * = nullptr> | ||
void MoveTo(typename shared_ptr<U>::PlacementBuffer &buffer) noexcept | ||
{ | ||
new (buffer.data) shared_ptr_wrapper{std::move(this->ptr_)}; | ||
} | ||
|
||
virtual pointer Get() const noexcept { return ptr_.get(); } | ||
|
||
virtual void Reset() noexcept { ptr_.reset(); } | ||
|
||
private: | ||
std::shared_ptr<T> ptr_; | ||
}; | ||
|
||
static_assert(sizeof(shared_ptr_wrapper) <= kMaxSize, "Placement buffer is too small"); | ||
static_assert(alignof(shared_ptr_wrapper) <= kAlignment, "Placement buffer not properly aligned"); | ||
|
||
public: | ||
shared_ptr() noexcept { new (buffer_.data) shared_ptr_wrapper{}; } | ||
|
||
explicit shared_ptr(pointer ptr) | ||
{ | ||
std::shared_ptr<T> ptr_(ptr); | ||
new (buffer_.data) shared_ptr_wrapper{std::move(ptr_)}; | ||
} | ||
|
||
shared_ptr(std::shared_ptr<T> ptr) noexcept | ||
{ | ||
new (buffer_.data) shared_ptr_wrapper{std::move(ptr)}; | ||
} | ||
|
||
shared_ptr(shared_ptr &&other) noexcept { other.wrapper().MoveTo(buffer_); } | ||
|
||
template <class U, | ||
typename std::enable_if<std::is_convertible<U *, pointer>::value>::type * = nullptr> | ||
shared_ptr(shared_ptr<U> &&other) noexcept | ||
{ | ||
other.wrapper().template MoveTo<T>(buffer_); | ||
} | ||
|
||
shared_ptr(const shared_ptr &other) noexcept { other.wrapper().CopyTo(buffer_); } | ||
|
||
~shared_ptr() { wrapper().~shared_ptr_wrapper(); } | ||
|
||
shared_ptr &operator=(shared_ptr &&other) noexcept | ||
{ | ||
wrapper().~shared_ptr_wrapper(); | ||
other.wrapper().MoveTo(buffer_); | ||
return *this; | ||
} | ||
|
||
shared_ptr &operator=(std::nullptr_t) noexcept | ||
{ | ||
wrapper().Reset(); | ||
return *this; | ||
} | ||
|
||
shared_ptr &operator=(const shared_ptr &other) noexcept | ||
{ | ||
wrapper().~shared_ptr_wrapper(); | ||
other.wrapper().CopyTo(buffer_); | ||
return *this; | ||
} | ||
|
||
element_type &operator*() const noexcept { return *wrapper().Get(); } | ||
|
||
pointer operator->() const noexcept { return wrapper().Get(); } | ||
|
||
operator bool() const noexcept { return wrapper().Get() != nullptr; } | ||
|
||
pointer get() const noexcept { return wrapper().Get(); } | ||
|
||
void swap(shared_ptr<T> &other) noexcept | ||
{ | ||
shared_ptr<T> tmp{std::move(other)}; | ||
|
||
wrapper().MoveTo(other.buffer_); | ||
tmp.wrapper().MoveTo(buffer_); | ||
} | ||
|
||
template <typename U> | ||
friend class shared_ptr; | ||
|
||
private: | ||
PlacementBuffer buffer_; | ||
|
||
shared_ptr_wrapper &wrapper() noexcept | ||
{ | ||
return *reinterpret_cast<shared_ptr_wrapper *>(buffer_.data); | ||
} | ||
|
||
const shared_ptr_wrapper &wrapper() const noexcept | ||
{ | ||
return *reinterpret_cast<const shared_ptr_wrapper *>(buffer_.data); | ||
} | ||
}; | ||
|
||
template <class T1, class T2> | ||
bool operator!=(const shared_ptr<T1> &lhs, const shared_ptr<T2> &rhs) noexcept | ||
{ | ||
return lhs.get() != rhs.get(); | ||
} | ||
|
||
template <class T1, class T2> | ||
bool operator==(const shared_ptr<T1> &lhs, const shared_ptr<T2> &rhs) noexcept | ||
{ | ||
return lhs.get() == rhs.get(); | ||
} | ||
|
||
template <class T> | ||
inline bool operator==(const shared_ptr<T> &lhs, std::nullptr_t) noexcept | ||
{ | ||
return lhs.get() == nullptr; | ||
} | ||
|
||
template <class T> | ||
inline bool operator==(std::nullptr_t, const shared_ptr<T> &rhs) noexcept | ||
{ | ||
return nullptr == rhs.get(); | ||
} | ||
|
||
template <class T> | ||
inline bool operator!=(const shared_ptr<T> &lhs, std::nullptr_t) noexcept | ||
{ | ||
return lhs.get() != nullptr; | ||
} | ||
|
||
template <class T> | ||
inline bool operator!=(std::nullptr_t, const shared_ptr<T> &rhs) noexcept | ||
{ | ||
return nullptr != rhs.get(); | ||
} | ||
} // namespace nostd | ||
OPENTELEMETRY_END_NAMESPACE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
#include "opentelemetry/nostd/shared_ptr.h" | ||
|
||
#include <gtest/gtest.h> | ||
|
||
using opentelemetry::nostd::shared_ptr; | ||
|
||
class A | ||
{ | ||
public: | ||
explicit A(bool &destructed) noexcept : destructed_{destructed} { destructed_ = false; } | ||
|
||
~A() { destructed_ = true; } | ||
|
||
private: | ||
bool &destructed_; | ||
}; | ||
|
||
class B | ||
{ | ||
public: | ||
int f() const { return 123; } | ||
}; | ||
|
||
class C | ||
{ | ||
public: | ||
virtual ~C() {} | ||
}; | ||
|
||
class D : public C | ||
{ | ||
public: | ||
virtual ~D() {} | ||
}; | ||
|
||
TEST(SharedPtrTest, DefaultConstruction) | ||
{ | ||
shared_ptr<int> ptr1; | ||
EXPECT_EQ(ptr1.get(), nullptr); | ||
|
||
shared_ptr<int> ptr2{nullptr}; | ||
EXPECT_EQ(ptr2.get(), nullptr); | ||
} | ||
|
||
TEST(SharedPtrTest, ExplicitConstruction) | ||
{ | ||
auto c = new C(); | ||
shared_ptr<C> ptr1{c}; | ||
EXPECT_EQ(ptr1.get(), c); | ||
|
||
auto d = new D(); | ||
shared_ptr<C> ptr2{d}; | ||
EXPECT_EQ(ptr2.get(), d); | ||
} | ||
|
||
TEST(SharedPtrTest, MoveConstruction) | ||
{ | ||
auto value = new int{123}; | ||
shared_ptr<int> ptr1{value}; | ||
shared_ptr<int> ptr2{std::move(ptr1)}; | ||
EXPECT_EQ(ptr1.get(), nullptr); | ||
EXPECT_EQ(ptr2.get(), value); | ||
} | ||
|
||
TEST(SharedPtrTest, MoveConstructionFromDifferentType) | ||
{ | ||
auto value = new int{123}; | ||
shared_ptr<int> ptr1{value}; | ||
shared_ptr<const int> ptr2{std::move(ptr1)}; | ||
EXPECT_EQ(ptr1.get(), nullptr); | ||
EXPECT_EQ(ptr2.get(), value); | ||
} | ||
|
||
TEST(SharedPtrTest, MoveConstructionFromStdSharedPtr) | ||
{ | ||
auto value = new int{123}; | ||
std::shared_ptr<int> ptr1{value}; | ||
shared_ptr<int> ptr2{std::move(ptr1)}; | ||
EXPECT_EQ(ptr1.get(), nullptr); | ||
EXPECT_EQ(ptr2.get(), value); | ||
} | ||
|
||
TEST(SharedPtrTest, Destruction) | ||
{ | ||
bool was_destructed; | ||
shared_ptr<A>{new A{was_destructed}}; | ||
EXPECT_TRUE(was_destructed); | ||
} | ||
|
||
TEST(SharedPtrTest, Assignment) | ||
{ | ||
auto value = new int{123}; | ||
shared_ptr<int> ptr1; | ||
|
||
ptr1 = shared_ptr<int>(value); | ||
EXPECT_EQ(ptr1.get(), value); | ||
|
||
ptr1 = nullptr; | ||
EXPECT_EQ(ptr1.get(), nullptr); | ||
|
||
auto value2 = new int{234}; | ||
const shared_ptr<int> ptr2 = shared_ptr<int>(value2); | ||
ptr1 = ptr2; | ||
EXPECT_EQ(ptr1.get(), value2); | ||
|
||
auto value3 = new int{345}; | ||
std::shared_ptr<int> ptr3(value3); | ||
ptr1 = ptr3; | ||
EXPECT_EQ(ptr1.get(), value3); | ||
} | ||
|
||
TEST(SharedPtrTest, BoolConversionOpertor) | ||
{ | ||
auto value = new int{123}; | ||
shared_ptr<int> ptr1{value}; | ||
|
||
EXPECT_TRUE(ptr1); | ||
EXPECT_FALSE(shared_ptr<int>{}); | ||
} | ||
|
||
TEST(SharedPtrTest, PointerOperators) | ||
{ | ||
auto value = new int{123}; | ||
shared_ptr<int> ptr1{value}; | ||
|
||
EXPECT_EQ(&*ptr1, value); | ||
EXPECT_EQ( | ||
shared_ptr<B> {}->f(), 123); | ||
} | ||
|
||
TEST(SharedPtrTest, Swap) | ||
{ | ||
auto value1 = new int{123}; | ||
shared_ptr<int> ptr1{value1}; | ||
|
||
auto value2 = new int{456}; | ||
shared_ptr<int> ptr2{value2}; | ||
ptr1.swap(ptr2); | ||
|
||
EXPECT_EQ(ptr1.get(), value2); | ||
EXPECT_EQ(ptr2.get(), value1); | ||
} | ||
|
||
TEST(SharedPtrTest, Comparison) | ||
{ | ||
shared_ptr<int> ptr1{new int{123}}; | ||
shared_ptr<int> ptr2{new int{456}}; | ||
shared_ptr<int> ptr3{}; | ||
|
||
EXPECT_EQ(ptr1, ptr1); | ||
EXPECT_NE(ptr1, ptr2); | ||
|
||
EXPECT_NE(ptr1, nullptr); | ||
EXPECT_NE(nullptr, ptr1); | ||
|
||
EXPECT_EQ(ptr3, nullptr); | ||
EXPECT_EQ(nullptr, ptr3); | ||
} | ||
|
||
TEST(SharedPtrTest, Sort) | ||
{ | ||
std::vector<shared_ptr<const int>> nums; | ||
|
||
for (int i = 10; i > 0; i--) | ||
{ | ||
nums.push_back(shared_ptr<int>(new int(i))); | ||
} | ||
|
||
auto nums2 = nums; | ||
|
||
std::sort(nums.begin(), nums.end(), | ||
[](shared_ptr<const int> a, shared_ptr<const int> b) { return *a < *b; }); | ||
|
||
EXPECT_NE(nums, nums2); | ||
|
||
std::reverse(nums2.begin(), nums2.end()); | ||
|
||
EXPECT_EQ(nums, nums2); | ||
} |