-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Port FieldTransform to C++ #1033
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
Conversation
The true commits start from 8eecd35. (The earlier ones are from base branch) |
@@ -278,15 +281,14 @@ NSComparator FSTTestDocComparator(const absl::string_view fieldPath) { | |||
FSTTransformMutation *FSTTestTransformMutation(NSString *path, | |||
NSArray<NSString *> *serverTimestampFields) { | |||
FSTDocumentKey *key = [FSTDocumentKey keyWithPath:testutil::Resource(util::MakeStringView(path))]; | |||
NSMutableArray<FSTFieldTransform *> *fieldTransforms = [NSMutableArray array]; | |||
std::vector<FieldTransform> fieldTransforms{}; |
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: {}
is unnecessary.
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
@@ -278,15 +281,14 @@ NSComparator FSTTestDocComparator(const absl::string_view fieldPath) { | |||
FSTTransformMutation *FSTTestTransformMutation(NSString *path, | |||
NSArray<NSString *> *serverTimestampFields) { | |||
FSTDocumentKey *key = [FSTDocumentKey keyWithPath:testutil::Resource(util::MakeStringView(path))]; | |||
NSMutableArray<FSTFieldTransform *> *fieldTransforms = [NSMutableArray array]; | |||
std::vector<FieldTransform> fieldTransforms{}; | |||
fieldTransforms.reserve(serverTimestampFields.count); |
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: unless the number is expected to be pretty large, this is probably overkill.
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__) | ||
bool operator==(const FieldTransform& 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.
Equality operator seems like a reasonable operation to have, maybe move outside the #if
section?
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
private: | ||
FieldPath path_; | ||
// Shared by copies of the same FieldTransform. | ||
std::shared_ptr<TransformOperation> transform_; |
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.
Can it be <const TransformOperation>
? Also, can you please provide more context for this optimization?
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.
Change to const
. Really does not matter for the only ServerTimestampTransform
. I think we can safely assume a TransformOperation
will not mutate itself when proceeding fields.
We need to access a TransformOperation
here. Since it is poly, we need either a pointer or a reference. Conceptually, FieldTransform
should own it instead of each TransformOperation
type owns it (for ServingTimestampTransform might be OK since it builds a singleton). Now if you wonder why unique_ptr
does not work here, that's because a FieldTransform
object can be copied.
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 we can safely assume a TransformOperation will not mutate itself when proceeding fields.
Thanks for making the change. I think it's useful to enforce such assumptions using the type system. Shared ownership of immutable data is a much simpler model than shared ownership of mutable data.
Now if you wonder why unique_ptr does not work here, that's because a FieldTransform object can be copied.
It's possible to do something like:
FieldTransform(const FieldTransform& rhs)
: transform_{rhs.transform_.Clone()}
{}
But it's more hassle -- as long as shared_ptr
points to const, I'm fine with it.
return path_; | ||
} | ||
|
||
const TransformOperation* transform() 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.
Nit: the function name sounds like an action (i.e., "do apply transformation"). transformation
? transform_operation
?
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
(NSArray<GCFSDocumentTransform_FieldTransform *> *)protos { | ||
NSMutableArray<FSTFieldTransform *> *fieldTransforms = [NSMutableArray array]; | ||
std::vector<FieldTransform> fieldTransforms{}; |
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 comments on vector initialization.
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
return result; | ||
} | ||
|
||
- (NSString *)description { | ||
return [NSString stringWithFormat:@"<FSTTransformMutation key=%s transforms=%@ precondition=%@>", | ||
self.key.ToString().c_str(), self.fieldTransforms, | ||
std::string fieldTransforms = ""; |
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: = ""
is unnecessary.
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
// _fieldMask is shared across all active context objects to accumulate the result. For example, | ||
// the result of calling any of contextForField, contextForFieldPath and contextForArrayIndex | ||
// shares the ownership of _fieldMask. | ||
// _fieldMask and _fieldTransforms are shared across all active context objects to accumulate 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.
I guess alternatively, a single context object could be wrapped in a shared_ptr
. Can you please provide some context on how it was implemented in Objective-C?
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 ObjC, NSMutableArray* is used. It is not a single FSTParseContext
object is shared by many users. It is multiple FSTParseContext
objects will update the same (shared) fieldMask
and fieldTransforms
. From my limited reading on the codes, it is mainly due to some recursive parse of the serialized stream input, where each sub-structure has their own FSTParseContext
object but all put the parsed result into the shared fieldMask
and fieldTransforms
. I am confident there is alternative design where there is separate class for those shared part, say SharedParsedContext
. But I'd not do the refactor in this porting PR.
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.
Thanks for the explanation.
private: | ||
FieldPath path_; | ||
// Shared by copies of the same FieldTransform. | ||
std::shared_ptr<TransformOperation> transform_; |
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 we can safely assume a TransformOperation will not mutate itself when proceeding fields.
Thanks for making the change. I think it's useful to enforce such assumptions using the type system. Shared ownership of immutable data is a much simpler model than shared ownership of mutable data.
Now if you wonder why unique_ptr does not work here, that's because a FieldTransform object can be copied.
It's possible to do something like:
FieldTransform(const FieldTransform& rhs)
: transform_{rhs.transform_.Clone()}
{}
But it's more hassle -- as long as shared_ptr
points to const, I'm fine with it.
// _fieldMask is shared across all active context objects to accumulate the result. For example, | ||
// the result of calling any of contextForField, contextForFieldPath and contextForArrayIndex | ||
// shares the ownership of _fieldMask. | ||
// _fieldMask and _fieldTransforms are shared across all active context objects to accumulate 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.
Thanks for the explanation.
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
FSTFieldTransform *fieldTransform = self.fieldTransforms[i]; | ||
const TransformOperation *transform = fieldTransform.transform; | ||
FieldPath fieldPath = fieldTransform.path; | ||
for (NSUInteger i = 0; i < self.fieldTransforms.size(); i++) { |
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.
The loop variable here should now be size_t
.
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
public: | ||
FieldTransform(FieldPath path, | ||
std::unique_ptr<TransformOperation> transformation) | ||
: path_(std::move(path)), transformation_(std::move(transformation)) { |
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.
Prefer brace initialization for consistency.
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
class FieldTransform { | ||
public: | ||
FieldTransform(FieldPath path, | ||
std::unique_ptr<TransformOperation> transformation) |
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.
Move constructors should be marked noexcept, 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
return path_; | ||
} | ||
|
||
const TransformOperation* transformation() 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.
I realize that inside FieldTransform
we need the TransformOperation
to be stored in a unique_ptr
so that we can store different subtypes. However, it seems like this implementation detail does not need to leak outside FieldTransform
and this method could return a const TransformOperation&
instead of a const pointer.
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.
That's true. Fixed.
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.
FSTFieldTransform *fieldTransform = self.fieldTransforms[i]; | ||
const TransformOperation *transform = fieldTransform.transform; | ||
FieldPath fieldPath = fieldTransform.path; | ||
for (NSUInteger i = 0; i < self.fieldTransforms.size(); i++) { |
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
public: | ||
FieldTransform(FieldPath path, | ||
std::unique_ptr<TransformOperation> transformation) | ||
: path_(std::move(path)), transformation_(std::move(transformation)) { |
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
class FieldTransform { | ||
public: | ||
FieldTransform(FieldPath path, | ||
std::unique_ptr<TransformOperation> transformation) |
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
return path_; | ||
} | ||
|
||
const TransformOperation* transformation() 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.
That's true. Fixed.
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` * address changes * fix lint * address changes
Discussion
This is part of the C++ migration.
Testing
unit and integration test pass.
API Changes
n/a