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