• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef COMMON_NSREF_H_
16 #define COMMON_NSREF_H_
17 
18 #include "common/RefBase.h"
19 
20 #import <Foundation/NSObject.h>
21 
22 #if !defined(__OBJC__)
23 #    error "NSRef can only be used in Objective C/C++ code."
24 #endif
25 
26 // This file contains smart pointers that automatically reference and release Objective C objects
27 // and prototocals in a manner very similar to Ref<>. Note that NSRef<> and NSPRef's constructor add
28 // a reference to the object by default, so the pattern to get a reference for a newly created
29 // NSObject is the following:
30 //
31 //    NSRef<NSFoo> foo = AcquireNSRef([NSFoo alloc]);
32 //
33 // NSRef overloads -> and * but these operators don't work extremely well with Objective C's
34 // features. For example automatic dereferencing when doing the following doesn't work:
35 //
36 //    NSFoo* foo;
37 //    foo.member = 1;
38 //    someVar = foo.member;
39 //
40 // Instead use the message passing syntax:
41 //
42 //    NSRef<NSFoo> foo;
43 //    [*foo setMember: 1];
44 //    someVar = [*foo member];
45 //
46 // Also did you notive the extra '*' in the example above? That's because Objective C's message
47 // passing doesn't automatically call a C++ operator to dereference smart pointers (like -> does) so
48 // we have to dereference manually using '*'. In some cases the extra * or message passing syntax
49 // can get a bit annoying so instead a local "naked" pointer can be borrowed from the NSRef. This
50 // would change the syntax overload in the following:
51 //
52 //    NSRef<NSFoo> foo;
53 //    [*foo setA:1];
54 //    [*foo setB:2];
55 //    [*foo setC:3];
56 //
57 // Into (note access to members of ObjC classes referenced via pointer is done with . and not ->):
58 //
59 //    NSRef<NSFoo> fooRef;
60 //    NSFoo* foo = fooRef.Get();
61 //    foo.a = 1;
62 //    foo.b = 2;
63 //    boo.c = 3;
64 //
65 // Which can be subjectively easier to read.
66 
67 template <typename T>
68 struct NSRefTraits {
69     static constexpr T kNullValue = nullptr;
ReferenceNSRefTraits70     static void Reference(T value) {
71         [value retain];
72     }
ReleaseNSRefTraits73     static void Release(T value) {
74         [value release];
75     }
76 };
77 
78 template <typename T>
79 class NSRef : public RefBase<T*, NSRefTraits<T*>> {
80   public:
81     using RefBase<T*, NSRefTraits<T*>>::RefBase;
82 
83     const T* operator*() const {
84         return this->Get();
85     }
86 
87     T* operator*() {
88         return this->Get();
89     }
90 };
91 
92 template <typename T>
AcquireNSRef(T * pointee)93 NSRef<T> AcquireNSRef(T* pointee) {
94     NSRef<T> ref;
95     ref.Acquire(pointee);
96     return ref;
97 }
98 
99 // This is a RefBase<> for an Objective C protocol (hence the P). Objective C protocols must always
100 // be referenced with id<ProtocolName> and not just ProtocolName* so they cannot use NSRef<>
101 // itself. That's what the P in NSPRef stands for: Protocol.
102 template <typename T>
103 class NSPRef : public RefBase<T, NSRefTraits<T>> {
104   public:
105     using RefBase<T, NSRefTraits<T>>::RefBase;
106 
107     const T operator*() const {
108         return this->Get();
109     }
110 
111     T operator*() {
112         return this->Get();
113     }
114 };
115 
116 template <typename T>
AcquireNSPRef(T pointee)117 NSPRef<T> AcquireNSPRef(T pointee) {
118     NSPRef<T> ref;
119     ref.Acquire(pointee);
120     return ref;
121 }
122 
123 #endif  // COMMON_NSREF_H_
124