Skip to content

Commit c37f1c6

Browse files
wilhuffCorrob
authored andcommitted
Add DelayedConstructor<T> (#2579)
* Add util::DelayedConstructor * Run sync_project on any Firestore change since start * Use DelayedConstructor in a few places to show how it works
1 parent bd8e5f3 commit c37f1c6

File tree

8 files changed

+296
-13
lines changed

8 files changed

+296
-13
lines changed

Firestore/Example/Firestore.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@
166166
6E59498D20F55BA800ECD9A5 /* FuzzingResources in Resources */ = {isa = PBXBuildFile; fileRef = 6ED6DEA120F5502700FC6076 /* FuzzingResources */; };
167167
6E8302E021022309003E1EA3 /* FSTFuzzTestFieldPath.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6E8302DF21022309003E1EA3 /* FSTFuzzTestFieldPath.mm */; };
168168
6EA39FDE20FE820E008D461F /* FSTFuzzTestSerializer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EA39FDD20FE820E008D461F /* FSTFuzzTestSerializer.mm */; };
169+
6EC28BB8C38E3FD126F68211 /* delayed_constructor_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */; };
169170
6EDD3B4620BF247500C33877 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
170171
6EDD3B4820BF247500C33877 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
171172
6EDD3B4920BF247500C33877 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; };
@@ -560,6 +561,7 @@
560561
B9C261C26C5D311E1E3C0CB9 /* query_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = query_test.cc; sourceTree = "<group>"; };
561562
BB92EB03E3F92485023F64ED /* Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Example_iOS_Firestore_SwiftTests_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
562563
C8522DE226C467C54E6788D8 /* mutation_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.cpp.cpp; path = mutation_test.cc; sourceTree = "<group>"; };
564+
D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */ = {isa = PBXFileReference; includeInIndex = 1; path = delayed_constructor_test.cc; sourceTree = "<group>"; };
563565
D3CC3DC5338DCAF43A211155 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
564566
D5B2593BCB52957D62F1C9D3 /* perf_spec_test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = perf_spec_test.json; sourceTree = "<group>"; };
565567
D5B25E7E7D6873CBA4571841 /* FIRNumericTransformTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRNumericTransformTests.mm; sourceTree = "<group>"; };
@@ -746,6 +748,7 @@
746748
548DB928200D59F600E00ABC /* comparison_test.cc */,
747749
B67BF448216EB43000CA9097 /* create_noop_connectivity_monitor.cc */,
748750
B67BF447216EB42F00CA9097 /* create_noop_connectivity_monitor.h */,
751+
D0A6E9136804A41CEC9D55D4 /* delayed_constructor_test.cc */,
749752
B6FB4689208F9B9100554BA2 /* executor_libdispatch_test.mm */,
750753
B6FB4687208F9B9100554BA2 /* executor_std_test.cc */,
751754
B6FB4688208F9B9100554BA2 /* executor_test.cc */,
@@ -2058,6 +2061,7 @@
20582061
ABE6637A201FA81900ED349A /* database_id_test.cc in Sources */,
20592062
AB38D93020236E21000A432D /* database_info_test.cc in Sources */,
20602063
546854AA20A36867004BDBD5 /* datastore_test.mm in Sources */,
2064+
6EC28BB8C38E3FD126F68211 /* delayed_constructor_test.cc in Sources */,
20612065
544129DD21C2DDC800EFB9CC /* document.pb.cc in Sources */,
20622066
B6152AD7202A53CB000E5744 /* document_key_test.cc in Sources */,
20632067
AB6B908420322E4D00CC290A /* document_test.cc in Sources */,

Firestore/Example/Tests/Core/FSTQueryListenerTests.mm

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
#include "Firestore/core/src/firebase/firestore/core/view_snapshot.h"
3434
#include "Firestore/core/src/firebase/firestore/model/types.h"
3535
#include "Firestore/core/src/firebase/firestore/remote/remote_event.h"
36+
#include "Firestore/core/src/firebase/firestore/util/delayed_constructor.h"
3637
#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h"
3738
#include "Firestore/core/src/firebase/firestore/util/status.h"
3839
#include "Firestore/core/src/firebase/firestore/util/statusor.h"
39-
#include "absl/memory/memory.h"
4040

