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 #include <utility> 22 23 namespace protozero { 24 25 // This class is essentially a std::vector<T> of fixed size = 1. 26 // It's a pointer wrapper with deep copying and deep equality comparison. 27 // At all effects this wrapper behaves like the underlying T, with the exception 28 // of the heap indirection. 29 // Conversely to a std::unique_ptr, the pointer will be always valid, never 30 // null. The problem it solves is the following: when generating C++ classes 31 // from proto files, we want to keep each header hermetic (i.e. not #include 32 // headers of dependent types). As such we can't directly instantiate T 33 // field members but we can instead rely on pointers, so only the .cc file needs 34 // to see the actual definition of T. If the generated classes were move-only we 35 // could just use a unique_ptr there. But they aren't, hence this wrapper. 36 // Converesely to unique_ptr, this wrapper: 37 // - Default constructs the T instance in its constructor. 38 // - Implements deep comparison in operator== instead of pointer comparison. 39 template <typename T> 40 class CopyablePtr { 41 public: CopyablePtr()42 CopyablePtr() : ptr_(new T()) {} 43 ~CopyablePtr() = default; 44 45 // Copy operators. CopyablePtr(const CopyablePtr & other)46 CopyablePtr(const CopyablePtr& other) : ptr_(new T(*other.ptr_)) {} 47 CopyablePtr& operator=(const CopyablePtr& other) { 48 *ptr_ = *other.ptr_; 49 return *this; 50 } 51 52 // Move operators. CopyablePtr(CopyablePtr && other)53 CopyablePtr(CopyablePtr&& other) noexcept : ptr_(std::move(other.ptr_)) { 54 other.ptr_.reset(new T()); 55 } 56 57 CopyablePtr& operator=(CopyablePtr&& other) { 58 ptr_ = std::move(other.ptr_); 59 other.ptr_.reset(new T()); 60 return *this; 61 } 62 get()63 T* get() { return ptr_.get(); } get()64 const T* get() const { return ptr_.get(); } 65 66 T* operator->() { return ptr_.get(); } 67 const T* operator->() const { return ptr_.get(); } 68 69 T& operator*() { return *ptr_; } 70 const T& operator*() const { return *ptr_; } 71 72 friend bool operator==(const CopyablePtr& lhs, const CopyablePtr& rhs) { 73 return *lhs == *rhs; 74 } 75 76 friend bool operator!=(const CopyablePtr& lhs, const CopyablePtr& rhs) { 77 // In theory the underlying type might have a special operator!= 78 // implementation which is not just !(x == y). Respect that. 79 return *lhs != *rhs; 80 } 81 82 private: 83 std::unique_ptr<T> ptr_; 84 }; 85 86 } // namespace protozero 87 88 #endif // INCLUDE_PERFETTO_PROTOZERO_COPYABLE_PTR_H_ 89