blob: 6423856e523b08e286649a81b69b60b471a59020 [file] [log] [blame]
Corentin Wallez0055d952020-11-16 23:07:56 +00001// 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
67template <typename T>
68struct NSRefTraits {
69 static constexpr T kNullValue = nullptr;
70 static void Reference(T value) {
71 [value retain];
72 }
73 static void Release(T value) {
74 [value release];
75 }
76};
77
78template <typename T>
79class 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
92template <typename T>
93NSRef<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.
102template <typename T>
103class 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
116template <typename T>
117NSPRef<T> AcquireNSPRef(T pointee) {
118 NSPRef<T> ref;
119 ref.Acquire(pointee);
120 return ref;
121}
122
123#endif // COMMON_NSREF_H_