4141
using firebase::firestore::FirestoreErrorCode;
4242
using firebase::firestore::core::DocumentViewChange;
@@ -45,6 +45,7 @@
4545
using firebase::firestore::model::DocumentKeySet;
4646
using firebase::firestore::model::OnlineState;
4747
using firebase::firestore::remote::TargetChange;
48+
using firebase::firestore::util::DelayedConstructor;
4849
using firebase::firestore::util::ExecutorLibdispatch;
4950
using firebase::firestore::util::Status;
5051
using firebase::firestore::util::StatusOr;
@@ -55,15 +56,12 @@ @interface FSTQueryListenerTests : XCTestCase
5556
@end
5657

5758
@implementation FSTQueryListenerTests {
58-
std::unique_ptr<ExecutorLibdispatch> _executor;
59+
DelayedConstructor<ExecutorLibdispatch> _executor;
5960
FSTListenOptions *_includeMetadataChanges;
6061
}
6162

6263
- (void)setUp {
63-
// TODO(varconst): moving this test to C++, it should be possible to store Executor as a value,
64-
// not a pointer, and initialize it in the constructor.
65-
_executor = absl::make_unique<ExecutorLibdispatch>(
66-
dispatch_queue_create("FSTQueryListenerTests Queue", DISPATCH_QUEUE_SERIAL));
64+
_executor.Init(dispatch_queue_create("FSTQueryListenerTests Queue", DISPATCH_QUEUE_SERIAL));
6765
_includeMetadataChanges = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES
6866
includeDocumentMetadataChanges:YES
6967
waitForSyncWhenOnline:NO];

Firestore/Source/API/FIRFirestore.mm

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "Firestore/core/src/firebase/firestore/core/database_info.h"
4040
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
4141
#include "Firestore/core/src/firebase/firestore/util/async_queue.h"
42+
#include "Firestore/core/src/firebase/firestore/util/delayed_constructor.h"
4243
#include "Firestore/core/src/firebase/firestore/util/log.h"
4344
#include "Firestore/core/src/firebase/firestore/util/string_apple.h"
4445

@@ -48,6 +49,7 @@
4849
using firebase::firestore::auth::CredentialsProvider;
4950
using firebase::firestore::model::DatabaseId;
5051
using firebase::firestore::util::AsyncQueue;
52+
using firebase::firestore::util::DelayedConstructor;
5153

5254
NS_ASSUME_NONNULL_BEGIN
5355

@@ -62,9 +64,7 @@ @interface FIRFirestore ()
6264
@end
6365

6466
@implementation FIRFirestore {
65-
// `std::mutex` member variable makes `core::Firestore` unmovable.
66-
67-
std::unique_ptr<Firestore> _firestore;
67+
DelayedConstructor<Firestore> _firestore;
6868
}
6969

