Skip to content

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

Merged
merged 30 commits into from
Apr 13, 2018
Merged

Port Precondition to C++ #1040

merged 30 commits into from
Apr 13, 2018

Conversation

zxu123
Copy link
Contributor

@zxu123 zxu123 commented Apr 6, 2018

This is based on the branch cpp/port_field_transform. Merge to it for easier review; eventually will merge it to master 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

@zxu123
Copy link
Contributor Author

zxu123 commented Apr 6, 2018

There is one design change from FSTPrecondition to Precondition: FSTPrecondition has optional properties, where only one property set meaningful value, which determines the type of FSTPrecondition. Precondition instead explicitly has a type member to define its type. This is to solve the issue of no nil equivalent in C++ and IMHO is a more clean design.

UpdateTime,
};

/** Creates a new Precondition with an exists flag. */
Copy link
Member

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.)

Copy link
Contributor Author

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
Copy link
Member

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/)

Copy link
Contributor Author

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;
}
}
Copy link
Member

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.)

Copy link
Contributor Author

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_;
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

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
Copy link
Member

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.

Copy link
Contributor Author

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;
}
Copy link
Member

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.)

Copy link
Contributor Author

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{};
Copy link
Member

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.)

Copy link
Contributor Author

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/macro/micro/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@googlebot
Copy link

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 cla/google commit status will not change from this state. It's up to you to confirm consent of the commit author(s) and merge this pull request when appropriate.

@googlebot googlebot added cla: no and removed cla: yes labels Apr 9, 2018
Copy link
Contributor Author

@zxu123 zxu123 left a 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. */
Copy link
Contributor Author

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
Copy link
Contributor Author

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;
}
}
Copy link
Contributor Author

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_;
Copy link
Contributor Author

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
Copy link
Contributor Author

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{};
Copy link
Contributor Author

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
Copy link
Contributor Author

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;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Member

@rsgowman rsgowman left a 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.

Copy link
Contributor

@var-const var-const left a 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 {
Copy link
Contributor

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__).

Copy link
Contributor Author

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;
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Contributor Author

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),
Copy link
Contributor

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() {}.

Copy link
Contributor Author

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.

Copy link
Contributor

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment re. breaks.

Copy link
Contributor Author

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(similar comment re. break)

Copy link
Contributor Author

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>
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Member

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"
Copy link
Contributor

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";?

Copy link
Contributor Author

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,
Copy link
Contributor

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.

Copy link
Contributor Author

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}};
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

@zxu123 zxu123 left a 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>
Copy link
Contributor Author

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;
Copy link
Contributor Author

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>
Copy link
Contributor Author

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"
Copy link
Contributor Author

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),
Copy link
Contributor Author

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;
Copy link
Contributor Author

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");
Copy link
Contributor Author

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
Copy link
Contributor Author

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,
Copy link
Contributor Author

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}};
Copy link
Contributor Author

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>
Copy link
Contributor

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).

Copy link
Contributor Author

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;
Copy link
Contributor

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),
Copy link
Contributor

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.

@zxu123 zxu123 changed the base branch from cpp/port_field_transform to master April 10, 2018 20:29
@zxu123 zxu123 requested a review from wilhuff April 10, 2018 20:36
Copy link
Contributor

@wilhuff wilhuff left a 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]]) {
Copy link
Contributor

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} {
}

Copy link
Contributor Author

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
Copy link
Contributor

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

Copy link
Contributor Author

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())];
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor Author

@zxu123 zxu123 left a 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]]) {
Copy link
Contributor Author

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
Copy link
Contributor Author

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())];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@zxu123 zxu123 merged commit 2ceea9b into master Apr 13, 2018
@zxu123 zxu123 deleted the cpp/port_precondition branch April 13, 2018 01:32
minafarid pushed a commit to minafarid/firebase-ios-sdk that referenced this pull request Jun 6, 2018
* 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
@firebase firebase locked and limited conversation to collaborators Nov 5, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants