-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Port Precondition
to C++
#1040
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Port Precondition
to C++
#1040
Conversation
There is one design change from |
UpdateTime, | ||
}; | ||
|
||
/** Creates a new Precondition with an exists flag. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "Creates a new" is the wrong phrase when returning a reference. How about "Returns a Precondition with an exists flag." ?
OTOH, it's a little awkward to return a reference for this one, but to create Preconditions for UpdateTime(). As a result, it's unclear who owns these.
It looks like this class just has a few small private members. I'd be tempted to turn it into a value object, and not optimize via static instances. You'll have more Precondition objects floating around, but each one looks to be not much bigger than a reference anyways.
My recommendation: Leave the phrasing as is, but change Exists() and None() to just return Precondition
rather than const Precondition&
.
(Note that a lot of the calling code takes copies anyways, so if you dont want to make this change, you should double check the calling code to ensure the copies are ok.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
static const Precondition& None(); | ||
|
||
/** | ||
* Returns true if the preconditions is valid for the given document (or null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"or null if no document is available"
^ Needs updating.
Looking at the code, if no document is available, this returns false. How about:
"Returns true if the precondition is valid for the given document (and the document is available)"
(Also note the grammar nit: s/preconditions/precondition/)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
FIREBASE_ASSERT_MESSAGE(false, "Invalid precondition"); | ||
break; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: since your cases all return, I don't think you need 'break' anywhere. (Presumably you're including it for safety?)
nit: you've exhaustively handled all (reasonable) enum values. Rather than including a default, you could omit it, and then assert outside the switch (which, since all your cases return, should be unreachable.)
i.e.
switch (x) {
case one_of_two:
return 1;
case two_of_two:
return 2;
}
FIREBASE_UNREACHABLE();
This should cause the compiler to complain if new enum values are added without adjusting the switch. (Otherwise the 'default' case will mask that.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
bool operator==(const Precondition& other) const { | ||
return update_time_ == other.update_time_ && exists_ == other.exists_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should you also compare type_
? (If yes, also adjust hash())
I see that:
- the old code did not
- it doesn't actually matter, due to the way the ctors are defined, there shouldn't be a case where two instances differ only via type_.
But it seems like it might be slightly safer in case the code changes in such a way as to otherwise allow this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Since you've updated operator==, you should also update Hash().
update_time_.timestamp().ToString().c_str()]; | ||
break; | ||
default: | ||
// We do not raise assertion here. This function is mainly used in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems reasonable.
Consider a dev assertion though. If this branch is ever executed, it might save us some grief.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
default: | ||
FIREBASE_ASSERT_MESSAGE(false, "Invalid precondition"); | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(See comment in header file.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
#if defined(__OBJC__) | ||
SnapshotVersion(FSTSnapshotVersion* version) { // NOLINT(runtime/explicit) | ||
if ([version isEqual:[FSTSnapshotVersion noVersion]]) { | ||
timestamp_ = Timestamp{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is unnecessary, as the timestamp_ instance will already be initialized with it's default ctor. (No objections if you want to keep it though.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -40,6 +46,24 @@ inline model::ResourcePath Resource(absl::string_view field) { | |||
return model::ResourcePath::FromString(field); | |||
} | |||
|
|||
// Version is epoch time in macroseconds. This helper is defined so to match |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: s/macro/micro/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
So there's good news and bad news. 👍 The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so. Everything is all good there. 😕 The bad news is that it appears that one or more commits were authored or co-authored by someone other than the pull request submitter. We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that here in the pull request. Note to project maintainer: This is a terminal state, meaning the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated. PTAL.
UpdateTime, | ||
}; | ||
|
||
/** Creates a new Precondition with an exists flag. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
static const Precondition& None(); | ||
|
||
/** | ||
* Returns true if the preconditions is valid for the given document (or null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
FIREBASE_ASSERT_MESSAGE(false, "Invalid precondition"); | ||
break; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
} | ||
|
||
bool operator==(const Precondition& other) const { | ||
return update_time_ == other.update_time_ && exists_ == other.exists_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
update_time_.timestamp().ToString().c_str()]; | ||
break; | ||
default: | ||
// We do not raise assertion here. This function is mainly used in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
#if defined(__OBJC__) | ||
SnapshotVersion(FSTSnapshotVersion* version) { // NOLINT(runtime/explicit) | ||
if ([version isEqual:[FSTSnapshotVersion noVersion]]) { | ||
timestamp_ = Timestamp{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -40,6 +46,24 @@ inline model::ResourcePath Resource(absl::string_view field) { | |||
return model::ResourcePath::FromString(field); | |||
} | |||
|
|||
// Version is epoch time in macroseconds. This helper is defined so to match |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
default: | ||
FIREBASE_ASSERT_MESSAGE(false, "Invalid precondition"); | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One nit (re adding type_ to Hash()). Otherwise lgtm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some nits.
FIREBASE_UNREACHABLE(); | ||
} | ||
|
||
bool operator==(const Precondition& other) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it could be useful outside #if defined(__OBJC__)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
switch (type_) { | ||
case Type::None: | ||
return @"<Precondition <none>>"; | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: no need for break
after a return
statement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Really adding break
do no harm but would catch bugs if later non-return code is added into the case. But I don't have strong opinion and thus removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your reasoning, thanks for explaining. I'm still leaning towards not having non-reachable code (and linters might complain).
update_time_.timestamp().ToString().c_str()]; | ||
break; | ||
default: | ||
// We only raise dev assertion here. This function is mainly used in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: perhaps move this code below the switch
statement and eliminate default
case? I think Clang can produce a warning if a switch
statement is non-exhaustive, but that won't work in presence of default
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
#if defined(__OBJC__) | ||
// Objective-C requires a default constructor. | ||
Precondition() | ||
: type_(Type::None), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional: since there is an in-class initializer, this line is unnecessary. Consider also providing in-class initializers for the other member variables. That way, the constructor could be just Precondition() {}
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel our usage of initializer is not consistent in this way. I don't see the heavy use of in-class initializer in other classes in our code base. The only exception of initializer to type_
is a safe mechanism to ensure type_
is always initialized intentionally. type_
is a particular member that ensure the integrity of the class (if the language support RTTI, type_
is not needed.), which is different from the other members.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, I think in-class initializers are superior to doing initialization in constructors (when applicable). They potentially reduce duplication (when the same member variable gets set to the same value across constructors), and, importantly, prevent forgetting to initialize a member variable when adding a new constructor. C++ core guidelines provide some recommendations to prefer in-class initializers: "Don't define a default constructor that only initializes data members", "Prefer in-class initializers to member initializers in constructors for constant initializers". This is of course non-binding, but I feel it's a good practice.
This is optional; I understand your reasoning, just wanted to provide my perspective on this.
return [maybe_doc isKindOfClass:[FSTDocument class]] && | ||
firebase::firestore::model::SnapshotVersion(maybe_doc.version) == | ||
update_time_; | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment re. break
s.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
case Type::UpdateTime: | ||
return maybe_doc.type() == MaybeDocument::Type::Document && | ||
maybe_doc.version() == update_time_; | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(similar comment re. break
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, removed since both of you are in favor of without break
.
|
||
#include "Firestore/core/src/firebase/firestore/model/precondition.h" | ||
|
||
#include <utility> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: <utility>
is already included in the header file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. But frankly speaking, I feel IWYU asking for include <utility>
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NB: there's an exception for IWYU if the related header includes it.
"However, any includes present in the related header do not need to be included again in the related cc"
http://go/cpp-style#Names_and_Order_of_Includes
(I don't think it's a requirement to not include it though.)
|
||
#include "Firestore/core/src/firebase/firestore/model/maybe_document.h" | ||
#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" | ||
#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: #include "Firestore/core/include/firebase/firestore/timestamp.h";
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
// SDKs in other platform. | ||
inline model::SnapshotVersion Version(int64_t version) { | ||
return model::SnapshotVersion{ | ||
Timestamp{/*seconds=*/version / 1000, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If time is in microseconds, should this be version / (1000 * 1000)
?
Also, optionally consider something like:
namespace chr = std::chrono;
auto timepoint = chr::time_point<chr::system_clock>(chr::microseconds(version));
return model::SnapshotVersion{Timestamp::FromTimePoint(timepoint)};
This is a little more verbose, but arguably more readable and saves doing arithmetic operations manually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
inline model::SnapshotVersion Version(int64_t version) { | ||
return model::SnapshotVersion{ | ||
Timestamp{/*seconds=*/version / 1000, | ||
/*nanoseconds=*/static_cast<int32_t>(version % 1000) * 1000}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, should this be (version % (1000 * 1000)) * 1000
? Or is version
in milliseconds?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack, should be 1000000
instead of 1000
. change to your suggested implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated. PTAL
|
||
#include "Firestore/core/src/firebase/firestore/model/precondition.h" | ||
|
||
#include <utility> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. But frankly speaking, I feel IWYU asking for include <utility>
here.
case Type::UpdateTime: | ||
return maybe_doc.type() == MaybeDocument::Type::Document && | ||
maybe_doc.version() == update_time_; | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, removed since both of you are in favor of without break
.
#ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_PRECONDITION_H_ | ||
#define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_MODEL_PRECONDITION_H_ | ||
|
||
#include <memory> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
|
||
#include "Firestore/core/src/firebase/firestore/model/maybe_document.h" | ||
#include "Firestore/core/src/firebase/firestore/model/snapshot_version.h" | ||
#include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
#if defined(__OBJC__) | ||
// Objective-C requires a default constructor. | ||
Precondition() | ||
: type_(Type::None), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel our usage of initializer is not consistent in this way. I don't see the heavy use of in-class initializer in other classes in our code base. The only exception of initializer to type_
is a safe mechanism to ensure type_
is always initialized intentionally. type_
is a particular member that ensure the integrity of the class (if the language support RTTI, type_
is not needed.), which is different from the other members.
switch (type_) { | ||
case Type::None: | ||
return @"<Precondition <none>>"; | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Really adding break
do no harm but would catch bugs if later non-return code is added into the case. But I don't have strong opinion and thus removed.
} | ||
break; | ||
case Type::None: | ||
FIREBASE_ASSERT_MESSAGE(IsNone(), "Precondition should be empty"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
update_time_.timestamp().ToString().c_str()]; | ||
break; | ||
default: | ||
// We only raise dev assertion here. This function is mainly used in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
// SDKs in other platform. | ||
inline model::SnapshotVersion Version(int64_t version) { | ||
return model::SnapshotVersion{ | ||
Timestamp{/*seconds=*/version / 1000, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
inline model::SnapshotVersion Version(int64_t version) { | ||
return model::SnapshotVersion{ | ||
Timestamp{/*seconds=*/version / 1000, | ||
/*nanoseconds=*/static_cast<int32_t>(version % 1000) * 1000}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ack, should be 1000000
instead of 1000
. change to your suggested implementation.
@@ -17,7 +17,8 @@ | |||
#ifndef FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_TESTUTIL_H_ | |||
#define FIRESTORE_CORE_TEST_FIREBASE_FIRESTORE_TESTUTIL_TESTUTIL_H_ | |||
|
|||
#include <inttypes.h> | |||
#include <chrono> // NOLINT(build/c++11) | |||
#include <cinttypes> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: please replace <cinttypes>
with <cstdint>
(IIUC, you're only including it for int64_t
, and the header defining that is <cstdint>
, while <cinttypes>
happens to include it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
switch (type_) { | ||
case Type::None: | ||
return @"<Precondition <none>>"; | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your reasoning, thanks for explaining. I'm still leaning towards not having non-reachable code (and linters might complain).
#if defined(__OBJC__) | ||
// Objective-C requires a default constructor. | ||
Precondition() | ||
: type_(Type::None), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, I think in-class initializers are superior to doing initialization in constructors (when applicable). They potentially reduce duplication (when the same member variable gets set to the same value across constructors), and, importantly, prevent forgetting to initialize a member variable when adding a new constructor. C++ core guidelines provide some recommendations to prefer in-class initializers: "Don't define a default constructor that only initializes data members", "Prefer in-class initializers to member initializers in constructors for constant initializers". This is of course non-binding, but I feel it's a good practice.
This is optional; I understand your reasoning, just wanted to provide my perspective on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty much LGTM, with a few minor last points.
@@ -38,6 +43,27 @@ class SnapshotVersion { | |||
/** Creates a new version that is smaller than all other versions. */ | |||
static const SnapshotVersion& None(); | |||
|
|||
#if defined(__OBJC__) | |||
SnapshotVersion(FSTSnapshotVersion* version) { // NOLINT(runtime/explicit) | |||
if (![version isEqual:[FSTSnapshotVersion noVersion]]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this check even required? Why not just directly initialize the timestamp?
SnapshotVersion(FSTSnapshotVersion* version)
: timestamp_{version.timestamp} {
}
Or if Timestamp doesn't define a conversion to FIRTimestamp:
SnapshotVersion(FSTSnapshotVersion* version)
: timestamp_{version.timestamp.seconds, version.timestamp.nanoseconds} {
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. not really required as far as the logic of noVersion
are the same between objc and C++, which seems the case as both are of epoch 0.
@@ -40,6 +47,25 @@ inline model::ResourcePath Resource(absl::string_view field) { | |||
return model::ResourcePath::FromString(field); | |||
} | |||
|
|||
// Version is epoch time in microseconds. This helper is defined so to match |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Version" here is ambiguous (it's not clear if you mean the function or the param.
Better to define this just in terms of what it does:
// Creates a snapshot version from the given version timestamp
//
// @param version a timestamp in microseconds since the epoch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
message.exists = precondition.exists == FSTPreconditionExistsYes; | ||
if (precondition.type() == Precondition::Type::UpdateTime) { | ||
message.updateTime = | ||
[self encodedVersion:static_cast<FSTSnapshotVersion *>(precondition.update_time())]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this cast necessary? -encodedVersion:
statically takes an FSTSnapshotVersion
so the compiler should find the user-defined conversion automatically, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
@@ -38,6 +43,27 @@ class SnapshotVersion { | |||
/** Creates a new version that is smaller than all other versions. */ | |||
static const SnapshotVersion& None(); | |||
|
|||
#if defined(__OBJC__) | |||
SnapshotVersion(FSTSnapshotVersion* version) { // NOLINT(runtime/explicit) | |||
if (![version isEqual:[FSTSnapshotVersion noVersion]]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. not really required as far as the logic of noVersion
are the same between objc and C++, which seems the case as both are of epoch 0.
@@ -40,6 +47,25 @@ inline model::ResourcePath Resource(absl::string_view field) { | |||
return model::ResourcePath::FromString(field); | |||
} | |||
|
|||
// Version is epoch time in microseconds. This helper is defined so to match |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
message.exists = precondition.exists == FSTPreconditionExistsYes; | ||
if (precondition.type() == Precondition::Type::UpdateTime) { | ||
message.updateTime = | ||
[self encodedVersion:static_cast<FSTSnapshotVersion *>(precondition.update_time())]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
* port FieldMask to C++ * address changes * address changes * fix test * address change * Port transform operations (FSTTransformOperation, FSTServerTimestampTransform) to C++ * address changes * address changes * address changes * implement `FieldTransform` in C++ * port `FieldTransform` * make `fieldTransforms` shared inside `context` * Implement Precondition in C++ w/o test yet * add unit test for `Precondition` * port `Precondition` * address changes * address changes * fix bugs for integration test * address changes * fix lint * address changes * address changes
This is based on the branch
cpp/port_field_transform
. Merge to it for easier review; eventually will merge it tomaster
once the base branch is merged.Discussion
This is part of the C++ migration.
Testing
unit test and integration test pass.
API Changes
n/a