7070
+ (NSMutableDictionary<NSString *, FIRFirestore *> *)instances {
@@ -144,9 +144,8 @@ - (instancetype)initWithProjectID:(std::string)projectID
144144
workerQueue:(std::unique_ptr<AsyncQueue>)workerQueue
145145
firebaseApp:(FIRApp *)app {
146146
if (self = [super init]) {
147-
_firestore = absl::make_unique<Firestore>(
148-
std::move(projectID), std::move(database), std::move(persistenceKey),
149-
std::move(credentialsProvider), std::move(workerQueue), (__bridge void *)self);
147+
_firestore.Init(std::move(projectID), std::move(database), std::move(persistenceKey),
148+
std::move(credentialsProvider), std::move(workerQueue), (__bridge void *)self);
150149

151150
_app = app;
152151

Firestore/core/src/firebase/firestore/util/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ cc_library(
245245
comparison.cc
246246
comparison.h
247247
config.h
248+
delayed_constructor.h
248249
hashing.h
249250
iterator_adaptors.h
250251
objc_compatibility.h
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2019 Google
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 FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_DELAYED_CONSTRUCTOR_H_
18+
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_DELAYED_CONSTRUCTOR_H_
19+
20+
#include <type_traits>
21+
#include <utility>
22+
23+
namespace firebase {
24+
namespace firestore {
25+
namespace util {
26+
27+
// DelayedConstructor<T> is a wrapper around an object of type T that
28+
//
29+
// * stores the object of type T inline inside DelayedConstructor<T>;
30+
// * initially does not call T's constructor, leaving storage uninitialized;
31+
// * calls the constructor when you call Init();
32+
// * provides access to the object of type T like a pointer via ->, *, and
33+
// get(); and
34+
// * calls T's destructor as usual.
35+
//
36+
// This is useful for embedding objects of type T inside Objective-C objects
37+
// when T has no default constructor.
38+
//
39+
// Objective-C separates allocation from initialization which is different from
40+
// the way C++ does it. A C++ object embedded in an Objective-C object is
41+
// normally default constructed then assigned a value later. This doesn't work
42+
// for classes that have no default constructor.
43+
//
44+
// DelayedConstructor does not count or otherwise check that Init is only
45+
// called once. For best results call Init() from the Objective-C class's
46+
// designated initializer.
47+
//
48+
// Note that DelayedConstructor makes no guarantees about the state of the
49+
// storage backing it before Init() is called. However, Objective-C objects are
50+
// zero filled during allocation, so as a member of an Objective-C object, the
51+
// default state will be zero-filled.
52+
//
53+
// Normally this doesn't matter, but DelayedConstructor unconditionally invokes
54+
// T's destructor, even if you don't call Init(). This may cause problems in
55+
// Objective-C classes where the initializer is designed to return an instance
56+
// other than self. It's best to avoid such instance switching techniques in
57+
// combination with DelayedConstructor, but it is possible: either ensure that
58+
// T's destructor handles the zero-filled case correctly, or call Init() before
59+
// switching instances.
60+
template <typename T>
61+
class DelayedConstructor {
62+
public:
63+
typedef T element_type;
64+
65+
/**
66+
* Default constructor does nothing.
67+
*/
68+
DelayedConstructor() {
69+
}
70+
71+
/**
72+
* Forwards arguments to the T's constructor: calls T(args...).
73+
*
74+
* This overload is disabled when it might collide with copy/move.
75+
*/
76+
template <typename... Ts,
77+
typename std::enable_if<
78+
!std::is_same<void(typename std::decay<Ts>::type...),
79+
void(DelayedConstructor)>::value,
80+
int>::type = 0>
81+
void Init(Ts&&... args) {
82+
new (&space_) T(std::forward<Ts>(args)...);
83+
}
84+
85+
/**
86+
* Forwards copy and move construction for T.
87+
*/
88+
void Init(const T& x) {
89+
new (&space_) T(x);
90+
}
91+
void Init(T&& x) {
92+
new (&space_) T(std::move(x));
93+
}
94+
95+
// No copying.
96+
DelayedConstructor(const DelayedConstructor&) = delete;
97+
DelayedConstructor& operator=(const DelayedConstructor&) = delete;
98+
99+
~DelayedConstructor() {
100+
get()->~T();
101+
}
102+
103+
// Pretend to be a smart pointer to T.
104+
T& operator*() {
105+
return *get();
106+
}
107+
T* operator->() {
108+
return get();
109+
}
110+
T* get() {
111+
return reinterpret_cast<T*>(&space_);
112+
}
113+
const T& operator*() const {
114+
return *get();
115+
}
116+
const T* operator->() const {
117+
return get();
118+
}
119+
const T* get() const {
120+
return reinterpret_cast<const T*>(&space_);
121+
}
122+
123+
private:
124+
typename std::aligned_storage<sizeof(T), alignof(T)>::type space_;
125+
};
126+
127+
} // namespace util
128+
} // namespace firestore
129+
} // namespace firebase
130+
131+
#endif // FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_DELAYED_CONSTRUCTOR_H_

Firestore/core/test/firebase/firestore/util/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ cc_test(
150150
autoid_test.cc
151151
bits_test.cc
152152
comparison_test.cc
153+
delayed_constructor_test.cc
153154
hashing_test.cc
154155
iterator_adaptors_test.cc
155156
ordered_code_test.cc

0 commit comments

Comments
 (0)