1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef INCLUDE_PERFETTO_PROTOZERO_COPYABLE_PTR_H_ 18 #define INCLUDE_PERFETTO_PROTOZERO_COPYABLE_PTR_H_ 19 20 #include <memory> 21 22 namespace protozero { 23 24 // This class is essentially a std::vector<T> of fixed size = 1. 25 // It's a pointer wrapper with deep copying and deep equality comparison. 26 // At all effects this wrapper behaves like the underlying T, with the exception 27 // of the heap indirection. 28 // Conversely to a std::unique_ptr, the pointer will be always valid, never 29 // null. The problem it solves is the following: when generating C++ classes 30 // from proto files, we want to keep each header hermetic (i.e. not #include 31 // headers of dependent types). As such we can't directly instantiate T 32 // field members but we can instead rely on pointers, so only the .cc file needs 33 // to see the actual definition of T. If the generated classes were move-only we 34 // could just use a unique_ptr there. But they aren't, hence this wrapper. 35 // Converesely to unique_ptr, this wrapper: 36 // - Default constructs the T instance in its constructor. 37 // - Implements deep comparison in operator== instead of pointer comparison. 38 template <typename T> 39 class CopyablePtr { 40 public: CopyablePtr()41 CopyablePtr() : ptr_(new T()) {} 42 ~CopyablePtr() = default; 43 44 // Copy operators. CopyablePtr(const CopyablePtr & other)45 CopyablePtr(const CopyablePtr& other) : ptr_(new T(*other.ptr_)) {} 46 CopyablePtr& operator=(const CopyablePtr& other) { 47 *ptr_ = *other.ptr_; 48 return *this; 49 } 50 51 // Move operators. CopyablePtr(CopyablePtr && other)52 CopyablePtr(CopyablePtr&& other) noexcept : ptr_(std::move(other.ptr_)) { 53 other.ptr_.reset(new T()); 54 } 55 56 CopyablePtr& operator=(CopyablePtr&& other) { 57 ptr_ = std::move(other.ptr_); 58 other.ptr_.reset(new T()); 59 return *this; 60 } 61 get()62 T* get() { return ptr_.get(); } get()63 const T* get() const { return ptr_.get(); } 64 65 T* operator->() { return ptr_.get(); } 66 const T* operator->() const { return ptr_.get(); } 67 68 T& operator*() { return *ptr_; } 69 const T& operator*() const { return *ptr_; } 70 71 friend bool operator==(const CopyablePtr& lhs, const CopyablePtr& rhs) { 72 return *lhs == *rhs; 73 } 74 75 friend bool operator!=(const CopyablePtr& lhs, const CopyablePtr& rhs) { 76 // In theory the underlying type might have a special operator!= 77 // implementation which is not just !(x == y). Respect that. 78 return *lhs != *rhs; 79 } 80 81 private: 82 std::unique_ptr<T> ptr_; 83 }; 84 85 } // namespace protozero 86 87 #endif // INCLUDE_PERFETTO_PROTOZERO_COPYABLE_PTR_H_ 88